本人学艺不精尤其这篇除了欧拉筛都是自学的可能有错,如果发现错误请指出呀!
啊啊啊啊啊啊我终于过了!!!!!本来今天想康康去年女赛的题来着结果莫名其妙跟这个东西磕了一下午,终于悟辣!那就浅说一下素数筛和素数测试吧。
欧拉筛的底层理念是:每个合数会被他最小的素因子筛掉。无论是不重性还是不漏性都可以用这句话理解,因为我们永远让大数乘小数而不会反过来,所以每个合数遇到自己的(从小数)第一个因子就会被筛掉,可保证不会漏也不会重。
emmmm可能我理解的还不够深,只能讲到这里了(不知道接下来该怎么证可以去自己查查或者意会一下)
这里重点嗦嗦米勒罗宾素数测试。
他是一个由费马小定理和二次探测定理(主要)结合的概率算法,就是说他只是一个概率,但是这个出错的概率大概比中彩票还要小得多……
结合代码康康(板子题1286)
(emmm发现同学直接sqrt暴力就可以过…啊这)
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int c = 20;
ll quick(ll a, ll b, ll p) {
ll s = 1;
while (b) {
if (b & 1)
s = s % p * a % p;
a = a % p * a % p;
b >>= 1;
}
return s;
}
bool Miller_Rabin(ll n) {
//c是测试次数
if (n == 2)
return 1;
if (n < 2 || !(n & 1))
return 0;
srand(time(NULL));
//srand函数种种子!
//rand是根据一个seed计算出来的(伪)随机数
//如果seed一样的话,每次算出来的随机数就是相同的
//time(NULL)返回的是从1900年1月1日到现在的时间秒数
//我们让seed等于这个时间,就可以保证运行时间不同产生的随机数是不同的
ll m = n - 1, k = 0;
while (!(m & 1)) {
k++;
m >>= 1;
}//求q(上图中的)
ll a, x, y;
for (int i = 1; i <= c; ++i) {
a = rand() % (n - 1) + 1;
x = quick(a, m, n);
//从a^m开始筛,先算到底
for (int j = 1; j <= k; ++j) {
//筛到a^(n-1),(即到a^(2^k)*m)
y = x % n * x % n;
//每次自乘其实就是2^(k+1)
if (y == 1 && x != 1 && x != n - 1)
return 0;
//验每一次二次探测的解,由于最后会判断y
//如果y!=1最后也会被判错所以无所谓
x = y;
}
if (y != 1)
return 0;
}
return 1;
}
int main() {
ll n;
while (cin >> n) {
int ans = 0;
while (n--) {
ll t;
cin >> t;
if (Miller_Rabin(t)) {
ans++;
}
}
cout << ans << '\n';
}
return 0;
}
这个板子是搬了一个题解的,我个人感觉他好像没有进行费马小定理的判断,如果加上大概还能再优化一下时间复杂度,但边边还没讲这个那就再说叭。
这里还有一个随机数的问题,也去浅学了一点哈哈,有关这题的写在注释里啦,还有一丢丢没关系的:由于这个随机数是由种子决定的,种子一样随机数就会一样。所以我们两次调用srand()函数设置随机数种子之间的时间间隔不可以超过1s,不然会导致我们在同一秒内重置随机数种子,从而等价于使用了一个固定的随机数种子。反映在代码里就是,设置种子不可以写到随机数的循环里。