阶
定义
若 (a,m)=1,满足 an=1(modm) 的最小的 n 称为 a 模 m 的阶,记作 δma。
性质
- a,a2,…,aδma 模 m 两两不同。
- 若 an≡1(modm)⟺δma∣n
- ap≡aq(modm)⟺p≡q(modδma)。
- δm(ab)=δmaδmb⟺(δma,δmb)=1。
- δm(ak)=(δma,k)δma。
- ∃k,ak=b⟺δmb∣δma。(可用阶性质 5 证明)
求法
如果求 a 的阶,初始令阶 x 为 φ(m),对 φ(m) 的每一个质因数,如果除了它之后仍然有 ax≡1(modm),则不断除掉这个质因数,最后得到的就是它的阶。
复杂度 O(m+log2m)。(预处理质因数 m,有 logm 个质因数,每次快速幂判断复杂度为 logm).
参考代码:
若已知:
1 2 3 4 5 6 7 8 9 10 11 12
| ll ksm(ll a,ll b,ll p){ ll r = 1; while(b){ if(b&1)r=(__int128_t)r*a%p; a=(__int128_t)a*a%p; b>>=1; } return r; }
ll a,p; vector<ll> fp;
|
则 ord
就是 a
的阶。
1 2 3 4
| ll ord = p-1; for(auto f:fp){ while(ord%f==0 && ksm(a,ord/f,p)==1)ord/=f; }
|
原根
定义
若 δm(g)=φ(m),称 g 为模 m 的原根。
性质
- 当 m 为质数时,gimodm(0<i<m) 的值互不相同,恰为 1,2,…,m−1。(阶性质 1)
- (原根判定定理)g 是 m的原根⟺∀p∣φ(m),ggφ(m)≡1(modm) 且 (g,m)=1。
- (原根个数定理)若 g 为 m 的一个原根,则所有原根可以表示为 gk,其中 (k,φ(m))=1。故原根个数有 φ(φ(m))个。
- (原根存在定理)m 存在原根当且仅当 m=2,4 或 m=pα,2pα,其中
p 为质数,α∈N∗。
- 若一个数 m 存在原根,其最小原根 g=O(m1/4)。
求法
- 判断原根是否存在;O(n) (或预处理 O(n))
- 若存在,从 1 开始枚举与 n 互质的数,找到最小原根;O(n0.25log2n)
- 找出所有与 φ(n) 互质的 k,求出所有原根。O(φ(n)logφ(n))
- (对所有原根排序. O(φ(φ(n))logφ(φ(n)))。
参考代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| #include<bits/stdc++.h> using namespace std;
using ll=long long; inline int read(){int f=1,x=0;char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=x*10+c-'0';c=getchar();}return x*f;}
const int N = 1e6+5; bool vis[N]; int pri[N],cnt,phi[N]; bool have[N];
int ksm(int a,int b,int p){ int r = 1; while(b){ if(b&1)r=(ll)r*a%p; a=(ll)a*a%p; b>>=1; } return r; }
int main(){ phi[1]=1; for(int i=2;i<N;i++){ if(!vis[i])pri[cnt++]=i,phi[i]=i-1; for(int j=0;j<cnt&&i*pri[j]<N;j++){ vis[i*pri[j]]=1; if(i%pri[j]==0){ phi[i*pri[j]]=phi[i]*pri[j]; break; } phi[i*pri[j]]=phi[i]*(pri[j]-1); } } have[2]=have[4]=1; for(int i=1;i<cnt;i++){ for(ll j=pri[i];j<N;j*=pri[i])have[j]=1; for(ll j=2*pri[i];j<N;j*=pri[i])have[j]=1; }
int T=read(); while(T--){ int n=read(),d=read(); if(!have[n]){ puts("0\n"); continue; }
vector<int> fac; int P = phi[n]; for(int i=2;i*i<=P;i++){ if(P%i==0){ fac.push_back(i); while(P%i==0)P/=i; } } if(P!=1)fac.push_back(P); vector<int> g; for(int i=1;;i++){ if(__gcd(n,i)!=1)continue; bool ok = 1; for(auto p:fac) if(ksm(i,phi[n]/p,n)==1){ok=0;break;}
if(ok){ for(int k=1;k<=phi[n];k++) if(__gcd(k,phi[n])==1)g.push_back(ksm(i,k,n)); break; } } sort(g.begin(),g.end()); assert(g.size()==phi[phi[n]]); printf("%d\n",(int)g.size()); for(int i=d-1;i<g.size();i+=d)printf("%d ",g[i]); puts(""); }
return 0; }
|
离散对数
定义
设有原根的正整数 m,设其一个原根为 g。
对于 (a,m)=1 的整数 a 恰有一个 k 使 gk≡a(modm),记 k=indga。
性质
- indg1=0。
- indgg=1。
- ind(ab)=inda+indb(modφ(m)),indan≡ninda(modφ(m))。
- indg1a≡indg1g2indg2a(modφ(m))。
- a≡b(modm)⟺inda=indb。
注意:在取离散对数之后,取模变为 φ(m),而不是原来的 m。
求法
BSGS 算法:在 O(m) 的时间内求 ax=b(modm) 的解。要求 (a,m)=1。
设 t=⌈m⌉。不妨设解 x=it−j。(0≤j≤t)
则 ait−j≡b(modm),即 ait≡baj(modm)。
求出 i,j 后就能得到答案。(互质的原因是我们推导用到了逆元)
参考代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include<bits/stdc++.h> using ll=long long; using namespace std; ll ksm(ll a,ll b,ll p){ ll r=1; while(b){ if(b&1)r=r*a%p; a=a*a%p; b>>=1; } return r; } int main(){ ll p,a,b; scanf("%lld%lld%lld",&p,&a,&b); ll t=ceil(sqrt(p)); map<ll,int>vis; for(ll i=0,now=b;i<=t;i++,now=now*a%p)vis[now]=i; ll at = ksm(a,t,p); for(ll i=1,now=at;i<=t;i++,now=now*at%p){ if(vis.count(now)){ printf("%lld\n",i*t-vis[now]); return 0; } } puts("no solution"); return 0; }
|
习题
P6091 【模板】原根
P3846 [TJOI2007] 可爱的质数/【模板】BSGS
[ABC335G] Discrete Logarithm Problems (参考代码)