数论之阶与原根讲解

前言:

本来想写BSGS算法,但是笔者今天想打游戏 感觉先写原根会更好一点,所以我们今天重点讨论一下什么是原根、哪些整数有原根、原根的性质和求解。
 

提要:

为了减少后面的阅读障碍,先简单介绍一下欧拉函数 φ ( x ) \varphi(x) φ(x),主要部分后面放积性函数那介绍。(ps:书上符号写的是 ϕ ( x ) \phi(x) ϕ(x),但网上写的是 φ ( x ) \varphi(x) φ(x),不过这不重要)
欧拉函数 φ ( x ) \varphi(x) φ(x)的函数值是,小于 x x x且与 x x x互质的正整数的个数。
比如 φ ( 6 ) = 2 \varphi(6)=2 φ(6)=2,因为小于 6 6 6的正整数中,仅有 1 1 1 5 5 5与其互质。
容易发现, p p p为素数时,有 φ ( x ) = p − 1 \varphi(x)=p-1 φ(x)=p1
还有欧拉定理:若 g c d ( a , n ) = 1 gcd(a,n)=1 gcd(a,n)=1,则 a φ ( n ) ≡ 1 ( m o d    p ) a^{\varphi(n)}\equiv1(mod\;p) aφ(n)1(modp)
 
差不多先这些,开始正文。
 

整数的阶:

什么是阶?
a a a n n n是互素的整数, a ≠ 0 , n > 0 a\neq 0,n>0 a=0,n>0,使得 a x ≡ 1 ( m o d    n ) a^{x}\equiv1(mod\;n) ax1(modn)成立的最小正整数 x x x称为 a a a n n n的阶,符号表示为 o r d n a ord_{n}a ordna
 
比如要求 2 2 2 7 7 7的阶,我们一一列举,
2 1 ≡ 2 ( m o d    7 ) , 2 2 ≡ 4 ( m o d    7 ) , 2 3 ≡ 1 ( m o d    7 ) 2^{1}\equiv2(mod\;7),2^{2}\equiv4(mod\;7),2^{3}\equiv1(mod\;7) 212(mod7)224(mod7)231(mod7)
那么我们称 2 2 2 7 7 7的阶就是 3 3 3,记为 o r d 7 2 = 3 ord_{7}2=3 ord72=3
 
现在我们观察方程 a x ≡ 1 ( m o d    n ) a^{x}\equiv1(mod\;n) ax1(modn)
那么显然根据欧拉定理,我们可以知道 φ ( n ) \varphi(n) φ(n)是方程的一个解,但它未必是最小的,所以不一定是阶,而当 φ ( n ) \varphi(n) φ(n) a a a n n n的阶时,我们称 a a a n n n的一个原根,这个放后面介绍。

现在继续来讲阶。
 
定理一:若 g c d ( a , n ) = 1 gcd(a,n)=1 gcd(a,n)=1,则 x 0 x_{0} x0是方程 a x ≡ 1 ( m o d    n ) a^{x}\equiv1(mod\;n) ax1(modn)的一个解的充要条件是 o r d n a ∣ x ord_{n}a|x ordnax。(其中 ∣ | 是整除符号,表示 o r d n a ord_{n}a ordna整除 x x x)
这个必要性很好证明,既然 a o r d n a ≡ 1 ( m o d    n ) a^{ord_{n}a}\equiv1(mod\;n) aordna1(modn),那 a k × o r d n a ≡ 1 ( m o d    n ) a^{k\times ord_{n}a}\equiv1(mod\;n) ak×ordna1(modn)当然也成立。
然后充分性,假设存在 x 1 x1 x1不整除 x 0 x_{0} x0且满足方程,令 x 1 = k × o r d n a + d x_{1}=k\times ord_{n}a+d x1=k×ordna+d(其中 d d d不整除 o r d n a ord_{n}a ordna)满足方程,那么 d d d一定比 o r d n a ord_{n}a ordna小,且满足 a d ≡ 1 ( m o d    n ) a^{d}\equiv1(mod\;n) ad1(modn),那它才是原根嘛。
这样就证好了。

