我正在为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的节点模块,但是为了使事情简单,我希望将其保留在浏览器中。如果没有,并且可以提供证明,我将接受该答案。
答案 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
中进行了设置。)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个素数得到的结果相同,而且至少在轶事上,它的运行速度似乎要快得多。