【问题标题】:Is this a good Primality Checking Solution?这是一个好的素数检查解决方案吗?
【发布时间】:2015-10-13 16:09:50
【问题描述】:

我已经编写了这段代码来检查一个数字是否是素数(对于高达 10^9+7 的数字)

这是一个好方法吗??
这将是什么时间复杂度?

我所做的是我创建了一个unordered_set,它将质数存储到sqrt(n)
在检查一个数是否为素数时,首先检查它是否小于表中的最大数。
如果小于则在表中搜索,因此在这种情况下复杂度应为O(1)
如果更多,则对包含素数的数字集的数字进行可分性测试。

#include<iostream>
#include<set>
#include<math.h>
#include<unordered_set>
#define sqrt10e9 31623

using namespace std;

unordered_set<long long> primeSet = { 2, 3 }; //used for fast lookups

void genrate_prime_set(long range) //this generates prime number upto sqrt(10^9+7)
{
    bool flag;
    set<long long> tempPrimeSet = { 2, 3 }; //a temporay set is used for genration
    set<long long>::iterator j;
    for (int i = 3; i <= range; i = i + 2)
    {
        //cout << i << " ";
        flag = true;
        for (j = tempPrimeSet.begin(); *j * *j <= i; ++j)
        {
            if (i % (*j) == 0)
            {
                flag = false;
                break;
            }
        }
        if (flag)
        {
            primeSet.insert(i);
            tempPrimeSet.insert(i);
        }
    }
}

bool is_prime(long long i,unordered_set<long long> primeSet)
{
    bool flag = true;
    if(i <= sqrt10e9) //if number exist in the lookup table
        return primeSet.count(i);    

    //if it doesn't iterate through the table  

    for (unordered_set<long long>::iterator j = primeSet.begin(); j != primeSet.end(); ++j)
    {
        if (*j * *j <= i && i % (*j) == 0)
        {
            flag = false;
            break;
        }
    }
    return flag;
}
int main()
{
    //long long testCases, a, b, kiwiCount;
    bool primeFlag = true;
    //unordered_set<int> primeNum;
    genrate_prime_set(sqrt10e9);
    cout << primeSet.size()<<"\n";
    cout << is_prime(9999991,primeSet);
    return 0;
}

【问题讨论】:

    标签: performance optimization primes c++14


    【解决方案1】:

    这并没有让我觉得这是一种特别有效的方式来完成手头的工作。

    虽然最终可能不会产生太大的影响,但生成所有素数达到某个特定限制的有效方法显然是使用筛子——Eratosthenes 的筛子既简单又快速。有一些修改可以更快,但对于您正在处理的小尺寸,它们可能不值得。

    这些通常以比您当前使用的格式更有效的格式生成输出。特别是,您通常只为每个可能的素数(即每个奇数)分配一个位,如果该数字是合数,则将其归零,如果它是素数,则将其归零(当然,如果您愿意,可以反转含义)。

    由于从 3 到 31623 的每个奇数只需要一个位,因此这只需要大约 16 K 位,或大约 2K 字节——按照现代标准,这确实是微不足道的内存量(尤其是:小到足以容纳 L1很容易缓存)。

    由于位是按顺序存储的,因此计算和测试直到要测试的数字的平方根的因子也很简单,而不是针对表中的所有数字(包括大于平方的数字)进行测试您正在测试的数字的根,这显然是浪费时间)。这也优化了对内存的访问,以防其中一些不在缓存中(即,您可以按顺序访问所有数据,从而使硬件预取器的工作尽可能轻松)。

    如果您想进一步优化,我会考虑只使用筛子找到所有最高 109+7 的素数,然后查找输入。这是否是一场胜利将(很大程度上)取决于您可以预期收到的查询数量。快速检查表明,埃拉托色尼筛法的简单实现可以在大约 17 秒内找到高达 109 的所有素数。之后,每个查询(当然)本质上是瞬时的(即,单个内存读取的成本)。这确实需要大约 120 兆字节的内存来存储筛子的结果,这曾经是一个主要考虑因素,但(在相当有限的系统上除外)通常不再需要了。

    【讨论】:

    • 感谢您的帮助,先生。但是我仍然不知道如何使用位来实现筛子,请您详细说明一下,或者提供一些参考。
    • @ShinMigami13:一种简单的方法是使用std::vector&lt;bool&gt;One example.
    【解决方案2】:

    非常简短的回答:从“Miller-Rabin”这个词开始研究这个主题

    简短的回答是否定的:

    • 寻找一个数的因数是检查素数的不好方法
    • 彻底搜索素数是寻找因子的糟糕方法
      • 特别是如果您搜索每个素数,而不仅仅是小于或等于数字平方根的素数
    • 对它们中的每一个进行素数测试是生成素数列表的糟糕方法

    另外,如果确实需要作为参数,您应该通过 reference 获取primeSet 而不是复制。

    注意:测试小素数以查看它们是否能整除一个数是素数测试的有用第一步,但通常应仅用于最小素数,然后再切换到更好的素数方法

    【讨论】:

      【解决方案3】:

      不,这不是确定数字是否为素数的好方法。这是一个简单素性测试的伪代码,足以满足您范围内的数字;我把它留给你翻译成C++:

      function isPrime(n)
          d := 2
          while d * d <= n
              if n % d == 0
                  return False
              d := d + 1
          return True
      

      这是通过尝试每个可能的除数直到输入数字的平方根n;如果没有找到除数,则输入数不能是合数,形式为 n = p × q,因为其中之一两个除数 pq 必须小于 n 的平方根,而另一个大于 n 的平方根.

      有更好的方法来确定素数;例如,在最初检查数字是否为偶数(因此仅当 n = 2 时为素数)之后,只需要测试奇数的潜在除数,将所需的工作量减半。如果您有一个直到 n 的平方根的素数列表,您可以使用该列表作为试验除数并加快处理速度。对于更大的n,还有其他技术。

      但这应该足以让您入门。当您准备好更多时,请回到这里并提出更多问题。

      【讨论】:

        【解决方案4】:

        我只能建议一种在 Java 中使用库函数来检查数字素数的方法。至于其他问题,我没有答案。

        如果这个 BigInteger 可能是素数,则 java.math.BigInteger.isProbablePrime(int certainty) 返回 true,如果它肯定是复合的,则返回 false。如果确定性 ≤ 0,则返回 true。您应该尝试在您的代码中使用它。所以尝试用Java重写它

        参数

        确定性 - 调用者愿意容忍的不确定性的度量:如果调用返回 true,则此 BigInteger 为素数的概率超过 (1 - 1/2^certainty)。该方法的执行时间与该参数的值成正比。

        返回值

        如果这个 BigInteger 可能是素数,则此方法返回 true,如果它肯定是合数,则返回 false。

        示例

        下面的例子展示了 math.BigInteger.isProbablePrime() 方法的使用

        import java.math.*;
        
        public class BigIntegerDemo {
        
            public static void main(String[] args) {
                // create 3 BigInteger objects
            BigInteger bi1, bi2, bi3;
        
            // create 3 Boolean objects
            Boolean b1, b2, b3;
        
            // assign values to bi1, bi2
            bi1 = new BigInteger("7");
            bi2 = new BigInteger("9");
        
            // perform isProbablePrime on bi1, bi2
            b1 = bi1.isProbablePrime(1);
            b2 = bi2.isProbablePrime(1);
            b3 = bi2.isProbablePrime(-1);
        
            String str1 = bi1+ " is prime with certainity 1 is " +b1;
            String str2 = bi2+ " is prime with certainity 1 is " +b2;
            String str3 = bi2+ " is prime with certainity -1 is " +b3;
        
            // print b1, b2, b3 values
            System.out.println( str1 );
            System.out.println( str2 );
            System.out.println( str3 );
            }
        }
        

        输出

        7 is prime with certainity 1 is true
        9 is prime with certainity 1 is false
        9 is prime with certainity -1 is true
        

        【讨论】:

        • 这是一个写得很好的答案,但不幸的是,问题是寻求 C++ 代码的帮助,而不是 Java。我建议删除这个答案,这样你就不会反复被否决。继续努力,但要尽量确保你的答案符合问题的要求!感谢您对 StackOverflow 的帮助!
        • 感谢您的宝贵时间。但我正在寻找算法实现而不是库函数,它做同样的工作。
        猜你喜欢
        • 2012-10-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-08
        • 1970-01-01
        • 1970-01-01
        • 2012-02-18
        • 2021-09-03
        相关资源
        最近更新 更多