forked from trekhleb/javascript-algorithms
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
147 additions
and
102 deletions.
There are no files selected for viewing
This file contains 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 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 |
---|---|---|
@@ -1,34 +1,34 @@ | ||
# Prime Factors | ||
|
||
Prime factors are basically those prime numbers which multiply together to give the orignal number. For ex: 39 will have prime factors as 3 and 13 which are also prime numbers. Another example is 15 whose prime factors are 3 and 5. | ||
**Prime number** is a whole number greater than `1` that **cannot** be made by multiplying other whole numbers. The first few prime numbers are: `2`, `3`, `5`, `7`, `11`, `13`, `17`, `19` and so on. | ||
|
||
#### Method for finding the prime factors and their count accurately | ||
If we **can** make it by multiplying other whole numbers it is a **Composite Number**. | ||
|
||
The approach is to basically keep on dividing the natural number 'n' by indexes from i = 2 to i = n by prime indexes only. This is ensured by an 'if' check. Then value of 'n' keeps on overriding by (n/i). | ||
The time complexity till now is O(n) in worst case since the loop run from index i = 2 to i = n even when no index 'i' is left to be divided by 'n' other than n itself. This time complexity can be reduced to O(sqrt(n)) from O(n). This optimisation is acheivable when loop is ran from i = 2 to i = sqrt(n). Now, we go only till O(sqrt(n)) because when 'i' becomes greater than sqrt(n), we now have the confirmation there is no index 'i' left which can divide 'n' completely other than n itself. | ||
 | ||
