CodeIQ:「プライム・ぺア」問題の解答

（表題のペアがベアになってたので2025.5.3訂正）

Kawazoe(@riverplus)さんのCodeIQの問題です。
問題とテストケースはこちらにお世話になりました。解答例もあります。
~~->https://blog.goo.ne.jp/r-de-r/e/20a11cb53e7dd472a85d8b062cbba191~~
リンクが切れていました。新しいURLも見つかりません。（2025.5.2-2025.5.3修正）

こちらにも情報があります。
->https://togetter.com/li/1087033

ーーー2025.5.2追加
自然数kのオイラーのΦ関数をF(k)とするとき、1≦n≦10^5なるnにたいして、
F(n!)を求める問題です。F(k)がオイラーのΦ関数になるということがヒントでした。
ーーー

最初方針が見つかりませんでしたが、ヒントに従ってwikipediaを見て、
その定理を使ってすぐ解決しました。
->https://ja.wikipedia.org/wiki/%E3%82%AA%E3%82%A4%E3%83%A9%E3%83%BC%E3%81%AE%CF%86%E9%96%A2%E6%95%B0

p_iをnの素因数とすると　φ(n)=n*(1-1/p_1)\*(1-1/p_2)\*..(1-1/p_i)*...
n=1\*2\*3\*4\*5\*6\*7\*..なので
φ(n)=1\*2\*3\*4\*5\*6\*7\*..\*(1-1/2)\*(1-1/3)\*(1-1/5)\*(1-1/7)\*...
　　=1\*2\*(1-1/2)\*3\*(1-1/3)\*4\*5\*(1-1/5)\*6\*7\*(1-1/7)\*...
　　=1\*(2-1)\*(3-1)\*4\*(5-1)\*6\*(7-1)\*...     
よって n=k!の時ret=1 としてφ(n)は
1からkのiに対して、retにもしiが素数ならi-1、素数でないならiを
次々掛けて行った結果のretの値

Juliaには素数かどうか判定するisprimeという関数がありますが
packageのインストールが必要ですので、自前で実装しました。

```Julia
#julia ver 1.41,1.53,1.10.3

function solve57(n,a)
    function myprime(n)
        for i in 2 : Int(floor(sqrt(n)))
            if ar[i] == 0
                continue
            end    
            for j in i+1 : n
                if mod(j,i) == 0
                    ar[j] = 0
                end    
            end
        end    
    end
  
    ar=ones(Int,n)
    myprime(n)
    m = 1000003 
    ret = 1
    for i in 2 : n
        if ar[i] != 0
            ret = mod(ret*(i-1),m)
        else
            ret = mod(ret*i,m)
        end
    end
    s = a == ret ? "ok " : "no "
    println(s,ret)
end

solve57(10, 829440)
solve57(20, 961998)
solve57(30, 845477)
solve57(100, 145380)
solve57(431, 945906)
solve57(2017, 272985)
solve57(81645, 603326)
solve57(99100, 799614)
```
Prologでも書きました。
isprimeに相当するものがないようですので、最初に素数のリストを作りました。
配列ですと素数でないものに0を当てていきますが、リストですので、
まだ検討していない数のリストと素数のリストと素数でないリストに分けました。

```Prolog

/*
最初に素数のリストを作る。
リストの素数でないところを0にすると元の値がわからなくなるし、新しいリストを作るのは変わらないので
操作する要素を減らしていった方が良いだろうと思い、まだ検討していない数のリストと素数のリストと素数でないリストに分けた。
nの平方根まで調べればよい
素数でない数はあらかじめ1を引いてからリストに収納すると素数もそれ以外もX1 is mod(X*H,M)で一括できる
appendは非常に遅い。append(L,[X],L1)を繰り返すよりL1=[X|L]を繰り返して最後にreverseした方がダントツに早い
*/
%SWI-Prolog ver 7.42,7.64,9.25
%:-initialization(start).   %ideone,Online Prolog Compiler
%start.

pr1(_,[],L,NPR,L,NPR):-!.      %素数でない数とまだ不明の数のリストに分ける。 Lが不明のリスト
pr1(X,[H|T],L,NPL,LR,NPR):-
  Q is H mod X,(Q ==0->(NPL1=[H|NPL],L1=L);(L1=[H|L],NPL1=NPL)),
  pr1(X,T,L1,NPL1,LR,NPR).

prime57(N,[H|T],PL,NPL,PR,NPR):-  %素数のリストを作る
  H^2>N,!,reverse(PL,L),maplist(plus(-1),[H|T],L1),append(L,L1,PR),reverse(NPL,NPR).
prime57(N,[H|T],PL,NPL,PR,NPR):-    %[H|T]のHは常に素数
  H1 is H-1,PL2=[H1|PL],pr1(H,T,[],NPL,L,NPL1),reverse(L,L1),
  prime57(N,L1,PL2,NPL1,PR,NPR).

totient(R,[],_,R).                       %totient functionを計算
totient(X,[H|T],M,R):-X1 is mod(X*H,M),totient(X1,T,M,R).

solve57(N,M,R):-
  numlist(2,N,L),prime57(N,L,[],[],PL,NPL),totient(1,PL,M,R1),totient(R1,NPL,M,R).

start:-M=1000003,f57(N,A),(solve57(N,M,R)->(R==A->Str=" ok  ";Str=" no  ");
    write(fail)),write(" "),write(Str),writeln(N->R),fail.
start.

f57(10,829440).
f57(20,961998).
f57(30,845477).
f57(100,145380).
f57(431,945906).
f57(2017,272985).
f57(81645,603326).
f57(99100,799614).
```