原根总结

**~~

原根总结

**~~
数学彩笔继续自救之路
本文不讨论任何证明,对证明有兴趣移步这里
原根总结_第1张图片原根总结_第2张图片
原根的性质:
1.不是所有的整数都有原根。但所有的素数都有原根。
2.如果 a 与 m 是互质的整数且n>0,则如果a是模m的一个原根,那么整数a, a^2, … , a^φ(m)构成模m的既约剩余系。(既约剩余类,即简化剩余类,是指在每个模m的值与m互质的剩余类中,各取一数组成的集合)
3.当正整数m有原根时,有φ(φ(m))个原根。
4.对所有φ(m)的素因子i,若a^(φ(m)/i) mod m 均不为1 则a为模m的一个原根(用来找模m的原根)
5.模m有原根的充要条件是m= 1,2,4,p^n, 2*p^n。其中p是奇质数,n是任意正整数。(用来判断一个数有没有原根)
6.模m的最小原根小于等于m^0.25。
7.模m的所有原根之积mod m值为1。
8.模m的全体原根之和mod m = 莫比乌斯函数μ(n-1)(我还不会这个我记他干嘛)
9.找到最小原根,对所有小于φ(m)互素的k,a^k mod m也为模m的原根(用来得到最小原根后求一个数的所有原根)

模板:
检查一个数有无原根

bool check_proot(int p)//先打素数表
{
    if(n==2||n==4) return true;
    if(p%2==0) p/=2;
    if(is_prime[p]) return true;
    if(p%2==0) return false;
    repi(i,2,cntp)if(p%prime[i]==0){
        while(p%prime[i]==0) p/=prime[i];
        return p==1;
    }
    return false;
}

找最小原根

int find_proot_min(int n)//模n的最小原根 最大的话从n-1倒序即可 保证有原根再调用
{
	repi(i,2,n-1)if(qpow(i,phi[n],n)==1){//首先首先i^phi[n]==1(mod n)
        bool flag=true;
        repi(j,1,pos)if(qpow(i,phi[n]/divi[j],n)==1){//判是不是原根
            flag=false; break;
        }
    	if(flag) return i;
	}
}   

找所有原根

int now=1,proot=   //proot为该数最小原根
ans.clear();
repi(i,1,phi[n]-1){
    now=1ll*now*root%n;
    if(__gcd(i,phi[n])==1) ans.pb(now);
}
sort(all(ans)); ans.erase(unique(all(ans)),ans.end());//去重

例题
1.poj1284 性质3

const int MAX_N=1e5+5;
int phi[MAX_N];
void phi_table(int n)
{
    repi(i,2,n) phi[i]=0;
    phi[1]=1;
    repi(i,2,n)if(!phi[i]){
    	for(int j=i;j<=n;j+=i){
            if(!phi[j]) phi[j]=j;
            phi[j]=phi[j]/i*(i-1);
    	}
	}    
}
int main()
{
	phi_table(65536);
	int n;
	while(~si(n)) printf("%d\n",phi[phi[n]]);
	return 0;
}
/*
素数一定有原根
模m有原根时有phi[phi[m]]个原根
*/

2.hdu4992 判一个数有无原根,有的话找出其所有原根
性质5判有无 性质4找最小 性质9找所有

const int MAX_N=1e6+5;
ll qpow(ll x,ll n,ll mod){ ll res=1;while(n){ if(n&1) res=(res*x)%mod; x=x*x%mod,n>>=1; } return res; }
int phi[MAX_N];
void phi_table(int n)
{
    repi(i,2,n) phi[i]=0;
    phi[1]=1;
    repi(i,2,n)if(!phi[i]){
        for(int j=i;j<=n;j+=i){
            if(!phi[j]) phi[j]=j;
            phi[j]=phi[j]/i*(i-1);
        }
    }    
}
bool is_prime[MAX_N];
int prime[MAX_N/10],cntp=0;
void init()
{
    for(ll i=2;i<MAX_N;++i)    is_prime[i]=1;
    for(ll i=2;i<MAX_N;++i){ 
        if(is_prime[i]){
            prime[++cntp]=i;    
            for(ll j=i*i;j<MAX_N;j+=i) 
                 is_prime[j]=0;
        }
    }
}
bool check(int p)
{
    if(p%2==0) p/=2;
    if(is_prime[p]) return true;
    if(p%2==0) return false;
    repi(i,2,cntp)if(p%prime[i]==0){
        while(p%prime[i]==0) p/=prime[i];
        return p==1;
    }
    return false;
}
int divi[MAX_N],pos;
vector<int> ans;
int main()
{
    phi_table(MAX_N-1),init();
    int n;
    while(~si(n))
    {
        if(n==2) puts("1");
        else if(n==4) puts("3");
        else if(!check(n)) puts("-1");//判是否存在原根(2,4, p^n,2*p^n p为奇素数)
        else{
            pos=0;
            int tmp=phi[n];
            repi(i,1,cntp){
                if(prime[i]*prime[i]>tmp) break;
                if(tmp%prime[i]==0) divi[++pos]=prime[i];
                while(tmp%prime[i]==0) tmp/=prime[i];
            }//处理出素因子,之后找原根用
            if(tmp!=1) divi[++pos]=tmp;
            int now=1,root;
            repi(i,2,n-1)if(qpow(i,phi[n],n)==1){//首先i^phi[n]==1(mod n)
                bool flag=true;
                repi(j,1,pos)if(qpow(i,phi[n]/divi[j],n)==1){//再根据性质找最小满足的原根
                    flag=false; break;
                }
                if(flag){
                    root=i;break;
                }
            }
            ans.clear();
            repi(i,1,phi[n]-1){
                now=1ll*now*root%n;
                if(__gcd(i,phi[n])==1) ans.pb(now);//根据性质处理出其它的
            }
            sort(all(ans)); ans.erase(unique(all(ans)),ans.end());//去重
            int len=ans.size();
            repi(i,0,len-1) printf("%d%c",ans[i],ce(i,len-1));
        }
    }
    return 0;
}

你可能感兴趣的:(总结)