Prime
我们说,如果存在一个整数
显然大于
素数计数函数:小于或等于
素数判定¶
我们自然地会想到,如何用计算机来判断一个数是不是素数呢?
暴力做法¶
自然可以枚举从小到大的每个数看是否能整除
bool isPrime(a) {
if (a < 2) return 0;
for (int i = 2; i < a; ++i)
if (a % i == 0) return 0;
return 1;
}
这样做是十分稳妥了,但是真的有必要每个数都去判断吗?
很容易发现这样一个事实:如果
这个结论告诉我们,对于每一对
由于
bool isPrime(a) {
if (a < 2) return 0;
for (int i = 2; i * i <= a; ++i)
if (a % i == 0) return 0;
return 1;
}
Miller-Rabin 素性测试¶
Miller-Rabin 素性测试(Miller–Rabin primality test)是进阶的素数判定方法。 对数 n 进行 k 轮测试的时间复杂度是
Fermat 素性测试¶
我们可以根据 费马小定理 得出一种检验素数的思路:
它的基本思想是不断地选取在
bool millerRabin(int n) {
if (n < 3) return n == 2;
// test_time 为测试次数,建议设为不小于 8
// 的整数以保证正确率,但也不宜过大,否则会影响效率
for (int i = 1; i <= test_time; ++i) {
int a = rand() % (n - 2) + 2;
if (quickPow(a, n - 1, n) != 1) return 0;
}
return 1;
}
很遗憾,费马小定理的逆定理并不成立,换言之,满足了
卡迈克尔数¶
上面的做法中随机地选择
对于合数
比如,
而且我们知道,若
二次探测定理¶
如果
要证明该定理,只需将上面的方程移项,再使用平方差公式,得到
实现¶
根据卡迈克尔数的性质,可知其一定不是
不妨将费马小定理和二次探测定理结合起来使用:
将
比较正确的 Miller Rabin:(来自 fjzzq2002)
bool millerRabbin(int n) {
if (n < 3) return n == 2;
int a = n - 1, b = 0;
while (a % 2 == 0) a /= 2, ++b;
// test_time 为测试次数,建议设为不小于 8
// 的整数以保证正确率,但也不宜过大,否则会影响效率
for (int i = 1, j; i <= test_time; ++i) {
int x = rand() % (n - 2) + 2, v = quickPow(x, a, n);
if (v == 1 || v == n - 1) continue;
for (j = 0; j < b; ++j) {
v = (long long)v * v % n;
if (v == n - 1) break;
}
if (j >= b) return 0;
}
return 1;
}
参考¶
http://www.matrix67.com/blog/archives/234
https://blog.bill.moe/miller-rabin-notes/
反素数¶
定义¶
如果某个正整数
注:注意区分 emirp,它是用来表示从后向前写读是素数的数。
简介¶
(本段转载自 桃酱的算法笔记,原文戳 链接,已获得作者授权)
其实顾名思义,素数就是因子只有两个的数,那么反素数,就是因子最多的数(并且因子个数相同的时候值最小),所以反素数是相对于一个集合来说的。
我所理解的反素数定义就是,在一个集合中,因素最多并且值最小的数,就是反素数。
那么,如何来求解反素数呢?
首先,既然要求因子数,我首先想到的就是素因子分解。把
但是显然质因子分解的复杂度是很高的,并且前一个数的结果不能被后面利用。所以要换个方法。
我们来观察一下反素数的特点。
-
反素数肯定是从
2 -
数值小的素数的幂次大于等于数值大的素数,即
n=p_{1}^{k_{1}}p_{2}^{k_{2}} \cdots p_{n}^{k_{n}} k_1 \geq k_2 \geq k_3 \geq \cdots \geq k_n
解释:
-
如果不是从
2 n 2 n -
如果数值小的素数的幂次小于数值大的素数的幂,那么如果把这两个素数交换位置(幂次不变),那么所得的
n n
另外还有两个问题,
-
对于给定的
n 最极端的情况大不了就是
n=p_{1}p_{2} \cdots p_{n} n -
我们要枚举到多少次幂呢?
我们考虑一个极端情况,当我们最小的素数的某个幂次已经比所给的
n
细节有了,那么我们具体如何具体实现呢?
我们可以把当前走到每一个素数前面的时候列举成一棵树的根节点,然后一层层的去找。找到什么时候停止呢?
-
当前走到的数字已经大于我们想要的数字了
-
当前枚举的因子已经用不到了(和
1 -
当前因子大于我们想要的因子了
-
当前因子正好是我们想要的因子(此时判断是否需要更新最小
ans
然后 dfs 里面不断一层一层枚举次数继续往下迭代就好啦~~
常见题型¶
求因子数一定的最小数¶
题目链接:https://codeforces.com/problemset/problem/27/E
对于这种题,我们只要以因子数为 dfs 的返回条件基准,不断更新找到的最小值就可以了
上代码:
#include <stdio.h>
#define ULL unsigned long long
#define INF ~0ULL
ULL p[16] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53};
ULL ans;
ULL n;
// depth: 当前在枚举第几个素数。num: 当前因子数。
// temp: 当前因子数量为 num
// 的时候的数值。up:上一个素数的幂,这次应该小于等于这个幂次嘛
void dfs(ULL depth, ULL temp, ULL num, ULL up) {
if (num > n || depth >= 16) return;
if (num == n && ans > temp) {
ans = temp;
return;
}
for (int i = 1; i <= up; i++) {
if (temp / p[depth] > ans) break;
dfs(depth + 1, temp = temp * p[depth], num * (i + 1), i);
}
}
int main() {
while (scanf("%llu", &n) != EOF) {
ans = INF;
dfs(0, 1, 1, 64);
printf("%llu\n", ans);
}
return 0;
}
求 n 以内因子数最多的数¶
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1562
思路同上,只不过要改改 dfs 的返回条件。注意这样的题目的数据范围,我一开始用了 int,应该是溢出了,在循环里可能就出不来了就超时了。上代码,0ms 过。注释就没必要写了上面写的很清楚了。
#include <cstdio>
#include <iostream>
#define ULL unsigned long long
int p[16] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53};
ULL n;
ULL ans, ans_num; // ans 为 n 以内的最大反素数(会持续更新),ans_sum 为 ans
// 的因子数。
void dfs(int depth, ULL temp, ULL num, int up) {
if (depth >= 16 || temp > n) return;
if (num > ans_num) {
ans = temp;
ans_num = num;
}
if (num == ans_num && ans > temp) ans = temp;
for (int i = 1; i <= up; i++) {
if (temp * p[depth] > n) break;
dfs(depth + 1, temp *= p[depth], num * (i + 1), i);
}
return;
}
int main() {
while (scanf("%llu", &n) != EOF) {
ans_num = 0;
dfs(0, 1, 1, 60);
printf("%llu\n", ans);
}
return 0;
}
buildLast update and/or translate time of this article,Check the history
editFound smelly bugs? Translation outdated? Wanna contribute with us? Edit this Page on Github
peopleContributor of this article OI-wiki
translateTranslator of this article Visit the original article!
copyrightThe article is available under CC BY-SA 4.0 & SATA ; additional terms may apply.