導入
なんか 1 になります!
あけおめなので、抱負
ん〜、
とかですかね
原始根
いきなりですが、これは原始根です!
一般には、p を素数とすると、適当に r を取れば
r1r2rp−2rp−1≡1(mod p)≡1(mod p)⋯≡1(mod p)≡1(mod p)
r を
p に対応する原始根と言います
p=5, r=2 の場合
21222324≡2(mod p)≡4(mod p)≡3(mod p)≡1(mod p)
「いつ使うのか?」というと、フーリエ変換で使いますが、それはまた今度にして、今回は原始根を計算することに全集中します。素数 p を与えられたら、r を計算できるようにしたいということです!
57 に対応する原始根は何かわかりますか?
ちなみに、任意の有限体でも、乗法群は巡回群になります
証明は忘れました
原始根の計算方法
方針としては、r=1,2,3,… と順番に見ていって、それが原始根になるか試します
普通にやると r,r2,r3,… を p で割った余りを順番に計算して、…,rp−3,rp−2 までずっと ≡1 で、rp−1 で初めて ≡1 になることをチェックすればいいです
p が大きい場合はしんどそうです、、、なので、原始根かどうかをある程度効率的に判定したいですが、次のような判定条件があります:
p−1=q1⋯qk と素因数分解したとき
rqip−1≡1(mod p)(1≤i≤k)
が成り立てば
r は
p に対応する原始根になる
p=97 の場合
p−1=96=25⋅3
r=1 では
r296=r48=1→ダメ
r=2 では
r296=r48=281474976710656≡1(mod 97)→ダメ
r=3 では
r296=r48=79766443076872509863361≡1(mod 97)→ダメ
r=4 では
r296=r48=79228162514264337593543950336≡1(mod 97)→ダメ
r=5 では
r296=r48=3552713678800500929355621337890625≡96(mod 97)r396=r32=23283064365386962890625≡35(mod 97)→OK
r=5 は
p=97 に対応する原始根なことがわかりました
繰り返し 2 乗法
前節で 348 を 97 で割った余りなどを計算しましたが、愚直に計算すると
313233343536347348=3=3⋅3=9=9⋅3=27=27⋅3=81=81⋅3=243≡49≡49⋅3=147≡50⋯⋯⋯≡54⋅3=162≡65≡65⋅3=195≡1(mod 97)(mod 97)(mod 97)(mod 97)(mod 97)(mod 97)(mod 97)(mod 97)
と計算することになります
ですが、先に 31,32,34,… を計算しておきます:
31323438316332=3=3⋅3=9=9⋅9=81=81⋅81=6561≡62≡62⋅62=3844≡61≡61⋅61=3721≡35(mod 97)(mod 97)(mod 97)(mod 97)(mod 97)(mod 97)
そして
48 を
48=16+32 と
2 進展開すると
348=316+32=316⋅332≡61⋅35=2135≡1(mod 97)
のように計算を減らすことができます。
p が大きくなると影響は顕著です
最後に "type"script で
新年なので、typescript に計算させましょう!
型レベルで四則演算をできるライブラリを探していたら、↓を見つけました
https://github.com/gvergnaud/hotscript
使い心地はかなり良かったですが、再帰回数が多くなりがちな気もします。でも、おすすめかも
codesandbox
https://codesandbox.io/p/devbox/primitive-root-qdz2xm

コード
import type {
_,
Call,
Fn,
Numbers,
PartialApply,
Pipe,
Tuples,
Unions,
} from 'hotscript';
type PrimeFactors<N extends number, Q extends number = 2> =
Call<Numbers.LessThanOrEqual<Call<Numbers.Mul<Q, Q>>, N>> extends true
? Call<Numbers.Equal<Call<Numbers.Mod<N, Q>>, 0>> extends true
? PrimeFactors<Call<Numbers.Div<N, Q>>, Q> | Q
: PrimeFactors<N, Call<Numbers.Add<Q, 1>>>
: N extends 1
? never
: N;
type PMul<A extends number, B extends number, P extends number> = Call<
Numbers.Mod<Call<Numbers.Mul<A, B>>, P>
>;
type PPow<
R extends number,
N extends number,
P extends number,
RPowMap extends Record<number, number> = {
0: 1;
1: R;
2: PMul<R, R, P>;
3: PMul<PMul<R, R, P>, R, P>;
4: PMul<PMul<R, R, P>, PMul<R, R, P>, P>;
},
> =
Call<Numbers.Equal<N, 0>> extends true
? 1
: PMul<
PPow<PMul<R, RPowMap[4], P>, Call<Numbers.Div<N, 5>>, P>,
RPowMap[Call<Numbers.Mod<N, 5>>],
P
>;
interface PPowFn extends Fn {
return: PPow<this['arg0'], this['arg1'], this['arg2']>;
}
type IsPrimitiveRoot<
P extends number,
R extends number,
T extends number,
> = Pipe<
PrimeFactors<T>,
[
Unions.ToTuple,
Tuples.Map<Numbers.Div<T, _>>,
Tuples.Map<PartialApply<PPowFn, [R, _, P]>>,
Tuples.Every<Numbers.NotEqual<1>>,
]
>;
type PrimitiveRoot<
P extends number,
R extends number = 1,
T extends number = Call<Numbers.Sub<P, 1>>,
> =
IsPrimitiveRoot<P, R, T> extends true
? R
: PrimitiveRoot<P, Call<Numbers.Add<R, 1>>, T>;
export type r1 = PrimitiveRoot<5>;
export type r2 = PrimitiveRoot<17>;
export type r3 = PrimitiveRoot<97>;
export type R1 = PrimitiveRoot<998244353>;
export type R2 = PrimitiveRoot<167772161>;
export type R3 = PrimitiveRoot<469762049>;
参考
https://37zigen.com/primitive-root/
Discussion