diff --git a/README.md b/README.md index 8a4040f6..6c74d91f 100644 --- a/README.md +++ b/README.md @@ -33,10 +33,9 @@ * [Power Set](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/power-set) * [Primality Test](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/primality-test) (trial division) * [Euclidean Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/euclidean-algorithm) - calculate the greatest common divisor (GCD) + * [Fisher–Yates Shuffle](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/fisher-yates) - random permutation of a finite sequence * Collatz Conjecture algorithm * Extended Euclidean algorithm - * Find Divisors - * Fisher-Yates * Greatest Difference * Least Common Multiple * Newton's square diff --git a/src/algorithms/math/fisher-yates/README.md b/src/algorithms/math/fisher-yates/README.md new file mode 100644 index 00000000..36278d34 --- /dev/null +++ b/src/algorithms/math/fisher-yates/README.md @@ -0,0 +1,15 @@ +# Fisher–Yates shuffle + +The Fisher–Yates shuffle is an algorithm for generating a random +permutation of a finite sequence—in plain terms, the algorithm +shuffles the sequence. The algorithm effectively puts all the +elements into a hat; it continually determines the next element +by randomly drawing an element from the hat until no elements +remain. The algorithm produces an unbiased permutation: every +permutation is equally likely. The modern version of the +algorithm is efficient: it takes time proportional to the +number of items being shuffled and shuffles them in place. + +## References + +[Wikipedia](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle) diff --git a/src/algorithms/math/fisher-yates/__test__/fisherYates.test.js b/src/algorithms/math/fisher-yates/__test__/fisherYates.test.js new file mode 100644 index 00000000..f43a7b7c --- /dev/null +++ b/src/algorithms/math/fisher-yates/__test__/fisherYates.test.js @@ -0,0 +1,19 @@ +import fisherYates from '../fisherYates'; +import { sortedArr } from '../../../sorting/SortTester'; +import QuickSort from '../../../sorting/quick-sort/QuickSort'; + +describe('fisherYates', () => { + it('should shuffle small arrays', () => { + expect(fisherYates([])).toEqual([]); + expect(fisherYates([1])).toEqual([1]); + }); + + it('should shuffle array randomly', () => { + const shuffledArray = fisherYates(sortedArr); + const sorter = new QuickSort(); + + expect(shuffledArray.length).toBe(sortedArr.length); + expect(shuffledArray).not.toEqual(sortedArr); + expect(sorter.sort(shuffledArray)).toEqual(sortedArr); + }); +}); diff --git a/src/algorithms/math/fisher-yates/fisherYates.js b/src/algorithms/math/fisher-yates/fisherYates.js new file mode 100644 index 00000000..98cb2c31 --- /dev/null +++ b/src/algorithms/math/fisher-yates/fisherYates.js @@ -0,0 +1,21 @@ +/** + * @param {*[]} originalArray + * @return {*[]} + */ +export default function fisherYates(originalArray) { + // Clone array from preventing original array from modification (for testing purpose). + const array = originalArray.slice(0); + + if (array.length <= 1) { + return array; + } + + for (let i = (array.length - 1); i > 0; i -= 1) { + const randomIndex = Math.floor(Math.random() * (i + 1)); + const tmp = array[randomIndex]; + array[randomIndex] = array[i]; + array[i] = tmp; + } + + return array; +}