原根与指标

参考资料

一.阶

1.定义:设a,p是互质整数,必定有x满足ax1(modp),最小的满足条件的正整数x称为ap的阶,记为Ordp(a),如果gcd(a,p)!=1,则不存在阶。
2.性质:
<1>设a,p是互质整数,存在x满足ax1(modp),则Ordp(a)|x
<2>设a,p是互质整数,有Ordp(a)|φ(p)(根据欧拉定理反证)。

二.原根

1.定义:设a,p为互质整数,若Ordp(a)=φ(p),则称a是模p的一个原根。
2.性质:
<1>只有2,4,pk,2pkp是奇素数)有原根。
<2>一个数p若存在原根,则它有φ(φ(p))个原根。
<3>ap的原根,a0,a1,a2...aOrdp(a)1p两两不同。即当ap的原根时,a0,a1,a2...aOrdp(a)1构成了模p的简化剩余系。
3.求一个数的原根:
暴力:
2开始暴力枚举x,要求gcd(x,p)=1,之后检验x是否为p的原根。
由于OrdP(x)|φ(p),我们需要检验对于任意d|φ(p)dφ(p),是否都满足xd1(modp)
优化:
由于如果xk1(modp),则xkd1(modp)
φ(p)=i=1kpici
只需要对每个i验证是否满足xφ(p)pi1(modp),因为φ(p)pi是除去分解后次幂中含有pici以外的所有约数的倍数。这样就相当于判掉了所有φ(n)的约数。

三.指标

1.定义:设g是模p的一个原根,整数ap互质,则存在唯一的整数x满足:gxa(modp),称x为以g为底对模p的一个指标。记为x=indga
2.性质:
<1>ab(modp)>indgaindgb(modφ(p)))
<2>indg(ab)indga+indgb(modφ(p))
<3>indg(ak)kindga(modφ(p))
3.求指标:直接BSGS即可。

四.N次剩余问题(转自)

求解xka(modp),p是质数。
先求出p的最小原根g,因为p是质数,因此必定存在indgxindga
所求即变为kindgxindga(modφ(p))
exgcd解出即可。
模板题
code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int a,K,mod,g,t,x,y,d;
vector<int>prime,ans;
inline int power(int x,int k,int mod)
{
	int res=1;
	while(k)
	{
		if(k&1)res=res*x%mod;
		x=x*x%mod;k>>=1;
	}
	return res;
}
inline int find(int p)
{
	int tmp=p-1;
	for(int i=2;i*i<=tmp;i++)
	{
		if(tmp%i)continue;
		prime.push_back(i);
		while(tmp%i==0)tmp/=i;
	}
	if(tmp>1)prime.push_back(tmp);
	for(int g=1;g<=p;g++)
	{
		bool flag=1;
		for(unsigned int i=0;i<prime.size();i++)
			if(power(g,(p-1)/prime[i],p)==1){flag=0;break;}
		if(flag)return g;
	}
	return -1;
}
inline int BSGS(int a,int b,int mod)
{
	if(b==1)return 0;
	if(!a)return !b?1:-1;
    map<int,int>mp;mp.clear();
    int t=ceil(sqrt(mod));
    for(int i=0,j=1;i<=t;i++,j=j*a%mod)mp[b*j%mod]=i;
    a=power(a,t,mod);
    for(int i=1,j=a;i<=t;i++,j=j*a%mod)if(mp.count(j))return i*t-mp[j];
	return -1;
}
int exgcd(int a,int b,int &x,int &y)
{
	if(!b){x=1,y=0;return a;}
	int d=exgcd(b,a%b,x,y);
	int z=x;x=y,y=z-(a/b)*y;
	return d;
}
signed main()
{
	scanf("%lld%lld%lld",&mod,&K,&a);
	if(!a){puts("1");puts("0");return 0;}
	g=find(mod);t=BSGS(g,a,mod);
	//cerr<<"test::"<<g<<' '<<t<<endl;
	d=exgcd(K,mod-1,x,y);
	if(t%d){puts("0");return 0;}
	int P=(mod-1)/d;
	x=(x*(t/d)%P+P)%P;
	while(x<mod)
	{
		ans.push_back(power(g,x,mod));
		x+=P;
	}
	sort(ans.begin(),ans.end());
	printf("%lld\n",ans.size());
	for(int i=0;i<ans.size();i++)printf("%lld\n",ans[i]);
	return 0;
}
posted @   nofind  阅读(691)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示