如何使用JSFiddle在浏览器中计算具有非常大的最大值的所有素数

时间:2018-07-31 01:48:37

标签: javascript browser vuejs2 primes

我正在为JS开发一些典型的kata程序,偶然发现了一个需要很大数量的素数的程序。我尝试了following in JSFiddle

findPrimes(max){
   let dont = [], primes = [];
   for (var i = 2; i <= max; i++) {
     if (!dont[i]) {
       primes.push(i);
       for (var j = i; j <= max; j += i) dont[j] = true;
     }
   }
}

此方法在this.findPrimes(51475143);之前效果相对良好,但是,如果我尝试说... this.findPrimes(851475143);,我会感到悲伤,并且JS引擎似乎崩溃了。我知道我可以直接使用V8压缩一下,甚至可以使用基于C的节点模块,但是为了使事情简单,我希望将其保留在浏览器中。如果没有,并且可以提供证明,我将接受该答案。

1 个答案:

答案 0 :(得分:2)

您遇到的问题很可能是由于内存不足而导致的,dont数组是罪魁祸首。幸运的是,由于它只是一个布尔数组,因此您可以使用位数组来做同样的事情,这样可以节省一些空间。

JavaScript没有任何本机位数组类型,但是您可以通过存储32位数字数组并使用按位运算符来检索或设置所需的位来模拟一个。像这样:

class BitArray {
  constructor(len) {
    this.arr = Array(Math.ceil(len / 32)).fill(0)
  }

  set(i) {
    const j = Math.floor(i / 32)
    this.arr[j] = this.arr[j] | (1 << (i % 32))
  }

  get(i) {
    const j = Math.floor(i / 32)
    return (this.arr[j] & (1 << (i % 32))) && 1 || 0;
  }
}

然后,仅此一项,就可以运行您的代码片段并获得结果(尽管需要一些时间):

class BitArray {
  constructor(len) {
    this.arr = Array(Math.ceil(len / 32)).fill(0)
  }

  set(i) {
    const j = Math.floor(i / 32)
    this.arr[j] = this.arr[j] | (1 << (i % 32))
  }

  get(i) {
    const j = Math.floor(i / 32)
    return (this.arr[j] & (1 << (i % 32))) && 1 || 0;
  }
}

function findPrimes(max) {
  const dont = new BitArray(max)
  const primes = [];
  for (var i = 2; i <= max; i++) {
    if (!dont.get(i)) {
      primes.push(i);
      for (var j = i * 2; j <= max; j += i) dont.set(j);
    }
  }

  return primes;
}

const primes = findPrimes(851475143);
console.log("Done. Max Prime:", primes[primes.length - 1])
console.log("And last 10 primes:", primes.slice(-10))

但是,除此之外,您还可以对筛子进行更多优化:

  • 您可以从j开始i*i而不是仅仅从i开始,因为小于这个数的素数已经比i的素数小了(因此,已经在dont中进行了设置。)
  • 您实际上只需要检查筛子中的奇数,因此您可以将外部循环更改为每次递增2(i += 2而不是i++),然后更改内部循环循环以i*2而不是i递增(因为j + i*(odd)总是偶数)。

使用这些代码,您可以将代码段更改为:

class BitArray {
  constructor(len) {
    this.arr = Array(Math.ceil(len / 32)).fill(0)
  }

  set(i) {
    const j = Math.floor(i / 32)
    this.arr[j] = this.arr[j] | (1 << (i % 32))
  }

  get(i) {
    const j = Math.floor(i / 32)
    return (this.arr[j] & (1 << (i % 32))) && 1 || 0;
  }
}

function findPrimes(max) {
  const dont = new BitArray(max / 2)  // Only need half the memory, since we only check odds.
  const primes = [2];                 // We already know 2 is prime
  for (var i = 3; i <= max; i += 2) { // Start at 3, increment by 2, so only odds are checked.
    if (!dont.get((i-1)/2)) {         // The (i-1)/2 converts each odd to it's "halfway" number
      primes.push(i);
      for (var j = i*i; j <= max; j += i*2) dont.set((j-1)/2);
    }
  }

  return primes;
}

const primes = findPrimes(851475143);
console.log("Done. Max Prime:", primes[primes.length - 1])
console.log("And last 10 primes:", primes.slice(-10))

如您所见,最后10个素数得到的结果相同,而且至少在轶事上,它的运行速度似乎要快得多。