【问题标题】:Sum of prime numbers for large range input大范围输入的素数总和
【发布时间】:2019-06-23 16:53:37
【问题描述】:

我正在编写代码来查找给定数字以下的所有质数之和(在这种情况下,我想找到 2000000)

我的代码对于 20000 或更低的数字可以正常运行,但是当我向它添加 0 时,它不会。

我尝试在代码沙盒上运行它,它会告诉我某处可能存在无限循环。

const isPrime = number => {

  let k=0

  for (let i=2; i < number; i++) {
    if (number % i === 0) {
    k++
    }
  }
  if (k === 0) {
    return true
  } else {
      return false
  } 
}

const sumOfPrimes = limit => {

  let primeSum = 0
  for (let i=1; i <= limit; i++) {
    if (isPrime(i)) {
        primeSum += i
    }
  } console.log(primeSum);
}

sumOfPrimes(2000000);

【问题讨论】:

  • "潜在的无限循环" 并不意味着你有一个无限循环,它只是意味着它运行了很长时间。这是预期的,您的算法具有平方时间复杂度。不,它不会占用内存 - 事实上,您应该使用更多内存并用它换取更好的运行时间。
  • 您可以采取一些措施来加快速度。首先,您应该尽早退出 for 循环。一旦你发现它不是素数,只需返回if (number % i === 0) return false。此外,您只需要检查一个数字的平方根。 2000000 仍然会很慢。
  • 您的isPrime 非常不理想。两个简单的改进是仅在第一个遇到的因素上从for 循环内迭代到Math.sqrt(number)return false,而不是计算所有因素。如果它只有一个因数,你就已经知道它不是质数了。

标签: javascript loops


【解决方案1】:

如果您必须处理大至 2,000,000 的数字,那么这不是解决问题的正确方法。判断一个数是否为素数的算法有很多,在算法的复杂性和大数的效率之间存在权衡。为了知道哪种算法适合您的用例,我们需要知道您的用例是什么。 (听起来您正在尝试解决课程或代码挑战中的给定问题。)

但即使使用您正在使用的算法,也有一些简单的方法可以加快速度。一方面,在isPrime 的循环中,当number % i === 0 时,您应该return false 而不是递增变量并稍后检查它。这种变化本身应该会显着加快您的程序,因为大多数数字都有小的除数,所以大多数数字只会运行几次循环。

另一个简单的加速方法是限制循环次数。您正在迭代从 2 到 n 的所有数字。但是要检查一个数是否是素数,你只需要检查它是否可以被素数整除。如果您的目标是计算前多个素数的总和,那么这很容易:构建一个素数列表,将每个新候选者与列表中已有的数字进行比较。我强烈怀疑这种方法将足以满足您的需求。

【讨论】:

    【解决方案2】:

    更有效的是使用 Eratosthenes 的筛子。通常这会返回一个素数列表,直到给定的限制,但是通过reduce 的小调整,你可以让它返回总和:

    function sumOfPrimes(n) {
        const nums = Array(n).fill(true); 
        nums[0] = nums[1] = false;
        const sq = Math.sqrt(n);
    
        for (let i = 2; i <= sq; i++) {
            if (nums[i]) {
                for (let j = i * i; j < n; j += i) nums[j] = false;
            }
        }
        
        return nums.reduce((sum, a, i) => sum + (a && i), 0);
    }
    
    
    console.log(sumOfPrimes(10));
    console.log(sumOfPrimes(2000000));

    请注意,有一些方法可以获得更好的性能,例如 segmented sieve of Eratosthenes

    【讨论】:

    • 有趣的是,我最近提供了an answer on how to optimize the memory usage of a sieve in JavaScript。我知道这超出了这个特定问题的范围,但也许这个链接会帮助任何遇到这个答案并通过更大的输入获得内存不足异常的人。 (虽然由于您使用的是Boolean,我实际上不确定内存使用情况如何)
    【解决方案3】:

    只是想我会用一个示例实现来支持Thom Smith's answer

    const primes = [2, 3];
    
    function isPrime (n) {
      // eliminate base cases
      if (n < 2) return false;
    
      const sqrt = Math.sqrt(n);
      let i;
    
      // check if known primes are factors of n
      for (i of primes) {
        if (i > sqrt) break;
        if (n % i === 0) return false;
      }
    
      // check if odd numbers between largest
      // known prime and sqrt(n) are factors of n
      for (i += 2; i <= sqrt; i += 2) {
        if (n % i === 0) return false;
      }
    
      // prevents duplicate primes from being added
      if (primes[primes.length - 1] < n) {
        primes.push(n);
      }
    
      return true;
    }
    
    function sumOfPrimes (limit) {
      let primeSum = 0;
    
      for (let i = 1; i <= limit; i++) {
        if (isPrime(i)) primeSum += i;
      }
    
      return primeSum;
    }
    
    console.log(sumOfPrimes(10));
    console.log(sumOfPrimes(2000000));

    isPrime() 专为通过增加输入n 调用而设计。如果在一个较小的素数n之前检查了较大的素数n,则条件

    if (primes[primes.length - 1] < n)
    

    将无法将较小的素数添加到已知素数列表中,但由于这种情况不会发生在这种用法中,这就足够了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-12-06
      • 1970-01-01
      • 1970-01-01
      • 2013-08-03
      • 2021-08-28
      • 1970-01-01
      • 2020-05-16
      • 1970-01-01
      相关资源
      最近更新 更多