突然闲的没事就想起来这个玄学的素数测试法了,,试一下吧
时间复杂度:玄学
空间复杂度:大概玄学
准确度:玄学,据说(3/4)^s (s为尝试次数)
准备知识:
费马小定理:
对于素数p和任意整数a,有 ap≡a(mod p) 。反过来,满足 ap≡a(mod p) ,p也几乎是素数。
伪素数:
如果n是一个正整数,如果存在和n互素的正整数a满足 an−1≡1(mod n) ,我们说n是基于a的伪素数。如果一个数是伪素数,那么它几乎肯定是素数。
算法思想:
不断选取不超过n-1的基b(s次),计算是否每次都有 bn−1≡1(mod n) ,若每次都成立则n是素数,否则为合数。
核心代码:
主函数
Function Miller-Rabin (n : longint) :boolean;
begin
for i := 1 to s do
begin
a := random(n - 2) + 2;
if mod_exp(a, n-1, n) <> 1 then return false;
end;
return true;
end;
其中的mod_exp 为快速幂取模:
bool qpow(long long x, long long v, long long mod) {
long long ans = 1;
while (v > 0) {
if (v & 1) ans = (ans * x) % mod;
x = (x * x) % mod;
v >>= 1;
}
if (((ans % mod) + mod) % mod == 1) return 1;
return 0;
}
题目全部代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
int n;
long long a;
int ans = 0;
bool qpow(long long x, long long v, long long mod) {
long long ans = 1;
while (v > 0) {
if (v & 1) ans = (ans * x) % mod;
x = (x * x) % mod;
v >>= 1;
}
if (((ans % mod) + mod) % mod == 1) return 1;
return 0;
}
int main () {
srand(154481);
while(scanf("%d", &n) == 1) {
ans = 0;
for (int i = 1; i <= n; i++) {
scanf("%lld", &a);
if (a == 2) {
ans++;
continue;
}
bool f = 1;
for (int j = 1; j <= 10; j++) {
int x = rand() % (a - 2) + 2;
if (qpow(x, a-1, a) == 0) {
f = 0;
break;
}
}
if (f) ans++;
}
printf("%d\n", ans);
}
return 0;
}