Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit a99ba4f

Browse files
chore: Merge pull request #790 from arthurvergacas/Fermat-Primality-Test
Add Fermat Primality Test
2 parents 0705fa5 + fa1abf4 commit a99ba4f

File tree

3 files changed

+117
-0
lines changed

3 files changed

+117
-0
lines changed

‎DIRECTORY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@
157157
* [Factorial](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/Factorial.js)
158158
* [Factors](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/Factors.js)
159159
* [FareyApproximation](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/FareyApproximation.js)
160+
* [FermatPrimalityTest](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/FermatPrimalityTest.js)
160161
* [Fibonacci](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/Fibonacci.js)
161162
* [FindHcf](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/FindHcf.js)
162163
* [FindLcm](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/FindLcm.js)

‎Maths/FermatPrimalityTest.js

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* The Fermat primality test is a probabilistic test to determine whether a number
3+
* is a probable prime.
4+
*
5+
* It relies on Fermat's Little Theorem, which states that if p is prime and a
6+
* is not divisible by p, then
7+
*
8+
* a^(p - 1) % p = 1
9+
*
10+
* However, there are certain numbers (so called Fermat Liars) that screw things up;
11+
* if a is one of these liars the equation will hold even though p is composite.
12+
*
13+
* But not everything is lost! It's been proven that at least half of all integers
14+
* aren't Fermat Liars (these ones called Fermat Witnesses). Thus, if we keep
15+
* testing the primality with random integers, we can achieve higher reliability.
16+
*
17+
* The interesting about all of this is that since half of all integers are
18+
* Fermat Witnesses, the precision gets really high really fast! Suppose that we
19+
* make the test 50 times: the chance of getting only Fermat Liars in all runs is
20+
*
21+
* 1 / 2^50 = 8.8 * 10^-16 (a pretty small number)
22+
*
23+
* For comparison, the probability of a cosmic ray causing an error to your
24+
* infalible program is around 1.4 * 10^-15. An order of magnitude below!
25+
*
26+
* But because nothing is perfect, there's a major flaw to this algorithm, and
27+
* the cause are the so called Carmichael Numbers. These are composite numbers n
28+
* that hold the equality from Fermat's Little Theorem for every a < n (excluding
29+
* is factors). In other words, if we are trying to determine if a Carmichael Number
30+
* is prime or not, the chances of getting a wrong answer are pretty high! Because
31+
* of that, the Fermat Primality Test is not used is serious applications. :(
32+
*
33+
* You can find more about the Fermat primality test and its flaws here:
34+
* https://en.wikipedia.org/wiki/Fermat_primality_test
35+
*
36+
* And about Carmichael Numbers here:
37+
* https://primes.utm.edu/glossary/xpage/CarmichaelNumber.html
38+
*/
39+
40+
/**
41+
* Faster exponentiation that capitalize on the fact that we are only interested
42+
* in the modulus of the exponentiation.
43+
*
44+
* Find out more about it here: https://en.wikipedia.org/wiki/Modular_exponentiation
45+
*
46+
* @param {number} base
47+
* @param {number} exponent
48+
* @param {number} modulus
49+
*/
50+
const modularExponentiation = (base, exponent, modulus) => {
51+
if (modulus === 1) return 0 // after all, any x % 1 = 0
52+
53+
let result = 1
54+
base %= modulus // make sure that base < modulus
55+
56+
while (exponent > 0) {
57+
// if exponent is odd, multiply the result by the base
58+
if (exponent % 2 === 1) {
59+
result = (result * base) % modulus
60+
exponent--
61+
} else {
62+
exponent = exponent / 2 // exponent is even for sure
63+
base = (base * base) % modulus
64+
}
65+
}
66+
67+
return result
68+
}
69+
70+
/**
71+
* Test if a given number n is prime or not.
72+
*
73+
* @param {number} n The number to check for primality
74+
* @param {number} numberOfIterations The number of times to apply Fermat's Little Theorem
75+
* @returns True if prime, false otherwise
76+
*/
77+
const fermatPrimeCheck = (n, numberOfIterations = 50) => {
78+
// first check for edge cases
79+
if (n <= 1 || n === 4) return false
80+
if (n <= 3) return true // 2 and 3 are included here
81+
82+
for (let i = 0; i < numberOfIterations; i++) {
83+
// pick a random number a, with 2 <= a < n - 2
84+
const randomNumber = Math.floor(Math.random() * (n - 2) + 2)
85+
86+
// if a^(n - 1) % n is different than 1, n is composite
87+
if (modularExponentiation(randomNumber, n - 1, n) !== 1) {
88+
return false
89+
}
90+
}
91+
92+
// if we arrived here without finding a Fermat Witness, this is almost guaranteed
93+
// to be a prime number (or a Carmichael number, if you are unlucky)
94+
return true
95+
}
96+
97+
export { modularExponentiation, fermatPrimeCheck }

‎Maths/test/FermatPrimalityTest.test.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { fermatPrimeCheck, modularExponentiation } from '../FermatPrimalityTest'
2+
3+
describe('modularExponentiation', () => {
4+
it('should give the correct output for all exponentiations', () => {
5+
expect(modularExponentiation(38, 220, 221)).toBe(1)
6+
expect(modularExponentiation(24, 220, 221)).toBe(81)
7+
})
8+
})
9+
10+
describe('fermatPrimeCheck', () => {
11+
it('should give the correct output for prime and composite numbers', () => {
12+
expect(fermatPrimeCheck(2, 35)).toBe(true)
13+
expect(fermatPrimeCheck(10, 30)).toBe(false)
14+
expect(fermatPrimeCheck(94286167)).toBe(true)
15+
expect(fermatPrimeCheck(83165867)).toBe(true)
16+
expect(fermatPrimeCheck(13268774)).toBe(false)
17+
expect(fermatPrimeCheck(13233852)).toBe(false)
18+
})
19+
})

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /