我需要计算小于或等于某个N的素数,这是素数计数函数或PI函数。我有这个,但效果太慢了:
function PI(x) {
var primes = 4;
for (var i = 3; i <= x; i += 2) {
if (i % 3 === 0 || i % 5 === 0 || i % 7 === 0) continue;
var r = ~~Math.sqrt(i), p = true;
for (var j = 2; j <= r; j++) {
if (i % j === 0) {
p = false;
break;
}
}
if (p)
primes++;
}
return primes;
}
计算PI(1000000)
需要将近一秒钟,计算PI(10000000)
对我来说只需要20秒。那太慢了。有没有更快的方法呢?
答案 0 :(得分:0)
聚会晚了一点,但这在O(n ^ 2/3)的时间和空间上有效。
function eratosthenesWithPi(n) {
let array = [], upperLimit = Math.sqrt(n), output = [];
let pi = [0, 0]
for (let i = 0; i < n; i++) {
array.push(true);
}
for (let i = 2; i <= upperLimit; i++) {
if (array[i]) {
for (var j = i * i; j < n; j += i) {
array[j] = false;
}
}
}
let cnt = 0
for (let i = 2; i < n; i++) {
if (array[i]) {
output.push(i);
cnt++
}
pi.push(cnt)
}
return {primes: new Uint32Array(output), pi: new Uint32Array(pi)}
}
const phiMemo = []
let primes = []
function Phi(m, b) {
if (b === 0)
return m
if (m === 0)
return 0
if (m >= 800) {
return Phi(m, b - 1) - Phi(Math.floor(m / primes[b - 1]), b - 1)
}
let t = b * 800 + m
if (!phiMemo[t]) {
phiMemo[t] = Phi(m, b - 1) - Phi(Math.floor(m / primes[b - 1]), b - 1)
}
return phiMemo[t]
}
const smallValues = [0, 0, 1, 2, 2, 3]
let piValues
function primeCountingFunction(x) {
if (x < 6)
return smallValues[x]
let root2 = Math.floor(Math.sqrt(x))
let root3 = Math.floor(x ** (1/3))
let top = Math.floor(x / root3) + 1
if (root2 + 1 >= primes.length) {
let res = eratosthenesWithPi(top + 2)
primes = res.primes
piValues = res.pi
}
let a = piValues[root3 + 1], b = piValues[root2 + 1]
let sum = 0
for (let i = a; i < b; ++i) {
let p = primes[i]
sum += piValues[Math.floor(x / p)] - piValues[p] + 1
}
let phi = Phi(x, a)
return phi + a - 1 - sum
}
console.log(primeCountingFunction(1e8))
在JSFiddle上尝试:https://jsfiddle.net/vo0g274f/1/
对我来说,这大约需要31毫秒。我正在开发一种更节省空间的方法atm,完成后将其发布在这里。