mirror of
https://github.com/josdejong/mathjs.git
synced 2026-01-25 15:07:57 +00:00
Miller–Rabin primality test (#2139)
* Miller–Rabin primality test * add tests for some big numbers * Update isPrime.js * Update isPrime.js Co-authored-by: Jos de Jong <wjosdejong@gmail.com>
This commit is contained in:
parent
47a493d70a
commit
91141bbbb6
@ -58,13 +58,62 @@ export const createIsPrime = /* #__PURE__ */ factory(name, dependencies, ({ type
|
||||
}
|
||||
if (n.lte(3)) return n.gt(1)
|
||||
if (n.mod(2).eq(0) || n.mod(3).eq(0)) return false
|
||||
|
||||
for (let i = 5; n.gte(i * i); i += 6) {
|
||||
if (n.mod(i).eq(0) || n.mod(i + 2).eq(0)) {
|
||||
return false
|
||||
if (n.lt(Math.pow(2, 32))) {
|
||||
const x = n.toNumber()
|
||||
for (let i = 5; i * i <= x; i += 6) {
|
||||
if (x % i === 0 || x % (i + 2) === 0) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function modPow (base, exponent, modulus) {
|
||||
// exponent can be huge, use non-recursive variant
|
||||
let accumulator = 1
|
||||
while (!exponent.eq(0)) {
|
||||
if (exponent.mod(2).eq(0)) {
|
||||
exponent = exponent.div(2)
|
||||
base = base.mul(base).mod(modulus)
|
||||
} else {
|
||||
exponent = exponent.sub(1)
|
||||
accumulator = base.mul(accumulator).mod(modulus)
|
||||
}
|
||||
}
|
||||
return accumulator
|
||||
}
|
||||
|
||||
// https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test#Deterministic_variants
|
||||
const Decimal = n.constructor.clone({ precision: n.toFixed(0).length * 2 })
|
||||
n = new Decimal(n)
|
||||
let r = 0
|
||||
let d = n.sub(1)
|
||||
while (d.mod(2).eq(0)) {
|
||||
d = d.div(2)
|
||||
r += 1
|
||||
}
|
||||
let bases = null
|
||||
// https://en.wikipedia.org/wiki/Miller–Rabin_primality_test#Testing_against_small_sets_of_bases
|
||||
if (n.lt('3317044064679887385961981')) {
|
||||
bases = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41].filter(x => x < n)
|
||||
} else {
|
||||
const max = Math.min(n.toNumber() - 2, Math.floor(2 * Math.pow(n.toFixed(0).length * Math.log(10), 2)))
|
||||
bases = []
|
||||
for (let i = 2; i <= max; i += 1) {
|
||||
bases.push(max)
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < bases.length; i += 1) {
|
||||
const a = bases[i]
|
||||
const adn = modPow(n.sub(n).add(a), d, n)
|
||||
if (!adn.eq(1)) {
|
||||
for (let i = 0, x = adn; !x.eq(n.sub(1)); i += 1, x = x.mul(x).mod(n)) {
|
||||
if (i === r - 1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
|
||||
@ -44,4 +44,9 @@ describe('isPrime', function () {
|
||||
assert.throws(function () { isPrime(new Date()) }, /TypeError: Unexpected type of argument/)
|
||||
assert.throws(function () { isPrime({}) }, /TypeError: Unexpected type of argument/)
|
||||
})
|
||||
|
||||
it('should work fast for huge values', function () {
|
||||
assert.strictEqual(isPrime(bignumber('2305843009213693951')), true)
|
||||
assert.strictEqual(isPrime(bignumber('230584300921369395')), false)
|
||||
})
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user