mirror of
https://github.com/TheAlgorithms/JavaScript.git
synced 2025-07-05 08:16:50 +08:00
chore: Merge pull request #790 from arthurvergacas/Fermat-Primality-Test
Add Fermat Primality Test
This commit is contained in:
@ -157,6 +157,7 @@
|
||||
* [Factorial](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/Factorial.js)
|
||||
* [Factors](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/Factors.js)
|
||||
* [FareyApproximation](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/FareyApproximation.js)
|
||||
* [FermatPrimalityTest](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/FermatPrimalityTest.js)
|
||||
* [Fibonacci](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/Fibonacci.js)
|
||||
* [FindHcf](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/FindHcf.js)
|
||||
* [FindLcm](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/FindLcm.js)
|
||||
|
97
Maths/FermatPrimalityTest.js
Normal file
97
Maths/FermatPrimalityTest.js
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* The Fermat primality test is a probabilistic test to determine whether a number
|
||||
* is a probable prime.
|
||||
*
|
||||
* It relies on Fermat's Little Theorem, which states that if p is prime and a
|
||||
* is not divisible by p, then
|
||||
*
|
||||
* a^(p - 1) % p = 1
|
||||
*
|
||||
* However, there are certain numbers (so called Fermat Liars) that screw things up;
|
||||
* if a is one of these liars the equation will hold even though p is composite.
|
||||
*
|
||||
* But not everything is lost! It's been proven that at least half of all integers
|
||||
* aren't Fermat Liars (these ones called Fermat Witnesses). Thus, if we keep
|
||||
* testing the primality with random integers, we can achieve higher reliability.
|
||||
*
|
||||
* The interesting about all of this is that since half of all integers are
|
||||
* Fermat Witnesses, the precision gets really high really fast! Suppose that we
|
||||
* make the test 50 times: the chance of getting only Fermat Liars in all runs is
|
||||
*
|
||||
* 1 / 2^50 = 8.8 * 10^-16 (a pretty small number)
|
||||
*
|
||||
* For comparison, the probability of a cosmic ray causing an error to your
|
||||
* infalible program is around 1.4 * 10^-15. An order of magnitude below!
|
||||
*
|
||||
* But because nothing is perfect, there's a major flaw to this algorithm, and
|
||||
* the cause are the so called Carmichael Numbers. These are composite numbers n
|
||||
* that hold the equality from Fermat's Little Theorem for every a < n (excluding
|
||||
* is factors). In other words, if we are trying to determine if a Carmichael Number
|
||||
* is prime or not, the chances of getting a wrong answer are pretty high! Because
|
||||
* of that, the Fermat Primality Test is not used is serious applications. :(
|
||||
*
|
||||
* You can find more about the Fermat primality test and its flaws here:
|
||||
* https://en.wikipedia.org/wiki/Fermat_primality_test
|
||||
*
|
||||
* And about Carmichael Numbers here:
|
||||
* https://primes.utm.edu/glossary/xpage/CarmichaelNumber.html
|
||||
*/
|
||||
|
||||
/**
|
||||
* Faster exponentiation that capitalize on the fact that we are only interested
|
||||
* in the modulus of the exponentiation.
|
||||
*
|
||||
* Find out more about it here: https://en.wikipedia.org/wiki/Modular_exponentiation
|
||||
*
|
||||
* @param {number} base
|
||||
* @param {number} exponent
|
||||
* @param {number} modulus
|
||||
*/
|
||||
const modularExponentiation = (base, exponent, modulus) => {
|
||||
if (modulus === 1) return 0 // after all, any x % 1 = 0
|
||||
|
||||
let result = 1
|
||||
base %= modulus // make sure that base < modulus
|
||||
|
||||
while (exponent > 0) {
|
||||
// if exponent is odd, multiply the result by the base
|
||||
if (exponent % 2 === 1) {
|
||||
result = (result * base) % modulus
|
||||
exponent--
|
||||
} else {
|
||||
exponent = exponent / 2 // exponent is even for sure
|
||||
base = (base * base) % modulus
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if a given number n is prime or not.
|
||||
*
|
||||
* @param {number} n The number to check for primality
|
||||
* @param {number} numberOfIterations The number of times to apply Fermat's Little Theorem
|
||||
* @returns True if prime, false otherwise
|
||||
*/
|
||||
const fermatPrimeCheck = (n, numberOfIterations = 50) => {
|
||||
// first check for edge cases
|
||||
if (n <= 1 || n === 4) return false
|
||||
if (n <= 3) return true // 2 and 3 are included here
|
||||
|
||||
for (let i = 0; i < numberOfIterations; i++) {
|
||||
// pick a random number a, with 2 <= a < n - 2
|
||||
const randomNumber = Math.floor(Math.random() * (n - 2) + 2)
|
||||
|
||||
// if a^(n - 1) % n is different than 1, n is composite
|
||||
if (modularExponentiation(randomNumber, n - 1, n) !== 1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// if we arrived here without finding a Fermat Witness, this is almost guaranteed
|
||||
// to be a prime number (or a Carmichael number, if you are unlucky)
|
||||
return true
|
||||
}
|
||||
|
||||
export { modularExponentiation, fermatPrimeCheck }
|
19
Maths/test/FermatPrimalityTest.test.js
Normal file
19
Maths/test/FermatPrimalityTest.test.js
Normal file
@ -0,0 +1,19 @@
|
||||
import { fermatPrimeCheck, modularExponentiation } from '../FermatPrimalityTest'
|
||||
|
||||
describe('modularExponentiation', () => {
|
||||
it('should give the correct output for all exponentiations', () => {
|
||||
expect(modularExponentiation(38, 220, 221)).toBe(1)
|
||||
expect(modularExponentiation(24, 220, 221)).toBe(81)
|
||||
})
|
||||
})
|
||||
|
||||
describe('fermatPrimeCheck', () => {
|
||||
it('should give the correct output for prime and composite numbers', () => {
|
||||
expect(fermatPrimeCheck(2, 35)).toBe(true)
|
||||
expect(fermatPrimeCheck(10, 30)).toBe(false)
|
||||
expect(fermatPrimeCheck(94286167)).toBe(true)
|
||||
expect(fermatPrimeCheck(83165867)).toBe(true)
|
||||
expect(fermatPrimeCheck(13268774)).toBe(false)
|
||||
expect(fermatPrimeCheck(13233852)).toBe(false)
|
||||
})
|
||||
})
|
Reference in New Issue
Block a user