简体   繁体   English

使用埃拉托色尼筛法的最大素数

[英]Biggest prime number using sieve of Eratosthenes

I managed to solve the problem but I am exceeding the time limit.我设法解决了这个问题,但我超过了时间限制。

What to fix : If the given number n is very large it takes ridiculous amount of time.解决方法:如果给定的数字n非常大,则需要很长的时间。

Question : Is there a way I can tweak the code to get the last prime number faster?问题有没有办法可以调整代码以更快地获得最后一个素数?

const n = 126;
let lastPrime = 0;

for (let j = 2; j <= n; j++) {
    let counter = 0;
    for (let i = 1; i <= j; i++) {
        if (j % i === 0) counter++;
    }
    if (counter === 2) lastPrime = j;
}

print(lastPrime); // Biggest prime number of 126 is 113

Thanks!谢谢!

There are plenty of optimizations to be done here - a quick read here ( https://math.stackexchange.com/questions/889712/the-fastest-way-to-count-prime-number-that-smaller-or-equal-n/893767 ) would help.这里有很多优化要做 - 快速阅读这里( https://math.stackexchange.com/questions/889712/the-fastest-way-to-count-prime-number-that-smaller-or-equal -n/893767 )会有所帮助。

But for starters, you can change a few things in your code to make it trivially faster:但是对于初学者来说,您可以在代码中更改一些内容以使其更快:

Step 1: Reduce the number of outer iterations, since we know all even numbers are non-prime.第 1 步:减少外部迭代的次数,因为我们知道所有偶数都是非质数。

for (let j = 3; j <= n; j += 2) {
...
}

Step 2: Reduce the inner-loop by only iterating up to a max of Sqrt of the max number.第 2 步:通过仅迭代到最大数的最大 Sqrt 来减少内循环。 Also break out of the inner loop once we find even one factor.一旦我们找到一个因素,也要打破内部循环。 No need to iterate till the end.无需迭代到最后。 These two will give you the biggest wins.这两个会给你最大的胜利。

let prime = true;    
for (let i = 2; i <= Math.sqrt(j); i++) { 
  if (j % i === 0) {
    prime = false;
    break;
  }
}
if (prime) {
  lastPrime = j;
}

Step 3: Stop computing Math.sqrt(j) since you already know the previous max value.第 3 步:停止计算 Math.sqrt(j),因为您已经知道之前的最大值。 sqrt is a (relatively) expensive operation. sqrt 是一个(相对)昂贵的操作。 We can avoid it by making use of previous value.我们可以通过使用先前的值来避免它。

let maxBound = 2;
let maxSquare = maxBound * maxBound;

for (let j = 3; j <= n; j += 2) {
  if (maxSquare < j) {
    maxBound++;
    maxSquare = maxBound * maxBound;
  }
  for (let i = 2; i <= maxBound; i++) {
  ...
  }
}

Step 4: If all you want is the biggest prime, walk the loop backwards and break as soon as you find one prime.第 4 步:如果你想要的只是最大的素数,则向后走循环,并在找到一个素数时立即中断。

And here's the finished program which should be approximately 2 orders of magnitude faster than yours.这是完成的程序,它应该比你的快大约 2 个数量级。 Note that while I provided some trivial optimizations for your program, this will always pale in comparison to algorithmic optimizations that you can find here: https://math.stackexchange.com/a/893767请注意,虽然我为您的程序提供了一些微不足道的优化,但与您可以在此处找到的算法优化相比,这总是相形见绌:https://math.stackexchange.com/a/893767

function getMaxPrime(n) {
  for (let j = n; j >= 3; j --) {
    let prime = true;
    for (let i = 2; i <= Math.sqrt(j); i++) {
      if (j % i === 0) {
        prime = false;
        break;
      }
    }
    if (prime) {
      maxPrime = j;
      break;
    }
  }

  console.log(maxPrime);
}

A basic question I ask myself when looking at sieve code is: "does this code use the modulo (%) operator?"在查看筛选代码时,我问自己的一个基本问题是:“此代码是否使用模 (%) 运算符?” If it does, it isn't the Sieve of Eratosthenes.如果是这样,它就不是埃拉托色尼筛。 You're doing trial division, which is vastly slower.你正在做试验师,这要慢得多。 Now there are certainly places and reasons for trial division, but based on your question title, you intended to use the SoE.现在肯定有审判划分的地方和原因,但是根据您的问题标题,您打算使用SoE。

See Wikipedia pseudocode for example.例如,参见Wikipedia 伪代码 The only operations are the two loops that involve only simple additions, a test, and a set.唯一的操作是仅涉及简单加法、测试和集合的两个循环。 That's it.而已。 No multiplies, no divides, no modulos.没有乘法,没有除法,没有模数。 This is absolutely key to why the algorithm is fast.这绝对是算法快速的关键 The inner loop also quickly becomes sparse, in the sense that the increment keeps getting larger, so we actually run the inner loop code fewer times as we go on.内部循环也很快变得稀疏,因为增量不断变大,所以我们实际上在 go 上运行内部循环代码的次数更少。 Contrast to the initial code you posted, where the inner loop is running more times.与您发布的初始代码相比,内部循环运行的次数更多

To do the basic SoE, you need a small array, then exactly and only 4 lines of code to do the sieving, with the outer loop going to sqrt(n).要执行基本的 SoE,您需要一个小数组,然后只需 4 行代码即可进行筛选,外部循环转到 sqrt(n)。 Then you can examine the array which will have only primes left marked.然后您可以检查仅标记素数的数组。 In your case, you can walk it backwards and return when you find the first occurrence.在您的情况下,您可以向后走并在找到第一次出现时返回。 There are countless methods for optimization, but it's surprising how fast the simple basic SoE is for relatively small n (eg under 10^9, after which you really need to do a segmented SoE).有无数种优化方法,但是对于相对较小的 n(例如,在 10^9 以下,之后您确实需要进行分段 SoE),简单的基本 SoE 的速度之快令人惊讶。

All that said, you got working code, which is a great first step.综上所述,你得到了工作代码,这是很好的第一步。 It's much easier to try different methods once you have something working.一旦你有一些工作,尝试不同的方法会容易得多。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM