From 8587e6215bd7c6b5d82c8dc0203e2ce0e146fa73 Mon Sep 17 00:00:00 2001 From: "KNECHT MAXIME (MXA)" Date: Fri, 9 Jan 2026 00:06:43 +0100 Subject: [PATCH] Add Fisher-Yates shuffle algorithm --- README.md | 26 +++++++++++++++++++++++--- algorithms/fisherYatesShuffle.js | 10 ++++++++++ index.d.ts | 8 ++++++++ index.js | 4 +++- test/compareAll.test.js | 1 + test/fisherYatesShuffle.test.js | 32 ++++++++++++++++++++++++++++++++ 6 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 algorithms/fisherYatesShuffle.js create mode 100644 test/fisherYatesShuffle.test.js diff --git a/README.md b/README.md index dcfade5..d51f023 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![npm version](https://img.shields.io/npm/v/algorith)](https://www.npmjs.com/package/algorith) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![Tests](https://img.shields.io/badge/tests-114%20passing-brightgreen)](./test/) +[![Tests](https://img.shields.io/badge/tests-115%20passing-brightgreen)](./test/) > Collection complète d'algorithmes de similarité textuelle et moteur de génération aléatoire avancé @@ -21,7 +21,8 @@ const { hamming, compareAll, RandomEngine, - AutocompleteEngine + AutocompleteEngine, + fisherYatesShuffle } = require('algorith'); // Comparaison de similarité @@ -53,6 +54,11 @@ console.log(rng.randomWord()); // "bakaru" const autocomplete = new AutocompleteEngine({ language: 'fr' }); autocomplete.addWords(['javascript', 'java', 'python']); console.log(autocomplete.autocomplete('java')); // ['java', 'javascript'] + +// Mélange Fisher-Yates +const numbers = [1, 2, 3, 4, 5]; +const shuffled = fisherYatesShuffle(numbers); +console.log(shuffled); // [3, 1, 5, 2, 4] ``` ## 📚 API Documentation @@ -514,6 +520,20 @@ rng.fade(0.5); // Fonction de lissage rng.lerp(0, 10, 0.5); // Interpolation linéaire → 5 ``` +### 🔀 Mélange Fisher-Yates + +Mélange un tableau en utilisant l'algorithme de Fisher-Yates. + +```javascript +const { fisherYatesShuffle } = require('algorith'); + +const items = ['a', 'b', 'c', 'd']; +const shuffled = fisherYatesShuffle(items); + +console.log(items); // ['a', 'b', 'c', 'd'] (non modifié) +console.log(shuffled); // Mélange aléatoire +``` + ## 🎯 Exemples d'Usage ### Détection de Doublons @@ -607,7 +627,7 @@ const map = generateTerrain(100, 100); ## 🧪 Tests -Le module inclut 114 tests complets : +Le module inclut 115 tests complets : ```bash # Exécuter tous les tests diff --git a/algorithms/fisherYatesShuffle.js b/algorithms/fisherYatesShuffle.js new file mode 100644 index 0000000..ffc6a96 --- /dev/null +++ b/algorithms/fisherYatesShuffle.js @@ -0,0 +1,10 @@ +module.exports = function fisherYatesShuffle(array, random = Math.random) { + const result = array.slice(); + + for (let i = result.length - 1; i > 0; i--) { + const j = Math.floor(random() * (i + 1)); + [result[i], result[j]] = [result[j], result[i]]; + } + + return result; +}; diff --git a/index.d.ts b/index.d.ts index c33fd3a..9980a71 100644 --- a/index.d.ts +++ b/index.d.ts @@ -82,6 +82,14 @@ export function diceCoefficient(a: string, b: string): number; */ export function trigramScore(a: string, b: string): number; +/** + * Shuffles an array using the Fisher-Yates algorithm + * @param array Input array + * @param random Optional random generator (default: Math.random) + * @returns New shuffled array + */ +export function fisherYatesShuffle(array: T[], random?: () => number): T[]; + /** * Generates the Soundex code for a string with multilingual support * @param s Input string diff --git a/index.js b/index.js index 6bdff8f..1bc438f 100644 --- a/index.js +++ b/index.js @@ -9,6 +9,7 @@ const cosineSimilarity = require('./algorithms/cosineSimilarity'); const soundex = require('./algorithms/soundex'); const RandomEngine = require('./algorithms/RandomEngine'); const AutocompleteEngine = require('./algorithms/autocomplete'); +const fisherYatesShuffle = require('./algorithms/fisherYatesShuffle'); function compareAll(a, b) { return { @@ -35,5 +36,6 @@ module.exports = { soundex, RandomEngine, AutocompleteEngine, + fisherYatesShuffle, compareAll -}; \ No newline at end of file +}; diff --git a/test/compareAll.test.js b/test/compareAll.test.js index 86e089c..2271c4b 100644 --- a/test/compareAll.test.js +++ b/test/compareAll.test.js @@ -12,6 +12,7 @@ describe('Compare All Algorithms', () => { assert(typeof algorithms.jaro === 'function'); assert(typeof algorithms.cosineSimilarity === 'function'); assert(typeof algorithms.RandomEngine === 'function'); // Constructor + assert(typeof algorithms.fisherYatesShuffle === 'function'); assert(typeof algorithms.compareAll === 'function'); }); diff --git a/test/fisherYatesShuffle.test.js b/test/fisherYatesShuffle.test.js new file mode 100644 index 0000000..517c872 --- /dev/null +++ b/test/fisherYatesShuffle.test.js @@ -0,0 +1,32 @@ +const assert = require('assert'); +const fisherYatesShuffle = require('../algorithms/fisherYatesShuffle'); + +describe('Fisher-Yates Shuffle', () => { + it('should return a new array without mutating the original', () => { + const original = [1, 2, 3, 4]; + const shuffled = fisherYatesShuffle(original, () => 0); + + assert.deepStrictEqual(original, [1, 2, 3, 4]); + assert.notStrictEqual(shuffled, original); + }); + + it('should keep the same elements', () => { + const original = [1, 2, 3, 4, 5]; + const shuffled = fisherYatesShuffle(original, () => 0.5); + + assert.deepStrictEqual(shuffled.slice().sort((a, b) => a - b), original); + }); + + it('should produce deterministic output with a custom RNG', () => { + const values = [0.1, 0.7, 0.3]; + let index = 0; + const rng = () => values[index++]; + + const result = fisherYatesShuffle([1, 2, 3, 4], rng); + assert.deepStrictEqual(result, [2, 4, 3, 1]); + }); + + it('should handle empty arrays', () => { + assert.deepStrictEqual(fisherYatesShuffle([], () => 0.5), []); + }); +});