素数广泛应用于密码学中,因而也有很多相关算法。不过程序设计竞赛涉及的主要是埃氏筛法、简单的素性测试和整数分解这类算法。
1.素数判定
所谓素数: 指恰好有2个约数的整数。
1.判定: 因为n的约数都不超过n, 所以只要检查 2 ~ n-1 的所有整数是否整除n就能判定n是不是素数。
2.在此,如果d 是 n的约数, 那么 n/d也是n的约数。由n = d * n / d 可知 min(d, n/d) <= 根号n , 所以只要检查 2 ~ 根号n 的所有整数就足够了。
3.同理可知,整数分解和约数枚举都可以在 O(根号n) 时间完成。(还有更高效的算法)
#include <iostream>
#include <map>
#include <vector>
using namespace std;
//假设输入的都是正数
//素性测试
bool is_prime(int n)
{
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) return false;
}
return n != 1; //1是例外
}
//约数枚举
vector<int> divisor(int n)
{
vector<int> res;
for (int i = 1; i * i <= n; i++)
{
if (n % i == 0) {
res.push_back(i);
if (i != n / i) {
res.push_back(n / i);
}
}
}
return res;
}
//整数分解
map<int, int> prime_factor(int n)
{
map<int, int> res;
for (int i = 2; i * i <= n; i++)
{
while (n % i == 0)
{
++res[i];
n /= i;
}
}
if (n != 1) {
res[n] = 1;
}
return res;
}
2.埃氏筛法
如果只对一个整数进行素性测试, 通常 O(根号n) 的算法就够了,如果要对许多整数进行素性测试,则有更高效的算法
素数的个数
给定整数n, 请问n以内有多少个素数?
限制条件:
. n <= 10^6
素数的个数
#include <iostream>
using namespace std;
const int maxn = 10000000;
int prime[maxn]; //第i个素数
bool is_prime[maxn + 1]; //is_prime[i]为true 表示i是素数
//返回n以内的素数个数
int sieve(int n)
{
int p = 0; //统计素数的个数
//初始化
for (int i = 0; i <= n; i++) {
is_prime[i] = true;
}
is_prime[0] = is_prime[1] = false;
for (int i = 0; i <= n; i++) {
if (is_prime[i]) {
prime[p++] = i; //将素数添加到prime中
//1.首先2是素数, 然后划去所有2的倍数
//2.表中剩余的最小数字是3, 它不能被更小的数整除, 所以是素数。再将表中所有3的倍数都划去
//3.依次类推, 如果表中剩余的最小数字是m时, m就是素数。然后将表中所有m的倍数都划去。像这样,就能依次枚举n以内的素数。
for (int j = 2 * i; j <= n; j += i) {
is_prime[j] = false;
}
}
}
return p;
}
int main()
{
int cnt = sieve(30);
for (int i = 0; i < cnt; i++)
{
cout << prime[i] << " ";
}
cout << endl;
return 0;
}
用埃氏素数解决蓝桥杯题
第八届蓝桥杯第二题:
标题:等差素数列
2,3,5,7,11,13,....是素数序列。
类似:7,37,67,97,127,157 这样完全由素数组成的等差数列,叫等差素数数列。
上边的数列公差为30,长度为6。
2004年,格林与华人陶哲轩合作证明了:存在任意长度的素数等差数列。
这是数论领域一项惊人的成果!
有这一理论为基础,请你借助手中的计算机,满怀信心地搜索:
长度为10的等差素数列,其公差最小值是多少?
注意:需要提交的是一个整数,不要填写任何多余的内容和说明文字。
#include <iostream>
using namespace std;
const int maxn = 10000000;
int prime[maxn]; //第i个素数
bool is_prime[maxn + 1]; //is_prime[i]为true 表示i是素数
int sieve(int n);
bool isPrime(int n);
void solve();
//返回n以内的素数个数
int sieve(int n)
{
int p = 0; //统计素数的个数
//初始化
for (int i = 0; i <= n; i++) {
is_prime[i] = true;
}
is_prime[0] = is_prime[1] = false;
for (int i = 0; i <= n; i++) {
if (is_prime[i]) {
prime[p++] = i; //将素数添加到prime中
//1.首先2是素数, 然后划去所有2的倍数
//2.表中剩余的最小数字是3, 它不能被更小的数整除, 所以是素数。再将表中所有3的倍数都划去
//3.依次类推, 如果表中剩余的最小数字是m时, m就是素数。然后将表中所有m的倍数都划去。像这样,就能依次枚举n以内的素数。
for (int j = 2 * i; j <= n; j += i) {
is_prime[j] = false;
}
}
}
return p;
}
//素性测试
bool isPrime(int n)
{
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) return false;
}
return n != 1; //1是例外
}
void solve()
{
int N = 10000;
int cnt = sieve(N);
// cout << cnt << endl;
for (int i = 1; i <= N; i++) //枚举 1~1000的公差
{
for (int j = 0; j < cnt; j++)
{
int flag = 1, //标志
tmp = prime[j]; //第j个素数
for (int k = 1; k < 10; k++) //第一个数已经确定是素数
{
if (tmp + i > N || !isPrime(tmp + i)) {
flag = 0; break;
}
else {
tmp = tmp + i; //下一个数
}
}
//如果 连续9个公差为i的数都是素数
if (flag) {
cout << i << " 开始的素数:" << prime[j] << endl;
return;
}
}
}
}
int main()
{
solve();
return 0;
}