可能对某些人有用:米勒拉宾素数测试的实现。适用于小于 Number.MAX_SAFE_INTEGER 的所有正整数。
试用 JSFiddle:https://jsfiddle.net/4rxhas2o/
let unsafeToSquare = Math.floor(Math.sqrt(Number.MAX_SAFE_INTEGER))
function addMod(a, b, m) {
// Returns (a + b) % m
let sum = a + b
let result = sum % m
if (sum < Number.MAX_SAFE_INTEGER)
return result
let signature = ((a % 8) + (b % 8)) % 8
let sumMod = sum % 8
for (let i = -2; i <= 2; ++i) {
if ((sumMod + i) % 8 === signature) {
let ret = result + i
if (ret > m)
ret = (result - m) + i // prevent overflow
return ret
}
}
}
function mulMod(a, b, m) {
if (m === 0)
return 0
let prod = a * b
if (prod < Number.MAX_SAFE_INTEGER)
return prod % m
let y = 0
let result = a
while (b > 1) {
if (b % 2 === 0) {
result = addMod(result, result, m)
b /= 2
} else {
y = addMod(result, y, m)
result = addMod(result, result, m)
b = (b - 1) / 2
}
}
return addMod(result, y, m)
}
function squareMod(b, m) {
// Computes (b * b % m)
return mulMod(b, b, m)
}
function expModLargeB(b, exponent, m) {
let y = 1
while (exponent > 1) {
if (exponent % 2 === 0) {
b = squareMod(b, m)
exponent /= 2
} else {
y = mulMod(y, b, m)
b = squareMod(b, m)
exponent = (exponent - 1) / 2
}
}
return mulMod(b, y, m)
}
function expMod(b, exponent, m) {
if (exponent === 0)
return 1
if (b >= unsafeToSquare || m >= unsafeToSquare) {
return expModLargeB(b, exponent, m)
}
let y = 1
while (exponent > 1) {
if (exponent % 2 === 0) {
b *= b
b %= m
exponent /= 2
} else {
y *= b
b *= b
y %= m
b %= m
exponent = (exponent - 1) / 2
}
}
return (b * y) % m
}
function _isPrimeTrialDivision(p) {
let sqrtP = Math.ceil(Math.sqrt(p))
for (let i = 23; i <= sqrtP + 1; i += 2) {
if (p % i === 0)
return false
}
return true
}
function _isProbablePrimeMillerRabin(p, base=2) {
let pm1 = p - 1
let pm1div = pm1
let d, r = 0
while (true) {
if (pm1div % 2 === 0) {
pm1div /= 2
r++
} else {
d = pm1div
break
}
}
let x = expMod(base, d, p)
if (x === 1 || x === pm1)
return true
for (let i = 0; i < r - 1; ++i) {
x = squareMod(x, p)
if (x === pm1)
return true
}
return false
}
function _isPrimeLarge(p) {
let bases
if (p < 2047)
bases = [2]
else if (p < 1373653)
bases = [2, 3]
else if (p < 9080191)
bases = [31, 73]
else if (p < 25326001)
bases = [2, 3, 5]
else if (p < 3215031751)
bases = [2, 3, 5, 7]
else if (p < 4759123141)
bases = [2, 7, 61]
else if (p < 1122004669633)
bases = [2, 13, 23, 1662803]
else if (p < 2152302898747)
bases = [2, 3, 5, 7, 11]
else if (p < 3474749660383)
bases = [2, 3, 5, 7, 11, 13]
else if (p < 341550071728321)
bases = [2, 3, 5, 7, 11, 13, 17]
else
bases = [2, 3, 5, 7, 11, 13, 17, 19, 23]
return bases.every(base => _isProbablePrimeMillerRabin(p, base))
}
let smallPrimes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223]
function isPrime(p) {
if (!Number.isInteger(p) || p < 2)
return false
// Test for small primes
for (let i = 0; i < smallPrimes.length; ++i) {
let prime = smallPrimes[i]
if (p === prime)
return true
if (p % prime === 0)
return false
}
if (p < 150) {
return _isPrimeTrialDivision(p)
} else {
return _isPrimeLarge(p)
}
}
const tests = [1, 2, 3, 10, 100, 100019, 10000000019, 100000000003, 10000000000037]
let start = performance.now()
tests.forEach(test => {
console.log(`${test} is ${ isPrime(test) ? "" : "not " }prime`)
})
let end = performance.now()
console.log("Tests completed in " + (end - start) + " ms.")