mirror of
https://github.moeyy.xyz/https://github.com/trekhleb/javascript-algorithms.git
synced 2024-09-20 07:43:04 +08:00
Merge c4eead424a
into ca3d16dcce
This commit is contained in:
commit
d1388f8dc0
41
src/algorithms/math/karatsuba-multiplication/README.md
Normal file
41
src/algorithms/math/karatsuba-multiplication/README.md
Normal file
@ -0,0 +1,41 @@
|
||||
# Karatsuba Multiplication
|
||||
|
||||
Karatsuba is a fast multiplication algorithm discovered by Anatoly Karatsuba in 1960. Given two n-digit numbers, the "grade-school" method of long multiplication has a time complexity of O(n<sup>2</sup>), whereas the karatsuba algorithm has a time complexity of O(n<sup>1.59</sup>).
|
||||
|
||||
## Recursive Formula
|
||||
|
||||
```
|
||||
x = 1234
|
||||
y = 5678
|
||||
|
||||
karatsuba(x, y)
|
||||
```
|
||||
|
||||
1. Split each number into numbers with half as many digits
|
||||
```
|
||||
a = 12
|
||||
b = 34
|
||||
|
||||
c = 56
|
||||
d = 78
|
||||
```
|
||||
|
||||
2. Compute 3 subexpressions from the smaller numbers
|
||||
- `ac = a * c`
|
||||
- `bd = b * d`
|
||||
- `abcd = (a + b) * (c + d)`
|
||||
|
||||
3. Combine subexpressions to calculate the product
|
||||
```
|
||||
A = ac * 10000
|
||||
B = (abcd - ac - bd) * 100
|
||||
C = bd
|
||||
|
||||
x * y = A + B + C
|
||||
```
|
||||
|
||||
_**Note:**_ *The karatsuba algorithm can be applied recursively to calculate each product in the subexpressions.* (`a * c = karatsuba(a, c)`*). When the numbers get smaller than some arbitrary threshold, they are multiplied in the traditional way.*
|
||||
|
||||
## References
|
||||
[Stanford Algorithms (YouTube)](https://www.youtube.com/watch?v=JCbZayFr9RE)
|
||||
[Wikipedia](https://en.wikipedia.org/wiki/Karatsuba_algorithm)
|
@ -0,0 +1,14 @@
|
||||
import karatsuba from '../karatsuba';
|
||||
|
||||
describe('karatsuba multiplication', () => {
|
||||
it('should multiply simple numbers correctly', () => {
|
||||
expect(karatsuba(0, 37)).toEqual(0);
|
||||
expect(karatsuba(1, 8)).toEqual(8);
|
||||
expect(karatsuba(5, 6)).toEqual(30);
|
||||
});
|
||||
|
||||
it('should multiply larger numbers correctly', () => {
|
||||
expect(karatsuba(1234, 5678)).toEqual(7006652);
|
||||
expect(karatsuba(9182734, 726354172)).toEqual(6669917151266248);
|
||||
});
|
||||
});
|
68
src/algorithms/math/karatsuba-multiplication/karatsuba.js
Normal file
68
src/algorithms/math/karatsuba-multiplication/karatsuba.js
Normal file
@ -0,0 +1,68 @@
|
||||
/**
|
||||
*
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @return {number}
|
||||
*/
|
||||
export default function karatsuba(x, y) {
|
||||
// BASE CASE:
|
||||
// if numbers are sufficiently small,
|
||||
// multiply them together in the traditional way
|
||||
if (x < 10 || y < 10) {
|
||||
return x * y;
|
||||
}
|
||||
|
||||
// SCALE FACTOR:
|
||||
// scaleFactor is used to split the numbers
|
||||
// into smaller numbers for recursion.
|
||||
// when combining the subexpressions back
|
||||
// together, the scaleFactor is used to
|
||||
// recreate the magnitude of the original numbers
|
||||
const minDigits = Math.min(
|
||||
String(x).length,
|
||||
String(y).length,
|
||||
);
|
||||
const scaleFactor = 10 ** Math.floor(minDigits / 2);
|
||||
|
||||
// PARAMETER COMPONENTS:
|
||||
// a b are the two components of x
|
||||
// c d are the two components of y
|
||||
//
|
||||
// e.g.
|
||||
// x = 1234 -> a = 12, b = 34
|
||||
// y = 5678 -> c = 56, d = 78
|
||||
|
||||
// example of component computations:
|
||||
// x = 1234, y = 5678
|
||||
// scaleFactor = 100
|
||||
|
||||
// a = floor(1234 / 100) = floor(12.34) = 12
|
||||
const a = Math.floor(x / scaleFactor);
|
||||
|
||||
// b = 1234 - (12 * 100) = 1234 - 1200 = 34
|
||||
const b = x - (a * scaleFactor);
|
||||
|
||||
// c = floor(5678 / 100) = floor(56.78) = 56
|
||||
const c = Math.floor(y / scaleFactor);
|
||||
|
||||
// d = 5678 - (56 * 100) = 5678 - 5600 = 78
|
||||
const d = y - (c * scaleFactor);
|
||||
|
||||
// COMPUTE SUBEXPRESSIONS:
|
||||
// since a + b is less than x, and c + d is less than y
|
||||
// the recursion is guaranteed to reach the base case
|
||||
const ac = karatsuba(a, c);
|
||||
const bd = karatsuba(b, d);
|
||||
const abcd = karatsuba(a + b, c + d);
|
||||
|
||||
// COMBINE SUBEXPRESSIONS:
|
||||
// since the scaleFactor was used to
|
||||
// reduce the size of the components,
|
||||
// the scaleFactor must be applied in reverse
|
||||
// to reconstruct the magnitude of the original components
|
||||
const A = ac * (scaleFactor ** 2);
|
||||
const B = (abcd - ac - bd) * scaleFactor;
|
||||
const C = bd;
|
||||
|
||||
return A + B + C;
|
||||
}
|
Loading…
Reference in New Issue
Block a user