【问题标题】:How do I calculate all prime numbers with a really large max in the browser using JSFiddle如何使用 JSFiddle 在浏览器中计算所有具有非常大最大值的素数
【发布时间】:2019-01-07 07:40:53
【问题描述】:

我正在为 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 的节点模块,但为了简单起见,如果可能的话,我想将它保留在浏览器中。如果没有,并且可以提供证据,我会接受这个答案。

【问题讨论】:

  • 嗯,好吧,不管用什么方法,一个包含 3,084,205 项的数组会有点笨拙……我不认为 Javascript 是计算海量数据的正确工具
  • 你应该使用segmented sieve
  • primes-generator 你能不能给你更好的性能,前提是你不想一次把所有的值都存入内存。

标签: javascript browser vuejs2 primes


【解决方案1】:

您遇到的问题可能是由于内存不足,而您的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;
  }
}

然后,仅凭这一点,您就可以运行 sn-p 并获得结果(尽管需要一段时间):

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))

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

  • 您可以从i*i 开始j 而不仅仅是i,因为任何小于此的数字都将具有比i 更小的素数作为因子(因此,已经在@ 中设置了987654328@)。
  • 你真的只需要检查奇数作为筛子的一部分,所以你可以改变外循环每次增加2(i += 2而不是i++),然后改变内循环增加i*2 而不是 i(因为 j + i*(odd) 总是偶数)。

使用这些,您可以将您的 sn-p 更改为:

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 个素数,您得到相同的结果,而且,至少有趣的是,它似乎运行得更快。

【讨论】:

  • 我确认这是使用节点的内存限制。当我在那里运行相同的代码时,我看到...FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory 所以我认为你在正确的轨道上。感谢您的精彩解释。
猜你喜欢
  • 1970-01-01
  • 2018-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多