定理二:若 g c d ( a , n ) = 1 gcd(a,n)=1 gcd(a,n)=1,则 o r d n a ord_{n}a ordna能整除 φ ( n ) \varphi(n) φ(n)
前面看下来应该也能想到,且由定理一发现显然嘛。
这时候我们容易想到一个 o ( n l o g ) o(\sqrt{n} log) o(n log)求阶的办法,因为 n n n的阶一定是 φ ( n ) \varphi(n) φ(n)的因子,所以可以暴力枚举其因子,再看看是不是满足条件。

定理三:若 g c d ( a , n ) = 1 gcd(a,n)=1 gcd(a,n)=1,则 a i ≡ a j ( m o d    n ) a^i\equiv a^j(mod\;n) aiaj(modn)的充要条件是 i ≡ j ( m o d    o r d n a ) i\equiv j(mod\;ord_{n}a) ij(modordna)
定理比较重要,自己意会一下。

定理四:若 o r d n a = t ord_{n}a=t ordna=t,且 u u u为正整数,则有 o r d n ( a u ) = t g c d ( t , u ) ord_{n}(a^{u})=\frac{t}{gcd(t,u)} ordn(au)=gcd(t,u)t
打个比方体会一下, 2 3 ≡ 1 ( m o d    7 ) 2^3\equiv1(mod\;7) 231(mod7),那么就有 8 1 ≡ 1 ( m o d    7 ) 8^1\equiv1(mod\;7) 811(mod7)。其实就是这个 3 3 3的因子,这样想就容易理解多了吧。
 
 

原根:

a a a n n n的阶为 φ ( n ) \varphi(n) φ(n),也就是说当且仅当 x x x φ ( n ) \varphi(n) φ(n)的倍数,使得 a x ≡ 1 ( m o d    n ) a^{x}\equiv1(mod\;n) ax1(modn)成立,此时称 a a a n n n的原根
 
