其他分享
首页 > 其他分享> > Fansblog(Miller_Rabin, 费马小定理, 逆元, 威尔逊定理)

Fansblog(Miller_Rabin, 费马小定理, 逆元, 威尔逊定理)

作者:互联网

题面

给你一个素数p,让你求 k!%p, 其中k为比p小的整数里最大的素数。例如p=5,则k=3。p=11,则k=7。 k! = k*(k-1)*······21;
Input
第一行包含一个整数 T(1<=T<=10) 表示测试样例的个数.
接下来有T行,每行包含一个素数 p (1e9≤p≤1e14)
Output
对于每个测试样例,输出一个整数k!%p,代表答案
Sample Input
1
1000000007
Sample Output
328400734

思路: 威尔逊定理 (p - 1) ! % p = p - 1
显然 k! * (k + 1) ~ * (p - 1) % p = p - 1 % p
k! % p = 1/((k + 1) ~*(p - 1)) %p
我们只需找到1/(k + 1) , ~ 1 / (p - 1) 的逆元
由费马小定理可得
x^(p - 1) % p = 1 % p;
x * x^(p - 2) % p = 1 % p;
则 x和x^(p - 2)互为乘法逆元
eg a * b % p = a * b * b ^ (p - 1) % p = a * b ^ p % p
a * b ^(-1) % p = a * b ^(-1) * b ^(p - 1) % p = a * b ^ (p - 2) % p
Miller_Rabin
对于一个大数我们可以用两种方法来判断是否为质数
取随机数 n 来判断 p 是否为质数
1, 费马小定理
如果p为质数则 至少满足 n^(p - 1) % p = 1 % p;
如果不满足则p一定不是质数,满足则可能是质数。
但正确率可能较低所以要二次探测
2.二次检测定理
如果p是一个素数,则x^2%p==1的解为,则x=1或者x=p-1。
证明: x ^ 2 - 1 % p == 0
(x - 1) *(x + 1) %p == 0
则 x == 1 || x == p - 1

更多细节见于代码

#include <bits/stdc++.h>

using namespace std;
#define ll long long
const long long S = 20; // 探测次数
ll ksj(ll a,ll b,ll mod)//快速乘
{
	ll res=0;
	while(b)
  {
		if(b & 1) res = (res + a) % mod;
		a = (a + a) % mod;
		b >>= 1;
	}
	return res;
}
ll ksm(ll a,ll b,ll mod)//快速幂
{
	ll res=1;
	a %= mod;
	while(b)
  {
		if(b&1) res = ksj(res, a, mod);
		a = ksj(a, a, mod);
    b >>= 1;
	}
	return res;
}
int check(ll a, ll n, ll x, ll t){
    ll ret = ksm(a, x, n);//费马小定理 a^(p-1)%p==1
    ll last = ret;
    for(ll i = 1; i <= t; i++)
    {//二次检测定理 如果p是一个素数,则x^2%p==1的解为,则x=1或者x=n-1。
        ret = ksj(ret, ret, n);
        if(ret == 1 && last != 1 && last != n-1) return 1;
        last=ret;
    }
    if(ret != 1) return 1;
    return 0;
}
int Miller_Rabin(ll n){
    if(n < 2)return 0;
    if(n == 2)return 1;
    if((n&1) == 0) return 0;
    ll x = n - 1;
    ll t = 0;
    while((x&1) == 0){ x >>= 1; t++;}
    for(ll i = 0; i < S; i++){
        ll a = rand() % (n-1) + 1;
        if(check(a, n, x, t))  return 0;
    }
    return 1;
}

int main()
{
    int t;
    scanf("%d", &t);
    while(t --)
    {
        ll p, n, q;
        scanf("%lld", &n);
        p = n - 1;
        while(!Miller_Rabin(p)) p --;
        ll ans = 1;
        for(ll i = p + 1; i < n - 1; i++)
        {
            ans = ksj(ans, ksm(i, n - 2, n), n);
        }
        printf("%lld\n", ans);
    }
    return 0;
}

标签:return,Miller,定理,Fansblog,ret,res,ll,mod
来源: https://blog.csdn.net/qq_45778406/article/details/113835733