高次同余方程之阶与原根

版权声明:转载请注明原出处啦QAQ(虽然应该也没人转载): https://blog.csdn.net/hzk_cpp/article/details/89669794

一.阶与阶的性质.

首先我们回忆一下欧拉定理.

欧拉定理:对于 g c d ( a , p ) = 1 gcd(a,p)=1 ,有 a ϕ ( p ) 1    ( m o d    p ) a^{\phi(p)}\equiv 1\,\,(mod\,\,p) .

根据欧拉定理,若 g c d ( a , p ) = 1 gcd(a,p)=1 ,则必然有一个数 x x 满足 a x 1    ( m o d    p ) a^x\equiv 1\,\,(mod\,\,p) .

:对于一个满足 g c d ( a , p ) = 1 gcd(a,p)=1 的离散对数方程 a x 1    ( m o d    p ) a^x\equiv 1\,\,(mod\,\,p) ,我们称最小的正整数 x x a a p p 的阶,我们记为 o r d p a ord_pa .

x = ϕ ( p ) x=\phi(p) 时,我们称 a a p p 的原根.

阶有如下几个性质:

性质1 a x 1    ( m o d    p ) o r d p a x a^x\equiv 1\,\,(mod\,\,p)\Leftrightarrow ord_p a|x .

证明:
首先,若 o r d p a x ord_p a|x ,设 x = k o r d p a x=k*ord_p a ,则:
a x a k o r d p a ( a o r d p ) k 1    ( m o d    p ) a^x\equiv a^{k*ord_p a}\equiv (a^{ord_p})^k\equiv 1\,\,(mod\,\,p)

反过来,对于任意 x = k o r d p a + r    ( 0 < r < o r d p a ) x=k*ord_p a+r\,\,(0<r<ord_p a) ,则:
a x a k o r d p a + r a r    ( m o d    p ) a^x\equiv a^{k*ord_p a+r}\equiv a^r\,\,(mod\,\,p)

根据定义, o r d p a ord_p a 是使得 a x 1    ( m o d    p ) a^x\equiv 1\,\,(mod\,\,p) 的最小正整数,不存在更小的正整数满足此方程,故原定理得证.
证毕.

扫描二维码关注公众号,回复: 6163636 查看本文章

性质2 o r d p a ϕ ( p ) ord_p a|\phi(p) .

利用欧拉定理与性质1即可得证.

性质3 { a 0 , a 1   a 2 , . . . , a o r d p a } \{a^0,a^1\,a^2,...,a^{ord_p a}\} 构成 p p 的简化剩余系.即 i j    ( m o d    o r d p a ) a i a j    ( m o d    p ) i\equiv j\,\,(mod\,\,ord_p a)\Leftrightarrow a^i\equiv a^j\,\,(mod\,\,p) .

证明:
i j    ( m o d    o r d p a ) i\equiv j\,\,(mod\,\,ord_p a) ,令 i = k o r d p a + j i=k*ord_p a+j ,那么:
a i a k o r d p a + j a j a k o r d p a a j    ( m o d    p ) a^i\equiv a^{k*ord_p a+j}\equiv a^ja^{k*ord_p a}\equiv a^j\,\,(mod\,\,p)

反之,若 a i a j    ( m o d    p ) a^i\equiv a^j\,\,(mod\,\,p) i j i\geq j ,由于 g c d ( a j , n ) = 1 gcd(a^j,n)=1 ,显然:
a i a j a j a i j    ( m o d    p ) a i j 1 a^i\equiv a^j\equiv a^ja^{i-j}\,\,(mod\,\,p)\\ a^{i-j}\equiv 1

由性质1可得 o r d p a i j ord_p a|i-j ,即 i j    ( m o d    o r d p a ) i\equiv j\,\,(mod\,\,ord_p a) .
证毕.

性质4:令 t = o r d p a t=ord_p a u Z u\in Z ,那么有:
o r d p ( a u ) = t g c d ( t , u ) ord_p(a^u)=\frac{t}{gcd(t,u)}