那么知道了 n n n的原根 a a a后,有什么好处呢,可以举一个例子。
我们举998244353的原根3, 我们举一个素数 7 7 7的一个原根 3 3 3,然后列举 3 3 3的幂次模 7 7 7
3 1 ≡ 3 ( m o d    7 ) , 3 2 ≡ 2 ( m o d    7 ) , 3 3 ≡ 6 ( m o d    7 ) , 3 4 ≡ 4 ( m o d    7 ) , 3 5 ≡ 5 ( m o d    7 ) , 3 6 ≡ 1 ( m o d    7 ) 3^{1}\equiv3(mod\;7),3^{2}\equiv2(mod\;7),3^{3}\equiv6(mod\;7),3^{4}\equiv4(mod\;7),3^{5}\equiv5(mod\;7),3^{6}\equiv1(mod\;7) 313(mod7)322(mod7)336(mod7)344(mod7)355(mod7)361(mod7)
然后发现这些余数构成了一个模 7 7 7的完全剩余系 1 , 2 , 3 , 4 , 5 , 6 1,2,3,4,5,6 1,2,3,4,5,6,也就是对于任意 a a a,都可以找到 x 0 x_{0} x0使得 3 x 0 ≡ a ( m o d    7 ) 3^{x_{0}}\equiv a(mod\;7) 3x0a(mod7)
这是素数的情况,现在我们来康康非素数的情况,举 18 18 18的一个原根 5 5 5。然后列举 5 5 5的幂次模 18 18 18
5 1 ≡ 5 ( m o d    18 ) , 5^{1}\equiv5(mod\;18), 515(mod18)
5 2 ≡ 7 ( m o d    18 ) , 5^{2}\equiv7(mod\;18), 527(mod18)
5 3 ≡ 17 ( m o d    18 ) , 5^{3}\equiv17(mod\;18), 5317(mod18)
5 4 ≡ 13 ( m o d    18 ) , 5^{4}\equiv13(mod\;18), 5413(mod18)
5 5 ≡ 11 ( m o d    18 ) , 5^{5}\equiv11(mod\;18), 5511(mod18)
5 6 ≡ 1 ( m o d    18 ) 。 5^{6}\equiv1(mod\;18)。 561(mod18)
5 7 ≡ 5 ( m o d    18 ) , 5^{7}\equiv5(mod\;18), 575(mod18)
5 8 ≡ 7 ( m o d    18 ) , 5^{8}\equiv7(mod\;18), 587(mod18)
5 9 ≡ 17 ( m o d    18 ) , 5^{9}\equiv17(mod\;18), 5917(mod18)
5 10 ≡ 13 ( m o d    18 ) , 5^{10}\equiv13(mod\;18), 51013(mod18)
5 11 ≡ 11 ( m o d    18 ) , 5^{11}\equiv11(mod\;18), 51111(mod18)
5 12 ≡ 1 ( m o d    18 ) 。 5^{12}\equiv1(mod\;18)。 5121(mod18)
5 13 ≡ 5 ( m o d    18 ) , 5^{13}\equiv5(mod\;18), 5135(mod18)
5 14 ≡ 7 ( m o d    18 ) , 5^{14}\equiv7(mod\;18), 5147(mod18)
5 15 ≡ 17 ( m o d    18 ) , 5^{15}\equiv17(mod\;18), 51517(mod18)
5 16 ≡ 13 ( m o d    18 ) , 5^{16}\equiv13(mod\;18), 51613(mod18)
5 17 ≡ 11 ( m o d    18 ) , 5^{17}\equiv11(mod\;18), 51711(mod18)
5 18 ≡ 1 ( m o d    18 ) 。 5^{18}\equiv1(mod\;18)。 5181(mod18)
很容易发现以 6 6 6个为一组,那这个 6 6 6是什么呢?
其实是 φ ( 18 ) = 6 \varphi(18)=6 φ(18)=6。然后出现的数字 1 , 5 , 7 , 11 , 13 , 17 1,5,7,11,13,17 1,5,7,11,13,17,正是与 18 18 18互质的那 6 6 6个数啊。
那么前面素数的情况也说得通了。
所以我们就可以得到,
定理一:若 g c d ( a , n ) = 1 gcd(a,n)=1 gcd(a,n)=1,则 a 1 , a 2 , … … , a φ ( n ) a^{1},a^{2},……,a^{\varphi(n)} a1,a2,,aφ(n)构成模 n n n的既约剩余系。
大概懂了就不证明了。
而当 p p p为素数时,我们就可以将求解方程 x 5 ≡ a ( m o d    p ) x^{5}\equiv a(mod\;p) x5a(modp)的问题代换为求解 g 5 x ≡ a ( m o d    p ) g^{5x}\equiv a(mod\;p) g5xa(modp),其中 g g g p p p的原根,因为 g g g的幂次是模 n n n的完全剩余系,所以可以用 g x g^{x} gx来代换 x x x,这样我们只要求出新方程的 x 0 x_{0} x0,那么原方程的解就可以求出来了。
 
下一个,
定理二:如果一个数 n n n有原根,那么他有多少个不同余的原根,
答案是 φ ( φ ( n ) ) \varphi(\varphi(n)) φ(φ(n))个。
伪证明:由前面阶那里的定理四,我们可以知道 o r d n ( a u ) = t g c d ( t , u ) ord_{n}(a^{u})=\frac{t}{gcd(t,u)} ordn(au)=gcd(t,u)t,那么当 a a a n n n的原根时,就有 o r d n a = φ ( n ) ord_{n}a=\varphi(n) ordna=φ(n)
于是我们得到, o r d n ( a u ) = φ ( n ) g c d ( φ ( n ) , u ) ord_{n}(a^{u})=\frac{\varphi(n)}{gcd(\varphi(n),u)} ordn(au)=gcd(φ(n),u)φ(n)
所以要使 o r d n ( a u ) ord_{n}(a^{u}) ordn(au)等于 φ ( n ) \varphi(n) φ(n),就要满足 g c d ( φ ( n ) , u ) gcd(\varphi(n),u) gcd(φ(n),u),在 1 1 1 φ ( n ) − 1 \varphi(n)-1 φ(n)1范围里自然就有 φ ( φ ( n ) ) \varphi(\varphi(n)) φ(φ(n))个满足条件的。

吃饭去了,回来再写。

