diff --git a/src/algorithms/math/karatsuba-multiplication/README.md b/src/algorithms/math/karatsuba-multiplication/README.md new file mode 100644 index 0000000000..94ea93085b --- /dev/null +++ b/src/algorithms/math/karatsuba-multiplication/README.md @@ -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) diff --git a/src/algorithms/math/karatsuba-multiplication/__test__/karatsuba.test.js b/src/algorithms/math/karatsuba-multiplication/__test__/karatsuba.test.js new file mode 100644 index 0000000000..7a4979d698 --- /dev/null +++ b/src/algorithms/math/karatsuba-multiplication/__test__/karatsuba.test.js @@ -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); + }); +}); diff --git a/src/algorithms/math/karatsuba-multiplication/karatsuba.js b/src/algorithms/math/karatsuba-multiplication/karatsuba.js new file mode 100644 index 0000000000..f0da5194c7 --- /dev/null +++ b/src/algorithms/math/karatsuba-multiplication/karatsuba.js @@ -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; +} diff --git a/src/algorithms/sorting/bubble-sort/BubbleSort.js b/src/algorithms/sorting/bubble-sort/BubbleSort.js index 4d3f156995..d78ebcbdc1 100644 --- a/src/algorithms/sorting/bubble-sort/BubbleSort.js +++ b/src/algorithms/sorting/bubble-sort/BubbleSort.js @@ -19,9 +19,7 @@ export default class BubbleSort extends Sort { // Swap elements if they are in wrong order. if (this.comparator.lessThan(array[j + 1], array[j])) { - const tmp = array[j + 1]; - array[j + 1] = array[j]; - array[j] = tmp; + [array[j], array[j + 1]] = [array[j + 1], array[j]]; // Register the swap. swapped = true; diff --git a/src/algorithms/sorting/counting-sort/__test__/CountingSort.test.js b/src/algorithms/sorting/counting-sort/__test__/CountingSort.test.js index be15d03233..9bf5d5c513 100644 --- a/src/algorithms/sorting/counting-sort/__test__/CountingSort.test.js +++ b/src/algorithms/sorting/counting-sort/__test__/CountingSort.test.js @@ -27,14 +27,10 @@ describe('CountingSort', () => { const sorter = new CountingSort({ visitingCallback }); // Detect biggest number in array in prior. - const biggestElement = notSortedArr.reduce((accumulator, element) => { - return element > accumulator ? element : accumulator; - }, 0); + const biggestElement = Math.max(...notSortedArr); // Detect smallest number in array in prior. - const smallestElement = notSortedArr.reduce((accumulator, element) => { - return element < accumulator ? element : accumulator; - }, 0); + const smallestElement = Math.min(...notSortedArr); const sortedArray = sorter.sort(notSortedArr, smallestElement, biggestElement); diff --git a/src/data-structures/linked-list/README.zh-CN.md b/src/data-structures/linked-list/README.zh-CN.md index e135f7b190..55ec7ad604 100644 --- a/src/data-structures/linked-list/README.zh-CN.md +++ b/src/data-structures/linked-list/README.zh-CN.md @@ -28,7 +28,20 @@ Add(value) end if end Add ``` - + +``` +Prepend(value) + Pre: value is the value to add to the list + Post: value has been placed at the head of the list + n ← node(value) + n.next ← head + head ← n + if tail = ø + tail ← n + end +end Prepend +``` + ### 搜索 ```text @@ -67,10 +80,10 @@ Remove(head, value) end if return true end if - while n.next = ø and n.next.value = value + while n.next != ø and n.next.value != value n ← n.next end while - if n.next = ø + if n.next != ø if n.next = tail tail ← n end if @@ -88,7 +101,7 @@ Traverse(head) Pre: head is the head node in the list Post: the items in the list have been traversed n ← head - while n = 0 + while n != 0 yield n.value n ← n.next end while @@ -101,11 +114,11 @@ end Traverse ReverseTraversal(head, tail) Pre: head and tail belong to the same list Post: the items in the list have been traversed in reverse order - if tail = ø + if tail != ø curr ← tail - while curr = head + while curr != head prev ← head - while prev.next = curr + while prev.next != curr prev ← prev.next end while yield curr.value diff --git a/src/data-structures/tree/fenwick-tree/FenwickTree.js b/src/data-structures/tree/fenwick-tree/FenwickTree.js index 7bfb84264f..9146fa138c 100644 --- a/src/data-structures/tree/fenwick-tree/FenwickTree.js +++ b/src/data-structures/tree/fenwick-tree/FenwickTree.js @@ -60,7 +60,7 @@ export default class FenwickTree { */ queryRange(leftIndex, rightIndex) { if (leftIndex > rightIndex) { - throw new Error('Left index can not be greater then right one'); + throw new Error('Left index can not be greater than right one'); } if (leftIndex === 1) {