原根&离散对数
一.原根
1.定义:
(a与m互质)使成立的最小的d(记住原根是a,不是d!)
2.原根的性质:一般给出p(有时叫m)
1.具有原根的数字仅有以下几种形式:,(p是奇质数)
2.一个数的最小原根的大小不超过
3.原根个数Φ(Φ(m))个,m为质数则原根个数Φ(m-1)
3.求解原根的基本步骤:
- 判断一个数是否有原根。(通过性质1,枚举质数即可)
- 求得最小原根。(通过性质2,依次枚举2~判断即可)
- 求出所有原根。(通过性质3,枚举次数d即可)
- 简化版:
poj 1284 性质3质数的原根个数
原根可以把求指数问题转化为求对数问题,这样实现乘到加的转换,复杂度大大降低
2.离散对数
1.定义
普通对数: 记做
离散对数就是把这个放在了模意义下,即求解方程: a^x≡b(mod p)
2.求解方法(扩展版BGBS 大步小步算法) 参考代码
这个模板用的二分,复杂度多个logn,有些模板用map或hash,hash可以根号n解决
#include<cmath>
#include<cstdio>
#include<vector>
#include<string>
#include<cstdlib>
#include<utility>
#include<cstring>
#include<iostream>
#include<algorithm>
#define Inf (1<<29)
#define LL long long
using namespace std;
const int MM=110;
const long long MOD=1000000007;
///扩展大步小步算法
struct baby///小步算法预存的结构体定义
{
long long b,j;
bool operator < (const baby &other)const{
if(b==other.b)return j<other.j;
return b<other.b;
}
}babyv[100005];
long long extended_euclid(long long a,long long b,long long &x,long long &y)
{
long long d;
if(b == 0) {x = 1; y = 0; return a;}
d = extended_euclid(b, a % b, y, x);
y -= a / b * x;
return d;
}
long long Inv(long long a,long long n)
{
long long d, x, y;
d = extended_euclid(a, n, x, y);
if(d == 1) return (x%n + n) % n;
else return -1; // no solution
}
long long gcd(long long x,long long y)
{
if(y==0)return x;
return gcd(y,x%y);
}
///快速幂,x^y%mod
long long q_pow(long long x,long long y,long long mod)
{
long long ans=1;
while(y>0)
{
if(y&1)ans=(ans*x)%mod;
x=(x*x)%mod;
y>>=1;
}
return ans;
}
///小步预存的个数为m的结构体里面查找num
long long find(long long num,long long m)
{
long long l=0,r=m-1,ans=-1;
while(r>=l)
{
long long mid=(r+l)/2;
if(babyv[mid].b>=num){
if(babyv[mid].b==num)
ans=babyv[mid].j;
r=mid-1;
}
else l=mid+1;
}
return ans;
}
///A^x=B(modC)求解x,gcd(A,C)不一定等于1,无解返回-1
long long ex_babystep_giantstep(long long A,long long B,long long C)
{
for(long long i=0;i<=100;i++)///先在小于100的数里面测试
if(q_pow(A,i,C)==B%C)return i;
///消因子, 将A^x=B%C化为d*A^(x – n)=B%C
long long g,n=0,d=1;
while((g=gcd(A,C))!=1)
{
if(B%g)return -1;///无解
n++;
B/=g;
C/=g;
d=(d*A/g)%C;
}
///无扩展小步大步操作
long long m=ceil(sqrt(C)),ans=1;
for(long long j=0;j<m;j++)///预存操作
{
babyv[j].b=ans;
babyv[j].j=j;
ans=(ans*A)%C;///递推
}
ans=1;
sort(babyv,babyv+m);///预存排序
long long Bm=q_pow(Inv(A,C),m,C);///算出A^(-m)%C的值
for(long long i=0;i<m;i++)
{
long long j=find(Inv(d,C)*B%C*ans%C,m);///二分查找
if(j!=-1)return i*m+j+n;///找到返回答案
ans=(ans*Bm)%C;///继续递推
}
return -1;///找不到答案
}
int main()
{
long long X,Z,K;
//freopen("D:\\o.txt","r",stdin);
while(~scanf("%lld%lld%lld",&X,&Z,&K))
{
if(X==0&&Z==0&&K==0)break;
long long ans=ex_babystep_giantstep(X,K,Z);//x y C
// if(ans==-1)puts("No Solution");
// else printf("%lld\n",ans);
}
return 0;
}