现在研究哪些数是有原根的,也就是原根的存在性
因为刚吃了饭,我懒了。
先证明素数都是有原根的。
再证明奇素数的幂都是有原根的。
然后讨论关于 2 2 2的幂次,发现 2 2 2 4 4 4是有原根的,其他 2 2 2的幂次都没有原根。
然后再发现 2 2 2乘上奇素数的幂也是有原根的。
最后再证明剩下的都没有原根。
于是,得出结论:
定理三: 2 2 2 4 4 4,和奇素数的正整数幂次 p k p^{k} pk,以及 2 2 2乘上奇素数的正整数幂次 2 p k 2p^{k} 2pk都是有原根的。
 
 
最后,再研究一下怎么求 p p p的原根。传送门
先给出一种暴力的解法。(没有第二种了)
先判断有无原根。
2 2 2的原根是 1 1 1 3 3 3的原根是2, 4 4 4的原根是 3 3 3直接特判掉。
然后我们暴力枚举 [ 2 , p − 1 ] [2,p-1] [2,p1],再判断它是不是 p p p的原根,枚举的时候当然还要保证与模数互质。
至于要判断 i i i是不是 p p p的原根,只要看是否存在 x 0 x_{0} x0 φ ( p ) \varphi(p) φ(p)小,且满足 i x 0 ≡ 1 ( m o d    p ) i^{x_{0}}\equiv 1(mod\;p) ix01(modp)
但是我们不至于再去一个一个枚举 x 0 x_{0} x0,因为我们知道它的阶是满足方程的,而阶的倍数也是满足方程的。所以我们只要将 φ ( p ) \varphi(p) φ(p)质因子分解成 p 1 k 1 p 2 k 2 … p n k n p_{1}^{k_{1}}p_{2}^{k_{2}}…p_{n}^{k_{n}} p1k1p2k2pnkn
然后枚举每个质因子,判断是否存在 i φ ( p ) p j ≡ 1 ( m o d    p ) i^{\frac{\varphi(p)}{p_{j}}}\equiv1(mod\;p) ipjφ(p)1(modp)。若存在,说明阶比 φ ( p ) \varphi(p) φ(p)小,则不是原根;反之,则是原根。

大力出奇迹,不愧是大力。
最后贴代码:

#include 
using namespace std;
typedef long long ll;
const ll mod=1e9+7;

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

ll fpow(ll a,ll n,ll p)
{
    ll sum=1,base=a%p;
    while(n!=0)
    {
        if(n%2)sum=sum*base%p;
        base=base*base%p;
        n/=2;
    }
    return sum;
}

vector<ll> getPrimFac(ll n)
{
    vector<ll>fac;
    fac.clear();
    for(ll i=2;i*i<=n;i++)
    {
        if(n%i==0)
        {
            fac.push_back(i);
            while(n%i==0)n/=i;
        }
    }
    if(n>1)fac.push_back(n);
    return fac;
}

bool hasRoot(ll n)
{
    if(n==2||n==4)return true;
    if(n<=1||n%4==0)return false;
    ll num=0;
    while(n%2==0)n/=2;
    for(ll i=3;i*i<=n;i++)
    {
        if(n%i==0)
        {
            num++;
            while(n%i==0)n/=i;
        }
    }
    if(n>1)num++;
    if(num==1)return true;
    return false;
}

ll getPhi(ll n)
{
    ll ans=n;
    for(ll i=2;i*i<=n;i++)
    {
        if(n%i==0)
        {
            ans=ans/i*(i-1);
            while(n%i==0)n/=i;
        }
    }
    if(n>1)ans=ans/n*(n-1);
    return ans;
}

ll getRoot(ll n)
{
    if(!hasRoot(n))return -1;//不存在原根返回-1
    if(n==2)return 1;
    if(n==3)return 2;
    if(n==4)return 3;
    ll w=getPhi(n);
    vector<ll>fac=getPrimFac(w);
    for(ll i=2;i<w;i++)
    {
        if(gcd(i,n)!=1)continue;
        ll is=1;
        for(ll j=0;j<fac.size();j++)
        {
            if(fpow(i,w/fac[j],n)==1)is=0;
        }
        if(is)return i;
    }
    return -1;
}

int main()
{
    ll p;
    scanf("%lld",&p);
    printf("%lld\n",getRoot(p));
    return 0;
}

你可能感兴趣的:(数论)