原始根探し
1.原始根は粘り強い
このワークシートはMath by Codeの一部です。
<原始根ってなんだっけ?>
7で割った余りで数を同一視する整数の世界、
つまり、mod7でのべき乗計算をしてみる。
aのべき乗を順次計算してみよう。
たとえば、
a=2 (mod 7)なら,
21≡2 (mod 7),
22≡4 (mod 7),
23≡1 (mod 7),
24≡2 (mod 7),
25≡4 (mod 7),
26≡1 (mod 7)
と、2,4,1を2回繰り返す。
powermod(2,k,7)のkを動かしても1,2,4しか結果がでない。
初めて1と合同になるのは6乗ではなく、3乗だ。だから周期3乗で繰り返す。
a=3 (mod 7)なら、
31≡3 (mod 7),
32≡2 (mod 7),
33≡6 (mod 7),
34≡4 (mod 7),
35≡5 (mod 7),
36≡1と、繰り返すことなく6乗まで粘って1になる。
この最後までねばるヤツが原始根だ。
3を使うと、そのべき乗でmod7の0以外の要素7-1=6個が全部出そろう。
powermod(3,k,7)のkを1から6までまわすと、1から6の剰余がすべて出そろう。
3はすべての元の生成者になれるという意味で生成元、原始根だ。
aが原始根なら、p-1乗で初めてap-1≡1(mod p)となる。
順番はともかく、かぶりがない、1対1対応だ。
だから、逆算にも役立つ。そういう、ありがたい要素が原始根だ。
<原始根はレアなの?>
そんなありがたい原始根だけど、有り、難い、つまりレアな存在というものではない。
mod7でみてみよう。
11≡1 (mod 7),
23≡1 (mod 7),
36≡1 (mod 7),
43≡1 (mod 7),
56≡1 (mod 7),
62≡1 (mod 7)
原始根は3だけではない、5もそうだ。
原始根を3,5をrとすると、
L=[1,2,3,4,5,6]の要素xを順にpowermod(r,x,7)=Lになる。
mod 7の原始根は3と5の2個があることがわかった。
<指数の約数>
こんどは原始根以外にスポットライトをあてよう。
たとえば、2は3乗で1になり、振り出しに戻る。
原始根と同じ6乗で1になる必要があるから、3乗を2回くりかえして6乗になる。
たとえば、6は2乗で1になり、振り出しに戻る。
原始根と同じ6乗で1になる必要があるから、2乗を3回くりかえした6乗になる。
つまり、原始根は最大周期6乗で1になったけど、原始根以外でも6乗すれば必ず1になる。
これはオイラーの小定理からそうなる。
だから、その手前の周期のx乗で1になるとしたら、x乗をy回くりかえして6乗になる。
つまり、xは6の約数になるということだ。
aが原始根でないときは、am≡1(mod p)なら、mはp-1のp-1より小さい約数だ。
2.全体をみると部分がわかる
原始根だけ特別扱いしないで、平等に調べて全体をみよう。
11≡1 (mod 7),
23≡1 (mod 7),
36≡1 (mod 7),
43≡1 (mod 7),
56≡1 (mod 7),
62≡1 (mod 7)
周期が6の要素は3,6の2個。
周期が3の要素は2,4の2個。
周期が2の要素は6の1個。
周期が1の要素は1の1個。
2+2+1+1=6(個)
x=[6,3,2,1]に対してy=[2,2,1,1]となる関数はないかな?
mod11ならどうだろうか?
11 ≡1 (mod 11),
210≡1 (mod 11),
35 ≡1 (mod 11),
45 ≡1 (mod 11),
55 ≡1 (mod 11),
610≡1 (mod 11)
710≡1 (mod 11),
810≡1 (mod 11),
95 ≡1 (mod 11),
102 ≡1 (mod 11),
周期が10の要素は4個。
周期が5 の要素は4個。
周期が2 の要素は1個。
周期が1 の要素は1個。
4+4+1+1=10
x=[10,5,2,1]に対してy=[4,4,1,1]となる関数はないかな?
この2つの事例から
x=1ならy=1,
素数x=p(2,3,5)ならy=p-1。
合成数x=pq(6,10)ならy=(p-1)(q-1)となっている。
もう見当がつきましたか?
これは、オイラー関数φそのものだね!
要素そのものではなく約数と個数と互いに素に着目することで、全体像が見えてきたね。
つまり、
mod pの剰余x(1からp-1個)に対して、powermod(x,k,p)の値の周期はp-1の約数になる。
周期1は1個(x=1)。
周期n(素数)はn-1個。
周期d(合成数)になる要素はφ(d)個。
1+n-1+....φ(d)=p−1(個)になる。
p-1の約数すべてにわたって周期ごとの要素数を合計するとp-1になる。
つまり、
∑φ(d)=p-1
周期が最大のp-1になる要素が原始根だったから、
どんな素数pでも
原始根はφ(p−1)個あるということだ。
3.原始根を探そう
素数pには原始根が必ずあり、その個数はφ(p-1)だとわかった。
では、1からp-1のうち、どれが原始根になるのか?
それは、p-1個の要素を順にp-1乗まで調べないとみつからないのか?
たとえば、p=7のとき
11≡1 (mod 7),
23≡1 (mod 7),
36≡1 (mod 7),
43≡1 (mod 7),
56≡1 (mod 7),
62≡1 (mod 7)
で、3,5が原始根だった。
31≡3 (mod 7),
32≡2 (mod 7),
33≡6 (mod 7),
34≡4 (mod 7),
35≡5 (mod 7),
36≡1 (mod 7)
51≡5 (mod 7),
52≡4 (mod 7),
53≡6 (mod 7),
54≡2 (mod 7),
55≡3 (mod 7),
56≡1 (mod 7)
と順に計算すれば、aをp-2回aにかけてそのつど剰余を書き出してみつかった。
<p-1の素因数分解>
p-1=6=2・3と素因数分解される。
p1e1p2e2....となったとき、
要素aについて、
どの素数piについても a(p-1)/pi≡1(mod p)でないなら、aは原始根となる。
6の周期乗はどれも1(mod p)になるので、一番あやしいのは6の自明でない約数乗、2乗、3乗で1(mod p)になることだ。だから、2乗、3乗だけを調べれば良い。
a=1は論外。
a=2から6までで、2乗と3乗だけ剰余をしらべよう。φ(6)=(2-1)(3-1)=2個原始根があるはず。
22≡4 、23≡8≡1 (mod 7) 3乗で1になるから2はアウト。
32≡9=2 33≡6 (mod 7) 2乗も3乗も1にならないからOK。
42≡16=2 , 43≡8=1(mod 7) 3乗で1になるからアウト。
52≡25=4 , 53≡12=5(mod 7) 2乗も3乗も1にならないからOK。
2個見つかったから終了。
面白いのは、p=5やp=17のとき。
p-1=4,16のように素因数は2しかない。
だから、
p=5のときは4/2=2乗が1にならないとすぐ原始根とわかる。
p=17のときは16/2=8乗が1にならないとすぐ原始根とわかる。
(理由)
p-1=p1e1p2e2... (素因数分解1)とする。
aが原始根でなければ、
p-1より小さいp-1の約数mでam≡1(mod p)となる。
だから、m=p1f1p2f2... (素因数分解2)とする。
2つの素因数分解1,2を比較したときに、p-1とmの大小関係から
指数eのリストと指数fのリストで必ずfにeより小さいものがある。
それが1番目なら、(p-1)/p1でもmのk倍(kは1以上)になる。
だから、a(p-1)/p1≡(am)k≡1k≡1(mod p)となる。
これが1回もおきないなら、m=p-1となり、aが原始根ということになるね。
質問:原始根を求める関数はどうやって作りますか?juliaやpythonで
(剰余n、原始根の個数、原始根のリスト)を返す、関数primitiveRoot(n)を作ることができる。
100以下の素数に対して、原始根のリストを表示してみよう。
#[IN]julia
#=====================================================================
#素数判定関数
function isPrime(n)
lim = Int(round(n^0.5))
if n<2
return false
end
for num in 2:lim
if n % num ==0
return false
end
end
return true
end
#オイラー関数
function eu(n)
divs=filter( m -> n % m==0,1:n) #nの約数リスト
ps= filter(p -> isPrime(p),divs) #nの素因数リスト
zs= [1-1/x for x in ps] #素数の倍数を除く割合リスト
return Int(n *prod(zs))
end
#素数nの原始根を求める関数
function primitiveRoot(n)
q=n-1
divs=filter( m -> q % m==0,1:q) #qの約数リスト
ps= filter(p -> isPrime(p),divs) #qの素因数リスト
ids = [q÷x for x in ps] #テスト用の指数リスト
rems = [ x for x in 1:q] #テスト対象の剰余リスト
#powermodが1になるものを抜き出すために−1して積が0になると除外する。
tested=[prod([powermod(x,id,n)-1 for id in ids]) for x in rems]
#非ゼロになるインデックスが原始根となる。
return findall(!iszero,tested)
end
P =filter(n->length(filter( m -> n % m==0,1:n))==2, 5:100)
pp=[(x,eu(x),primitiveRoot(x)) for x in P]
#=====================================================================
[OUT]
23-element Vector{Tuple{Int64, Int64, Vector{Int64}}}:
(5, 4, [2, 3])
(7, 6, [3, 5])
(11, 10, [2, 6, 7, 8])
(13, 12, [2, 6, 7, 11])
(17, 16, [3, 5, 6, 7, 10, 11, 12, 14])
(19, 18, [2, 3, 10, 13, 14, 15])
(23, 22, [5, 7, 10, 11, 14, 15, 17, 19, 20, 21])
(29, 28, [2, 3, 8, 10, 11, 14, 15, 18, 19, 21, 26, 27])
(31, 30, [3, 11, 12, 13, 17, 21, 22, 24])
(37, 36, [2, 5, 13, 15, 17, 18, 19, 20, 22, 24, 32, 35])
(41, 40, [6, 7, 11, 12, 13, 15, 17, 19, 22, 24, 26, 28, 29, 30, 34, 35])
(43, 42, [3, 5, 12, 18, 19, 20, 26, 28, 29, 30, 33, 34])
(47, 46, [5, 10, 11, 13, 15, 19, 20, 22, 23, 26 … 31, 33, 35, 38, 39, 40, 41, 43, 44, 45])
(53, 52, [2, 3, 5, 8, 12, 14, 18, 19, 20, 21 … 32, 33, 34, 35, 39, 41, 45, 48, 50, 51])
(59, 58, [2, 6, 8, 10, 11, 13, 14, 18, 23, 24 … 40, 42, 43, 44, 47, 50, 52, 54, 55, 56])
(61, 60, [2, 6, 7, 10, 17, 18, 26, 30, 31, 35, 43, 44, 51, 54, 55, 59])
(67, 66, [2, 7, 11, 12, 13, 18, 20, 28, 31, 32, 34, 41, 44, 46, 48, 50, 51, 57, 61, 63])
(71, 70, [7, 11, 13, 21, 22, 28, 31, 33, 35, 42 … 55, 56, 59, 61, 62, 63, 65, 67, 68, 69])
(73, 72, [5, 11, 13, 14, 15, 20, 26, 28, 29, 31 … 42, 44, 45, 47, 53, 58, 59, 60, 62, 68])
(79, 78, [3, 6, 7, 28, 29, 30, 34, 35, 37, 39 … 54, 59, 60, 63, 66, 68, 70, 74, 75, 77])
(83, 82, [2, 5, 6, 8, 13, 14, 15, 18, 19, 20 … 62, 66, 67, 71, 72, 73, 74, 76, 79, 80])
(89, 88, [3, 6, 7, 13, 14, 15, 19, 23, 24, 26 … 63, 65, 66, 70, 74, 75, 76, 82, 83, 86])
(97, 96, [5, 7, 10, 13, 14, 15, 17, 21, 23, 26 … 71, 74, 76, 80, 82, 83, 84, 87, 90, 92])