数学问题的解决窍门—素数判定

Posted 豆子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数学问题的解决窍门—素数判定相关的知识,希望对你有一定的参考价值。

数学问题的解决窍门

  • 素数判定

所谓素数: 指恰好有2个约数的整数。

判定: 因为n的约数都不超过n, 所以只要检查 2 ~ n-1 的所有整数是否整除n就能判定n是不是素数。

  1. 在此,如果d 是 n的约数, 那么 n/d也是n的约数。由n = d * n / d 可知 min(d, n/d) <= 根号n? , 所以只要检查 2 ~ ? 根号n 的所有整数就足够了。

  2. 同理可知,整数分解和约数枚举都可以在 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;
} 
?
int main()
{
    //记住这个就差不多了
    cout << "素性测试: " << is_prime(7) << endl;
    cout << "==================================================\n";
    
    cout << "约数枚举(约数总数为2,则为素数):\n";
    vector<int> res = divisor(13);
//  for (const auto& e : res) {
//      cout << e << " ";
//  }   
    cout << (res.size() == 2) << endl;
    cout << "==================================================\n";
    
    cout << "整数分解(他没有被任何数分解:\n";
    int n = 19;
    map<int, int> res2 = prime_factor(n);
//  for (auto it = res2.begin(); it != res2.end(); ++it)
//  {
//      cout << it->first << " " << it->second << "   ";
//      cout << endl;
//  }
    cout << (res2[n] == 1) << endl;
?
    return 0;
}
  • 诶氏筛法

如果只对一个整数进行素性测试, 通常 ? 的算法就够了,如果要对许多整数进行素性测试,则有更高效的算法。

素数的个数

给定整数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;
}
  • 练习题

  • POJ 3126: Prime Path

  • POJ 3421: X-factor Chains

  • POJ 3292: Semi-prime HB-numbers

 

以上是关于数学问题的解决窍门—素数判定的主要内容,如果未能解决你的问题,请参考以下文章

讲丶数学

第四章 数学知识

hdu 2012 素数判定

素数判定Miller_Rabin 算法详解

素数序列的生成及其应用(采用了自研的高效算法)

蒙特卡罗算法之素数测试