一类问题: 判定一个整数n(n>1)是否为素数。
算法一:
直接根据素数的定义枚举从到,如果n%i==0n为合数。
时间复杂度:O(n)
int is_prime(int n)
{
int i;
for(i = 2; i < n; i++)
if(n % i == 0) return 0;
return 1;
}
算法二:
发现若存在使得
n%i==0
,则必有n%(n/i)==0
。
所以只需枚举从到即可。
时间复杂度:
int is_prime(int n)
{
int i;
for(i = 2; i * i <= n; i++) if(n % i == 0)
return 0;
return 1;
}
如果要找到成1~n的所有素数那么这个时间代价就变为O(n^2),很多时候是不可接受的。
所以随着学习的深入,我们了解到了素数筛法,即从2开始,2的倍数肯定不是素数,再向右扫描,如果扫描到素数,则重复之前的过程,剔除之后的部分合数(准确的说是关于当前质数的倍数),如果扫描到合数则跳过(表示前面已经更新过这个数不是素数)。然后都扫描一遍即可把1~n的素数求解出来。这个算法的复杂度略高于O(n)。素数筛代码如下:
#include<iostream>
#include<cstring>
using namespace std;
const int MAXN = 500000;
bool isprime[MAXN];
int prime[MAXN];
int cnt = 0;//保存素数个数
void getPrime()
{
for(int i = 1; i < MAXN; i++)
isprime[i] = true; //先假设所有数是素数,后面逐个扫描更新
for(int i = 2; i < MAXN; i++) //扫一遍
{
if(!isprime[i]) continue; //如果不是素数,则不往后面更新
prime[cnt++] = i;
for(int j = 2 * i; j < MAXN; j += i)
isprime[j] = false;
}
}
int main()
{
getPrime();
for(int i = 0; i < cnt; i++)
cout << prime[i] << endl;
}
但是这个算法的弊端在于,为了判断一个大数是否是素数必须从从头开始扫描,而且空间代价也受不了,故不能离散的判断。
看完了上面的引理,那就可以正式开始Miller-Rabin算法的讲解了。
背景:
素性测试(即测试给定的数是否为素数)是近代密码学中的一个非常重要的课题。虽然Wilson定理(对于给定的正整数n,n是素数的充要条件为)给出了一个数是素数的充要条件,但根据它来素性测试所需的计算量太大,无法实现对较大整数的测试。目前,尽管高效的确定性的素性算法尚未找到,但已有一些随机算法可用于素性测试及大整数的因数分解。下面描述的Miller-Rabin素性测试算法就是一个这样的算法。
算法:
首先要知道费马定理只是n是素数的必要条件。即费马定理不成立,n一定是合数;费马定理成立,n可能是素数。接下来请看Miller-Rabin算法的分析过程。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll multi(ll a,ll b,ll p)
{
ll temp=0;
while (b)
{
if (b&(ll)1) temp=(temp+a)%p;
a=(a<<1)%p;
b>>=1;
}
return temp;
}
ll FastPower(ll a, ll p, ll k)
{
ll ans = 1;
while (p)
{
if (p & 1) ans = multi(ans,a,k);
a = multi(a,a,k);
p >>= 1;
}
return ans;
}
bool judge(ll a, ll n, ll m, ll r)
{
ll x = FastPower(a, m, n);
if (x == 1 || x == n - 1) return true;
while (r--)
{
x = multi(x,x,n);
if (x == n - 1) return true;
}
return false;
}
bool Miller_Rabin(ll n)
{
if (n == 2) return true;
if (n == 1 || !(n & 1)) return false;
ll m = n - 1, r = 0;
while (!(m & 1))
{
r++;
m >>= 1;
}
for (int i = 0; i < 10; i++)
{
int a = rand() % (n - 1) + 1;
if (!judge(a, n, m, r))
return false;
}
return true;
}
int main ()
{
ll n;
while (scanf ("%lld",&n)!=EOF)
{
if (Miller_Rabin(n)) printf ("%lld is a prime\n",n);
else printf ("%lld is not a prime\n",n);
}
return 0;
}
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int num[12]= {0,2,3,5,7,11,13,17,19,23,29};
ll t,ans;
ll n;
ll ksc(ll a,ll b,ll p)
{
ll ans=0;
while(b)
{
if(b&1) ans=(ans+a)%p;
b>>=1;
a=(a+a)%p;
}
return ans;
}
ll ksm(ll a,ll b,ll p)
{
ll ans=1;
while(b)
{
if(b&1) ans=ksc(ans,a,p);
a=ksc(a,a,p);
b>>=1;
}
return ans;
}
bool Miller_Rabin(ll n)
{
ll u=n-1,t=0;
if(n==2) return 1;
if(!(n&1) || n==1) return 0;
for(; !(u&1); u>>=1,t++); //n-1=2^t*u
for(ll i=1; i<=10; i++)
{
if(num[i]==n) continue;
ll a=num[i],x=ksm(a,u,n);
for(ll i=1; i<=t; i++)
{
ll y=ksc(x,x,n);
if(y==1 && x!=1 && x!=n-1) return 0;
x=y;
}
if(x!=1) return 0;
}
return 1;
}
int main ()
{
ll n;
while (scanf ("%lld",&n)!=EOF)
{
if (Miller_Rabin(n)) printf ("%lld is a prime\n",n);
else printf ("%lld is not a prime\n",n);
}
return 0;
}