Time Limit: 5000MS | Memory Limit: 65536K | |
Total Submissions: 4443 | Accepted: 2028 |
Description
BL == N (mod P)
Input
Output
Sample Input
5 2 1 5 2 2 5 2 3 5 2 4 5 3 1 5 3 2 5 3 3 5 3 4 5 4 1 5 4 2 5 4 3 5 4 4 12345701 2 1111111 1111111121 65537 1111111111
Sample Output
0 1 3 2 0 3 1 2 0 no solution no solution 1 9584351 462803587
Hint
B(P-1) == 1 (mod P)for any prime P and some other (fairly rare) numbers known as base-B pseudoprimes. A rarer subset of the base-B pseudoprimes, known as Carmichael numbers, are pseudoprimes for every base between 2 and P-1. A corollary to Fermat’s theorem is that for any m
B(-m) == B(P-1-m) (mod P) .
求解离散对数,B^L≡N(mod P). L的最小值。
本人博客
这应该是最基础的求解离散对数。利用求解离散对数的经典算法:BSGS算法(Baby-Step Giant-Step 算法)此题给的P是素数,所以不用扩展BSGS算法,使用原始算法就可以了。最好有一定的预备知识:扩展欧几里得、求逆元、欧拉函数、费马小定理,有一定的知识可以更快的掌握这种算法。
这里就谈谈我对求解BSGS算法的理解,欢迎指出不足!学习的时候在网上找了很久都没有详细的讲解,所以看了很长时间才看懂这个算法。PS:主要还是本人比较弱。。。
B(P-1) ≡ 1 (mod P)这里因为P比较大,并且我们需要求的L可能也比较大,普通暴力算法肯定不行。我们可以考虑优化一下。利用欧拉定理我们知道:B^L mod P = B^(L% φ(P)) mod P. 所以,我们可以考虑不用枚举所有的值,L的值最多是在φ(P)之内,不过,因为本题给的数P是素数,所以φ(P)=P-1,如果只有这个优化肯定还是不可以。所以我们得想到继续优化。我们可以考虑把,L这个值换算一下:L=i*M+r. M=ceil(√P) .
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
using namespace std;
const int MAXN = 1000000;
typedef long long LL;
bool hash[MAXN];
LL p[MAXN];
LL var[MAXN];
//hash算法-除留余数法
void insertx(LL n,LL v)
{
int x=v%MAXN;
while(hash[x]&&var[x]!=v){//这一步是筛重
x++;
if(x==MAXN)
x=0;
}
if(!hash[x]){
var[x]=v;//记录v,即B^j的值
hash[x]=true;
p[x]=n; //记录值B^j中j的值
}
}
LL find_x(LL v)
{
int x=v%MAXN;
while(hash[x]&&var[x]!=v){ //确保出来的值是B^j对照的j值
x++;
if(x==MAXN)
x=0;
}
if(hash[x]==false)
return -1;
return p[x];
}
LL exgcd(LL a,LL b,LL &x,LL &y)
{
if(b==0){
x=1;
y=0;
return a; //返回公约数
}
LL r=exgcd(b,a%b,x,y);
LL t=x;
x=y;
y=t-a/b*y;
return r;
}
//离散对数:A^x≡B(mod P) 求x。
LL BSGS(LL A,LL B,LL P)
{
LL num=1;
int M=ceil(sqrt(P*1.0));
for(int i=0;i<M;i++){
insertx(i,num);
num=num*A%P;
}
LL res=1;
for(int i=0;i<M;i++){
LL x,y;
LL r=exgcd(res,P,x,y);//求逆元
x=x*B;
x=(x%(P/r)+(P/r))%(P/r);//取x的最小正值。r可省略,因为r=1.
LL ans=find_x(x);
if(ans!=-1){
return (ans+i*M);
}
res=res*num%P;
}
return -1;
}
int main()
{
LL N,B,P;
while(scanf("%lld%lld%lld",&P,&B,&N)!=EOF){
memset(hash,0,sizeof(hash));
memset(var,-1,sizeof(var));
memset(p,-1,sizeof(p));
LL ans=BSGS(B,N,P);
if(ans==-1) printf("no solution\n");
else printf("%lld\n",ans);
}
return 0;
}