【问题标题】:Finding the list of prime numbers in shortest time在最短的时间内找到素数列表
【发布时间】:2012-05-28 06:48:27
【问题描述】:

我阅读了很多算法来找到素数,结论是如果一个数字不能被其前面的任何素数整除,那么它就是一个素数。

我找不到更准确的定义。基于此,我编写了一个代码,它的性能令人满意,直到我通过的最大数字为 1000000。但我相信有更快的算法来找到所有小于给定数字的素数。

以下是我的代码,我可以有更好的版本吗?

 public static void main(String[] args) {
    for (int i = 2; i < 100000; i++) {
        if (checkMod(i)) {
            primes.add(i);
        }
    }
}

private static boolean checkMod( int num) {
    for (int i : primes){
        if( num % i == 0){
            return false;
        }
    }
    return true;
}

【问题讨论】:

标签: algorithm primes


【解决方案1】:

我总是使用 Eratosthenes 筛子:

isPrime[100001] // - initially contains only '1' values (1,1,1 ... 1)
isPrime[0] = isPrime[1] = 0 // 0 and 1 are not prime numbers

primes.push(2); //first prime number. 2 is a special prime number because is the only even prime number.
for (i = 2; i * 2 <= 100000; i++) isPrime[i * 2] = 0 // remove all multiples of 2

for (i = 3; i <= 100000; i += 2) // check all odd numbers from 2 to 100000
    if (isPrime[i]) {
        primes.push(i); // add the new prime number to the solution
        for (j = 2; i * j <= 100000; j++) isPrime[i * j] = 0; // remove all i's multiples
    }

return primes

希望你能理解我的cmets

