Discrete Logging
Time Limit: 5000MS | Memory Limit: 65536K | |
Total Submissions: 3696 | Accepted: 1727 |
Description
Given a prime P, 2 <= P < 231, an integer B, 2 <= B < P, and an integer N, 1 <= N < P, compute the discrete logarithm of N, base B, modulo P. That is, find an integer L such that
BL
== N (mod P)
Input
Read several lines of input, each containing P,B,N separated by a space.
Output
For each line print the logarithm on a separate line. If there are several, print the smallest; if there is none, print "no solution".
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
The solution to this problem requires a well known result in number theory that is probably expected of you for Putnam but not ACM competitions.
It is Fermat's theorem that states
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
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 )
题意要求解一个 k^D = n ( mod p ) 的一个最小D
一开始也不会解 。
在网上看了一下,原来是用到一个 Baby step giant step 的算法 。
先要把 D 分解 , D = i * m + j ( m = ceil( sqrt (p - 1 ) ) )
原式 : k^D = n ( mod p )
-> k^i^m * k^j = n (mod p )
-> k^j = n * ( k ^(-m)^ i ) ( mod p )
根据题目给的 Hint ( 费马小定理 )可以求出 k^m 的逆元 .
然后枚举 i , 查找是否存在 k^j 与 n * ( k ^(-m)^i ) 相等
所以预处理 k^j ( mod p ) 排序以后 , 就可以进行二分查找了 (复杂度降为log(m))。
加上枚举 , 那么总复杂度就是 m*log(m) .
#include <iostream> #include <cstdio> #include <string> #include <cstring> #include <algorithm> #include <cmath> using namespace std; typedef long long LL ; const int N = 100010; struct B { LL num , id ; bool operator < ( const B &a ) const{ if( num != a.num ) return num < a.num; else return id < a.id ; } }baby[N]; LL n , k , p ; int tot ; LL quick_mod( LL a , LL b ,LL mod ) { LL res =1 ; while( b ) { if( b &1 ) res = res * a % mod ; a = a * a % mod ; b >>= 1 ; } return res ; } int find( LL n ) { int l = 0 , r = tot - 1 ; while( l <= r ){ int m = (l + r) >> 1; if( baby[m].num == n){ return baby[m].id; } else if( baby[m].num < n ) l = m + 1; else r = m - 1 ; } return -1; } void run() { int m = (int)ceil(sqrt((double)(p-1))); baby[0].num = 1 , baby[0].id = 0; for( int i = 1 ; i < m ; ++i ){ baby[i].id = i ; baby[i].num = baby[i-1].num * k % p ; // k^j } sort( baby , baby + m ); tot = 1 ; for( int i = 1 ; i < m ; ++i ){ if( baby[tot-1].num != baby[i].num ) baby[tot++] = baby[i]; } LL bm = quick_mod( k , p - 1 - m , p ) ; // k^(-m) ; LL temp = n ; for( int j = 0 ; j < m ; ++j ){ // k^(-m)^j int pos = find( temp ); if( pos != -1 ){ printf("%d " , ( m * j + pos ) ); return ; } temp = temp * bm % p ; } puts("no solution"); } int main() { #ifdef LOCAL freopen("in.txt","r",stdin); #endif // LOCAL while( ~scanf("%lld%lld%lld",&p,&k,&n) ) run(); }