|
||
##### Optimised Time Complexity: O(sqrt(n)) | ||
_Image source: [Math is Fun](https://www.mathsisfun.com/prime-factorization.html)_ | ||
|
||
**Prime factors** are those [prime numbers](https://en.wikipedia.org/wiki/Prime_number) which multiply together to give the original number. For example `39` will have prime factors of `3` and `13` which are also prime numbers. Another example is `15` whose prime factors are `3` and `5`. | ||
|
||
#### Hardy-Ramanujan formula for approximate calculation of prime-factor count | ||
 | ||
|
||
In 1917, a theorem was formulated by G.H Hardy and Srinivasa Ramanujan which approximately tells the total count of distinct prime factors of most 'n' natural numbers. | ||
The fomula is given by ln(ln(n)). | ||
_Image source: [Math is Fun](https://www.mathsisfun.com/prime-factorization.html)_ | ||
|
||
#### Code Explaiation | ||
## Finding the prime factors and their count accurately | ||
|
||
There are on 4 functions used: | ||
The approach is to keep on dividing the natural number `n` by indexes from `i = 2` to `i = n` (by prime indexes only). The value of `n` is being overridden by `(n / i)` on each iteration. | ||
|
||
- getPrimeFactors : returns array containing all distinct prime factors for given input n. | ||
The time complexity till now is `O(n)` in the worst case scenario since the loop runs from index `i = 2` to `i = n`. This time complexity can be reduced from `O(n)` to `O(sqrt(n))`. The optimisation is achievable when loop runs from `i = 2` to `i = sqrt(n)`. Now, we go only till `O(sqrt(n))` because when `i` becomes greater than `sqrt(n)`, we have the confirmation that there is no index `i` left which can divide `n` completely other than `n` itself. | ||
|
||
- getPrimeFactorsCount: returns accurate total count of distinct prime factors of given input n. | ||
## Hardy-Ramanujan formula for approximate calculation of prime-factor count | ||
|
||
- hardyRamanujanApprox: returns approximate total count of distinct prime factors of given input n using Hardy-Ramanujan formula. | ||
In 1917, a theorem was formulated by G.H Hardy and Srinivasa Ramanujan which states that the normal order of the number `ω(n)` of distinct prime factors of a number `n` is `log(log(n))`. | ||
|
||
- errorPercent : returns %age of error in approximation using formula to that of accurate result. The formula used is: **[Modulus(accurate_val - approximate_val) / accurate_val ] * 100**. This shows deviation from accurate result. | ||
|
||
Roughly speaking, this means that most numbers have about this number of distinct prime factors. | ||
|
||
## References | ||
|
||
- [Youtube](https://www.youtube.com/watch?v=6PDtgHhpCHo) | ||
- [Wikipedia](https://en.wikipedia.org/wiki/Hardy%E2%80%93Ramanujan_theorem) | ||
- [Prime numbers on Math is Fun](https://www.mathsisfun.com/prime-factorization.html) | ||
- [Prime numbers on Wikipedia](https://en.wikipedia.org/wiki/Prime_number) | ||
- [Hardy–Ramanujan theorem on Wikipedia](https://en.wikipedia.org/wiki/Hardy%E2%80%93Ramanujan_theorem) | ||
- [Prime factorization of a number on Youtube](https://www.youtube.com/watch?v=6PDtgHhpCHo&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=82) |
87 changes: 87 additions & 0 deletions
87
src/algorithms/math/prime-factors/__test__/primeFactors.test.js
This file contains 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,87 @@ | ||
import { | ||
primeFactors, | ||
hardyRamanujan, | ||
} from '../primeFactors'; | ||
|
||
/** | ||
* Calculates the error between exact and approximate prime factor counts. | ||
* @param {number} exactCount | ||
* @param {number} approximateCount | ||
* @returns {number} - approximation error (percentage). | ||
*/ | ||
function approximationError(exactCount, approximateCount) { | ||
return (Math.abs((exactCount - approximateCount) / exactCount) * 100); | ||
} | ||
|
||
describe('primeFactors', () => { | ||
it('should find prime factors', () => { | ||
expect(primeFactors(1)).toEqual([]); | ||
expect(primeFactors(2)).toEqual([2]); | ||
expect(primeFactors(3)).toEqual([3]); | ||
expect(primeFactors(4)).toEqual([2, 2]); | ||
expect(primeFactors(14)).toEqual([2, 7]); | ||
expect(primeFactors(40)).toEqual([2, 2, 2, 5]); | ||
expect(primeFactors(54)).toEqual([2, 3, 3, 3]); | ||
expect(primeFactors(100)).toEqual([2, 2, 5, 5]); | ||
expect(primeFactors(156)).toEqual([2, 2, 3, 13]); | ||
expect(primeFactors(273)).toEqual([3, 7, 13]); | ||
expect(primeFactors(300)).toEqual([2, 2, 3, 5, 5]); | ||
expect(primeFactors(980)).toEqual([2, 2, 5, 7, 7]); | ||
expect(primeFactors(1000)).toEqual([2, 2, 2, 5, 5, 5]); | ||
expect(primeFactors(52734)).toEqual([2, 3, 11, 17, 47]); | ||
expect(primeFactors(343434)).toEqual([2, 3, 7, 13, 17, 37]); | ||
expect(primeFactors(456745)).toEqual([5, 167, 547]); | ||
expect(primeFactors(510510)).toEqual([2, 3, 5, 7, 11, 13, 17]); | ||
expect(primeFactors(8735463)).toEqual([3, 3, 11, 88237]); | ||
expect(primeFactors(873452453)).toEqual([149, 1637, 3581]); | ||
}); | ||
|
||
it('should give approximate prime factors count using Hardy-Ramanujan theorem', () => { | ||
expect(hardyRamanujan(2)).toBeCloseTo(-0.366, 2); | ||
expect(hardyRamanujan(4)).toBeCloseTo(0.326, 2); | ||
expect(hardyRamanujan(40)).toBeCloseTo(1.305, 2); | ||
expect(hardyRamanujan(156)).toBeCloseTo(1.6193, 2); | ||
expect(hardyRamanujan(980)).toBeCloseTo(1.929, 2); | ||
expect(hardyRamanujan(52734)).toBeCloseTo(2.386, 2); | ||
expect(hardyRamanujan(343434)).toBeCloseTo(2.545, 2); | ||
expect(hardyRamanujan(456745)).toBeCloseTo(2.567, 2); | ||
expect(hardyRamanujan(510510)).toBeCloseTo(2.575, 2); | ||
expect(hardyRamanujan(8735463)).toBeCloseTo(2.771, 2); | ||
expect(hardyRamanujan(873452453)).toBeCloseTo(3.024, 2); | ||
}); | ||
|
||
it('should give correct deviation between exact and approx counts', () => { | ||
expect(approximationError(primeFactors(2).length, hardyRamanujan(2))) | ||
.toBeCloseTo(136.651, 2); | ||
|
||
expect(approximationError(primeFactors(4).length, hardyRamanujan(2))) | ||
.toBeCloseTo(118.325, 2); | ||
|
||
expect(approximationError(primeFactors(40).length, hardyRamanujan(2))) | ||
.toBeCloseTo(109.162, 2); | ||
|
||
expect(approximationError(primeFactors(156).length, hardyRamanujan(2))) | ||
.toBeCloseTo(109.162, 2); | ||
|
||
expect(approximationError(primeFactors(980).length, hardyRamanujan(2))) | ||
.toBeCloseTo(107.330, 2); | ||
|
||
expect(approximationError(primeFactors(52734).length, hardyRamanujan(52734))) | ||
.toBeCloseTo(52.274, 2); | ||
|
||
expect(approximationError(primeFactors(343434).length, hardyRamanujan(343434))) | ||
.toBeCloseTo(57.578, 2); | ||
|
||
expect(approximationError(primeFactors(456745).length, hardyRamanujan(456745))) | ||
.toBeCloseTo(14.420, 2); | ||
|
||
expect(approximationError(primeFactors(510510).length, hardyRamanujan(510510))) | ||
.toBeCloseTo(63.201, 2); | ||
|
||
expect(approximationError(primeFactors(8735463).length, hardyRamanujan(8735463))) | ||
.toBeCloseTo(30.712, 2); | ||
|
||
expect(approximationError(primeFactors(873452453).length, hardyRamanujan(873452453))) | ||
.toBeCloseTo(0.823, 2); | ||
}); | ||
}); |
40 changes: 0 additions & 40 deletions
40
src/algorithms/math/prime-factors/__test__/primefactors.test.js
This file was deleted.
Oops, something went wrong.
This file contains 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,42 @@ | ||
/** | ||
* Finds prime factors of a number. | ||
* | ||
* @param {number} n - the number that is going to be split into prime factors. | ||
* @returns {number[]} - array of prime factors. | ||
*/ | ||
export function primeFactors(n) { | ||
// Clone n to avoid function arguments override. | ||
let nn = n; | ||
|
||
// Array that stores the all the prime factors. | ||
const factors = []; | ||
|
||
// Running the loop till sqrt(n) instead of n to optimise time complexity from O(n) to O(sqrt(n)). | ||
for (let factor = 2; factor <= Math.sqrt(nn); factor += 1) { | ||
// Check that factor divides n without a reminder. | ||
while (nn % factor === 0) { | ||
// Overriding the value of n. | ||
nn /= factor; | ||
// Saving the factor. | ||
factors.push(factor); | ||
} | ||
} | ||
|
||
// The ultimate reminder should be a last prime factor, | ||
// unless it is not 1 (since 1 is not a prime number). | ||
if (nn !== 1) { | ||
factors.push(nn); | ||
} | ||
|
||
return factors; | ||
} | ||
|
||
/** | ||
* Hardy-Ramanujan approximation of prime factors count. | ||
* | ||
* @param {number} n | ||
* @returns {number} - approximate number of prime factors. | ||
*/ | ||
export function hardyRamanujan(n) { | ||
return Math.log(Math.log(n)); | ||
} |
This file was deleted.
Oops, something went wrong.