【讨论】:

    【解决方案2】:

    我理解素数是一个只能被自身和数字 1 整除的数字(没有余数)。见Wikipedia Article

    话虽如此,我在第二条评论中不太了解该算法,但对您的算法的一个小改进是将您的 for 循环更改为:

    for (int i = 5; i < 100000; i = i + 2) {
        if (checkMod(i)) {
            primes.add(i);
        }
    }
    

    这是基于这样的假设,即 1、2 和 3 都是素数,之后的所有偶数都不是素数。这至少将您的算法减半。

    【讨论】:

    • 不,这几乎没有影响。寻找非主要的复合材料是试用部门的廉价部分。昂贵的部分是检查素数。哦,还有 1 不是素数
    【解决方案3】:

    素数测试的好处是你只除以素数。

    private static boolean checkMod( int num) {
        for (int i : primes){
            if( num % i == 0){
                return false;
            }
        }
        return true;
    }
    

    不好的是你除以 所有 到目前为止找到的素数,即所有小于候选的素数。这意味着对于低于 100 万的最大素数 999983,您除以 78497 个素数以发现这个数字是素数。这是很多工作。事实上,当达到 100 万时,在这个算法中花费在素数上的工作占所有工作的 99.9% 左右,在更高的限制中占比更大。而且该算法几乎是二次的,要以这种方式找到n 的素数,您需要执行约

    n² / (2*(log n)²)
    

    部门。

    一个简单的改进是提前停止除法。令n 为合数(即大于1 且除数除1 和n 之外的数),令dn 的除数。

    现在,dn 的除数意味着 n/d 是一个整数,也是 n 的除数:n/(n/d) = d。 所以我们可以很自然地将n的除数分组,每个除数d产生一对(d, n/d)

    对于这样的一对,有两种可能:

    1. d = n/d,表示n = d²,或d = √n
    2. 两者不同,然后其中一个小于另一个,例如d &lt; n/d。但这会立即转化为d² &lt; nd &lt; √n

    因此,无论哪种方式,每对除数包含(至少)一个不超过√n,因此,如果n 是一个合数,它的最小除数(1 以外)不超过@ 987654347@.

    所以当我们到达√n时我们可以停止审判部门:

    private static boolean checkMod( int num) {
        for (int i : primes){
            if (i*i > n){
                // We have not found a divisor less than √n, so it's a prime
                return true;
            }
            if( num % i == 0){
                return false;
            }
        }
        return true;
    }
    

    注意:这取决于按升序迭代的素数列表。如果语言不能保证这一点,您必须使用不同的方法,通过ArrayList 或类似的东西按索引迭代。

    停止在候选的平方根处进行除法,对于 100 万以下的最大素数 999983,我们现在只需将其除以 1000 以下的 168 个素数。这比以前少了很多工作。在平方根处停止试除法,并且只除以素数,这与试除法一样好,并且需要大约

    2*n^1.5 / (3*(log n)²)
    

    对于n = 1000000 的划分,大约是 750 倍,还不错吧?

    但这仍然不是很有效,找到n 以下所有素数的最有效方法是筛子。易于实现的是经典的Sieve of Eratosthenes。在 O(n*log log n) 操作中找到低于n 的素数,通过一些增强(预先排除多个小素数的倍数),其复杂性可以降低到 O(n) 操作。一个相对较新的具有更好渐近行为的筛子是Sieve of Atkin,它在 O(n) 操作中找到n 的素数,或者通过消除一些小素数的倍数的增强,在 O(n/log log n ) 操作。 阿特金筛子的实现更复杂,因此埃拉托色尼筛子的良好实现可能比阿特金筛子的简单实现表现更好。对于类似优化级别的实现,性能差异很小,除非限制变大(大于 1010;而且在实践中,埃拉托色尼筛比阿特金筛更好的扩展性并不罕见那是因为更好的内存访问模式)。因此,我建议从 Eratosthenes 筛开始,并且仅当尽管诚实地优化优化但其性能仍不能令人满意时,才深入研究 Atkin 筛。或者,如果您不想自己实现它,请找到其他人已经认真调整过的良好实现。

    我在an answer 中进行了更详细的设置,但设置略有不同,问题在于找到第 n 个素数。一些或多或少有效方法的实现与该答案相关联,特别是埃拉托色尼筛的一两个可用(尽管没有多少优化)实现。

    【讨论】:

    • 谢谢丹尼尔..这解释得很好。
    • (3/4)*(1000000^0.5) = 750。:)
    • @WillNess 噢,废话,我真的应该花时间把它写在纸上。谢谢。
    【解决方案4】:

    我想对上面 Benjamin Oman 建议的 0ne 做一个稍微改进的版本, 这只是一种修改,以避免检查所有以数字“5”结尾的数字的素数,因为这些数字肯定不是素数,因为它们可以被 5 整除。

    for (int i = 7;(i < 100000) && (!i%5==0); i = i + 2) {
        if (checkMod(i)) {
            primes.add(i);
        }
    }
    

    这是基于 2,3,5 是素数的假设。上面的小改动会减少5的所有因素并提高。

    【讨论】:

    • 那么,在这个循环之前,primes 是如何初始化的?它是否包含任何数字,例如 2、3 和 5?
    【解决方案5】:

    @Daniel Fischer 很好地解释了。

    他的解释中的 C++ 实现:

    #include<iostream>
    
    using namespace std;
    
    long* getListOfPrimeNumbers (long total)
    {
      long * primes;
      primes = new long[total];
      int count = 1;
      primes[0] = 2;
      primes[1] = 3;
      while (count < total)
      {
        long composite_number = primes[count] + 2;
        bool is_prime = false;
        while (is_prime == false)
        {
          is_prime = true;
          for (int i = 0; i <= count; i++)
          {
            long prime = primes[i];
            if (prime * prime > composite_number)
            {
              break;
            }
            if (composite_number % prime == 0)
            {
              is_prime = false;
              break;
          }
          }
          if (is_prime == true)
          {
            count++;
            primes[count] = composite_number;
          }
          else
          {
            composite_number += 2;
        }
      }
      }
      return primes;
    }
    
    int main()
    {
      long * primes;
      int total = 10;
      primes = getListOfPrimeNumbers(total);
      for (int i = 0; i < total; i++){
        cout << primes[i] << "\n";
      }
      return 0;
    }
    

    【讨论】:

      【解决方案6】:
      import array , math
      
      print("enter a range to find prime numbers")
      
      a= 0
      b= 5000
      c=0
      x=0
      k=1
      g=[2]
      l=0
      
      
      for I in range( a , b):
          for k in g:
              x=x+1
      
      
              if k>2:
                  if k > math . sqrt( I ):
      
                      break
      
              if( I % k==0):
      
                  c=c+1
                  break
          if c==0:
              if I!=1:
      
                  g . append( I )
      
      
      
          c=0    
      print g
      
      
      
      #this algorithm will take only 19600 iteration for a range from 1-5000,which is one of fastest algorithm according to me
      

      【讨论】:

      • 如果有一些关于您的代码如何工作的 cmets,那就太好了。
      【解决方案7】:

      我发现数学家说“那个”“3 之后的质数总是 6 的倍数的一侧”。 它 maen 5 ,7 素数更接近 6。 11,13 也更接近 62。 17,19 也是 63。 21,23 也是 6*4。 我写的都很正常,喜欢这个最多100万,我发现这个算法也对而且更快。?


      num=1000000
      prime=[2,3]
      def test(i):
          for j in prime:
              if(i%j==0):
                  break
              if(j*j>i):
                  prime.append(i)
                  break
          return 0
      for i in range (6,num,6):
          i=i-1
          test(i)
          i=i+2
          test(i)
          i=i-1
          
      print(prime)
      

      【讨论】:

      • 为什么不选择 15 的倍数呢?并比较 8 个差异 (+/- 2,4,6,14) 与 6 到 30 (+/-2 * 4.5) = 9 个差异的倍数?为什么停在那里?
      • 对不起,我听不懂你的意思。可能是我的英语问题,请先生多解释一下。
      • 使用 6 +/- 1 很复杂。最好从 2 开始,然后从 3 +2 (3, 5, 7, 9...) 开始,并检查已经找到的素数。在 javascript 中:function primesUpTo(max) { var primes=[2]; for(i=3; i&lt;=max; i+=2) { var prime=true; for(j=0; j&lt;primes.length &amp;&amp; primes[j]&lt;=Math.sqrt(i) &amp;&amp; (prime=(i%primes[j]!=0)); j++); if(prime) { primes.push(i); } } return primes; } 在我的函数中:没有意义检查大于平方根。
      猜你喜欢
      • 2015-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-03
      • 2014-11-26
      • 2011-06-26
      相关资源
      最近更新 更多