-
-
Notifications
You must be signed in to change notification settings - Fork 5.8k
Add Fermat Primality Test #790
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
raklaptudirm
merged 5 commits into
TheAlgorithms:master
from
arthurvergacas:Fermat-Primality-Test
Oct 20, 2021
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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) | ||
| }) | ||
| }) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.