证明:
g = g c d ( t , u ) , t = t g , u = u g g=gcd(t,u),t'=\frac{t}{g},u'=\frac{u}{g} ,显然有:
( a u ) t a u t 1    ( m o d    p ) (a^u)^{t'}\equiv a^{u't}\equiv 1\,\,(mod\,\,p)

反过来,由于 g c d gcd 的极大性,显然 t t' 是最小的满足 ( a u ) t 1    ( m o d    p ) (a^u)^{t'}\equiv 1\,\,(mod\,\,p) 的正整数.
证毕.


二.原根与原根的性质.

原根:若 g c d ( r , p ) = 1 gcd(r,p)=1 且有 o r d p r = ϕ ( p ) ord_p r=\phi(p) 时,我们称 r r p p 的原根.

关于原根有几个性质:

性质5:若 g c d ( r , p ) = 1 gcd(r,p)=1 r r p p 的一个原根,则 { r 1 , r 2 , r 3 , . . . , r ϕ ( p ) } \{r^1,r^2,r^3,...,r^{\phi(p)}\} 构成 p p 的简化剩余系.

利用性质3可证.

性质6:若 r r p p 的原根,那么 r u r^u p p 的原根当且仅当 g c d ( u , ϕ ( p ) ) = 1 gcd(u,\phi(p))=1 .

证明:
由性质4可得:
o r d p r u = o r d p r g c d ( u , o r d p r ) = ϕ ( p ) g c d ( u , ϕ ( p ) ) ord_p r^u=\frac{ord_p r}{gcd(u,ord_p r)}=\frac{\phi(p)}{gcd(u,\phi(p))}

证毕.

性质7:若 p p 由原根,则它所有在模 p p 意义下的不同原根数量为 ϕ ( ϕ ( p ) ) \phi(\phi(p)) 个.

证明:
r r p p 的一个原根,根据性质3,则 { r 1 , r 2 , . . . r ϕ ( p ) } \{r^1,r^2,...r^{\phi(p)}\} p p 的一个简化剩余系.根据性质6, r u r^u p p 的原根当且仅当 g c d ( u , ϕ ( p ) ) = 1 gcd(u,\phi(p))=1 ,所有满足的 u u ϕ ( ϕ ( p ) ) \phi(\phi(p)) 个.
证毕.

性质8:任意素数都有原根.

这个东西需要研究多项式同余理论,这里不再赘述.

性质9:一个正整数 n n 存在原根当且仅当 n = 2 , 4 , p t , 2 p t n=2,4,p^t,2p^t .其中 p p 为奇素数(不等于 2 2 的素数)且 t t 为正整数.

这个东西需要研究大量初等数论知识,这里不再赘述.


三.求解阶与原根.

求解 o r d n a ord_n a ,可以通过性质2, O ( n ) O(\sqrt{n}) 求出 ϕ ( n ) \phi(n) ,然后枚举 ϕ ( n ) \phi(n) 的因数,每次通过快速幂判定即可,时间复杂度 O ( n log n ) O(\sqrt{n}\log n) .

代码如下:

LL gcd(LL a,LL b){return b?gcd(b,a%b):a;}

LL power(LL a,LL k,LL p){
  LL s=1;
  for (;k;k>>=1,a=a*a%p)
    if (k&1) s=s*a%p;
  return s;
}

LL phi(LL n){
  LL v=n,ans=n;
  for (LL i=2;i*i<=n;++i)
    if (v%i==0){
      ans=ans/i*(i-1);
      while (v%i==0) v/=i;
	}
  if (v>1) ans=ans/v*(v-1);
  return ans;
}

LL get_ord(LL a,LL n){      //求解阶ord_n a,若没有阶返回-1
  if (gcd(a,n)^1||n<2) return -1;
  LL v=phi(n),mn=v,t;
  for (LL i=1;i*i<=v;++i)
    if (v%i==0){
	  if (power(a,i,n)==1) mn=min(mn,i);
	  t=v/i;if (power(a,t,n)==1) mn=min(mn,t);
	}
  return mn;
}

求解 n n 的原根(一般是最小原根),首先要了解一个比较容易的性质.

性质10:设唯一分解 ϕ ( n ) = i = 1 k p i c i \phi(n)=\prod_{i=1}^{k}p_i^{c_i} ,那么 g g n n 的原根当且仅当 g c d ( g , n ) = 1 gcd(g,n)=1 且:
i , g ϕ ( n ) p i ̸ 1 &ThinSpace;&ThinSpace; ( m o d &ThinSpace;&ThinSpace; n ) \forall i,g^{\frac{\phi(n)}{p_i}}\equiv\not{} 1\,\,(mod\,\,n)

很显然就不证了吧…

那么我们先把 n n 的所有素因子存下来.由于 n n 的最小原根 g g 一般较小,所以暴力枚举 g g ,每次大力判定是否满足条件即可.时间复杂度 O ( n + g log 2 n ) O(\sqrt{n}+g\log^2 n) .

若需要判定是否有原根,则需要用到性质9.

代码如下:

LL gcd(LL a,LL b){return b?gcd(b,a%b):a;}

LL power(LL a,LL k,LL p){
  LL s=1;
  for (;k;k>>=1,a=a*a%p)
    if (k&1) s=s*a%p;
  return s; 
}

LL get_proot(LL n){
  if (n==2) return 1;
  if (n==4) return 3;
  LL v,phi=n;
  v=n&1?n:n>>1;
  if (v&1^1) return -1;
  for (LL i=3;i*i<=n;++i)
    if (v%i==0){
      phi=phi/i*(i-1);
      while (v%i==0) v/=i;
      if (v>1) return -1;
	}
  if (v>1) phi=phi/v*(v-1);
  if (n&1^1) phi>>=1;
  v=phi;
  for (LL i=2;i*i<=phi;++i)
    if (v%i==0){
      pr[++tp]=i;
      while (v%i==0) v/=i;
	}
  if (v>1) pr[++tp]=v;
  bool flag;
  for (LL g=2;g<n;++g){
  	if (gcd(g,n)^1) continue;
  	flag=0;
    for (int i=1;i<=tp;++i)
      if (power(g,phi/pr[i],n)==1){flag=1;break;}
    if (flag) continue;
    return g;
  }
}

当然还有一份仅仅用于求素数原根的代码:

LL power(LL a,LL k,LL p){
  LL s=1;
  for (;k;k>>=1,a=a*a%p)
    if (k&1) s=s*a%p;
  return s; 
}

LL get_proot(LL n){
  if (n==2) return 1;
  LL phi=n-1,v=phi;
  for (LL i=2;i*i<=phi;++i)
    if (v%i==0){
      pr[++tp]=i;
      while (v%i==0) v/=i;
	}
  if (v>1) pr[++tp]=v;
  bool flag;
  for (LL g=2;g<n;++g){
  	flag=0;
  	for (int i=1;i<=tp;++i)
  	  if (power(g,phi/pr[i],n)==1) {flag=1;break;}
  	if (flag) continue;
  	return g;
  }
}



四.例题与代码.

题目(原根板子):51nod1135.

代码如下:

#include<bits/stdc++.h>
  using namespace std;
  
#define Abigail inline void
typedef long long LL;

const int N=100;

LL p,pr[N+9],tp;

LL power(LL a,LL k,LL p){
  LL s=1;
  for (;k;k>>=1,a=a*a%p)
    if (k&1) s=s*a%p;
  return s; 
}

LL get_proot(LL n){
  if (n==2) return 1;
  LL phi=n-1,v=phi;
  for (LL i=2;i*i<=phi;++i)
    if (v%i==0){
      pr[++tp]=i;
      while (v%i==0) v/=i;
	}
  if (v>1) pr[++tp]=v;
  bool flag;
  for (LL g=2;g<n;++g){
  	flag=0;
  	for (int i=1;i<=tp;++i)
  	  if (power(g,phi/pr[i],n)==1) {flag=1;break;}
  	if (flag) continue;
  	return g;
  }
}

Abigail into(){
  scanf("%lld",&p);
}

Abigail outo(){
  printf("%lld\n",get_proot(p));
}

int main(){
  into();
  outo();
  return 0;
}

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/89669794