Skip to content
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

Add karatsuba algorithm #225

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions src/algorithms/math/karatsuba-multiplication/README.md
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
@@ -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 changes: 68 additions & 0 deletions src/algorithms/math/karatsuba-multiplication/karatsuba.js
Original file line number Diff line number Diff line change
@@ -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;
}