HNU_10694
这个题目本质上就是去求模p的原根。
首先,我们用反证法证明这个结论:如果对于任意的正整数i,r^i%p包含了1~p-1所有整数的话,那么满足r^i%p=1这个等式的最小正整数为p-1。
如果上面的命题不成立,不妨设满足r^i %p=1这个等式的最小的正整数为j,假如j<p-1,那么对于任意r^i总能分解成若干r^j的与r^x(x<j)的积,所以r^i%p最多有x个不同的值,那么r^i%p不可能包含了1~p-1所有的整数。假如j>p-1,那么就说明当1<=x<=p-1时,不会出现a^x%p的值为1,那么就必然有x1、x2(1<=x1<x2<=p-1)使得r^x1%p=r^x2%p,也就是说r^(x2-x1)%p=1,而这已经在前面证明了是会推出矛盾的。
因此,如果对于任意的正整数i,r^i%p包含了1~p-1所有整数的话,那么满足r^i%p=1这个等式的最小正整数为p-1。
接着,我们以上面的结论为前提,即承认满足r^i%p=1这个等式的最小正整数为p-1,然后用反证法证明这个结论:对于任意满足1<=x<y<=p-1的x、y,都有r^x%p !=r^y%p成立。
假设r^x%p==r^y%p成立的话,那么就会得到r^(y-x)%p=1,这样就与假设相矛盾了。
因此,对于任意满足1<=x<y<=p-1的x、y,都有r^x%p !=r^y%p成立,也就是说当i从1开始取到p-1时r^i%p就产生了p-1个不同的正整数,进而就可说明对于任意正整数i,r^i%p包含了1~p-1所有整数。
有了上面两点证明之后,就能够说明“满足r^i%p=1这个等式的最小正整数为p-1”这一命题和“对于任意的正整数i,r^i%p包含了1~p-1所有整数”这个命题是等价的(前面两个证明相当于是必要性和充分性的证明)。
而由于p是素数,所以欧拉函数是p-1,这就说明了r就是模p的原根。
兜了这么一个大圈子,就是为了说明题目中描述的r就是模p的原根(虽然题目一开始就是用primitive root去描述r的,但这个描述并不是原根的定义)。
接下来就是去找模p的原根了,也就是去找满足当i<p时当且仅当i=p-1才有r^i%p=1成立的最小的r。所以,可以暴力枚举r,然后去看当i<p时是否只有i=p-1时才有r^i%p=1成立即可。
直接暴力固然可以,但是当p比较大的时候耗时就比较长了。一个比较好的优化就是运用一个定理:设p是奇素数,p-1的所有的不同的素数时q1,...,qs,那么,g是模p的原根的充要条件是g^((p-1)/qj)%p !=1,j=1,...,s。由于一个数的素因子数量很少,同时再利用快速幂取模去验证的话就会省不少时间。
//Program #1
#include<stdio.h>
#include<string.h>
int p;
int check(int r)
{
int i, ans = 1;
r %= p;
for(i = 1; i < p - 1; i ++)
{
ans = (ans * r) % p;
if(ans == 1)
return 0;
}
ans = (ans * r) % p;
return ans == 1;
}
void solve()
{
int r;
for(r = 1; ; r ++)
if(check(r))
{
printf("%d\n", r);
return ;
}
}
int main()
{
int t;
scanf("%d", &t);
while(t --)
{
scanf("%d", &p);
solve();
}
return 0;
}
//Program #2
#include<stdio.h>
#include<string.h>
#include<math.h>
#define MAXD 20010
int isprime[MAXD], prime[MAXD], p, N;
void prepare()
{
int i, j, k;
memset(isprime, -1, sizeof(isprime));
N = 0;
for(i = 2; i < 20000; i ++)
if(isprime[i])
{
prime[N ++] = i;
for(j = i * i; j < 20000; j += i)
isprime[j] = 0;
}
prime[N] = 20000;
}
int pow_mod(int a, int n)
{
int ans;
if(n == 1)
return a % p;
ans = pow_mod(a, n / 2);
ans = (ans * ans) % p;
return n % 2 ? (ans * a) % p : ans;
}
int check(int r)
{
int i, j, k = p - 1;
for(i = 0; prime[i] < p - 1; i ++)
if(k % prime[i] == 0 && pow_mod(r, k / prime[i]) == 1)
return 0;
return 1;
}
void solve()
{
int i, j, k;
for(i = 2; ; i ++)
if(check(i))
{
printf("%d\n", i);
return ;
}
}
int main()
{
int t;
prepare();
scanf("%d", &t);
while(t --)
{
scanf("%d", &p);
if(p == 2)
printf("1\n");
else
solve();
}
return 0;
}