diff --git a/Hashes/PolynomialHash.js b/Hashes/PolynomialHash.js new file mode 100644 index 0000000000..4475ce9299 --- /dev/null +++ b/Hashes/PolynomialHash.js @@ -0,0 +1,99 @@ +/** + * Polynomial Hash also known as Polynomial rolling hash function. + * + * Polynomial rolling hash function is a hash function that uses only multiplications and additions. + * + * + * NOTE: If two strings are equal, their hash values should also be equal. But the inverse need not be true. + * + * Wikipedia: https://en.wikipedia.org/wiki/Rolling_hash + */ +const DEFAULT_BASE = 37; +const DEFAULT_MODULUS = 101; + +export default class PolynomialHash { + /** + * @param {number} [base] - Base number that is used to create the polynomial. + * @param {number} [modulus] - Modulus number that keeps the hash from overflowing. + */ + constructor({ base = DEFAULT_BASE, modulus = DEFAULT_MODULUS } = {}) { + this.base = base; + this.modulus = modulus; + } + + /** + * Function that creates hash representation of the word. + * + * Time complexity: O(word.length). + * + * @param {string} word - String that needs to be hashed. + * @return {number} + */ + hash(word) { + const charCodes = Array.from(word).map((char) => this.charToNumber(char)); + + let hash = 0; + for (let charIndex = 0; charIndex < charCodes.length; charIndex += 1) { + hash *= this.base; + hash += charCodes[charIndex]; + hash %= this.modulus; + } + + return hash; + } + + /** + * Function that creates hash representation of the word + * based on previous word (shifted by one character left) hash value. + * + * Recalculates the hash representation of a word so that it isn't + * necessary to traverse the whole word again. + * + * Time complexity: O(1). + * + * @param {number} prevHash + * @param {string} prevWord + * @param {string} newWord + * @return {number} + */ + roll(prevHash, prevWord, newWord) { + let hash = prevHash; + + const prevValue = this.charToNumber(prevWord[0]); + const newValue = this.charToNumber(newWord[newWord.length - 1]); + + let prevValueMultiplier = 1; + for (let i = 1; i < prevWord.length; i += 1) { + prevValueMultiplier *= this.base; + prevValueMultiplier %= this.modulus; + } + + hash += this.modulus; + hash -= (prevValue * prevValueMultiplier) % this.modulus; + + hash *= this.base; + hash += newValue; + hash %= this.modulus; + + return hash; + } + + /** + * Converts char to number. + * + * @param {string} char + * @return {number} + */ + charToNumber(char) { + let charCode = char.codePointAt(0); + + // Check if character has surrogate pair. + const surrogate = char.codePointAt(1); + if (surrogate !== undefined) { + const surrogateShift = 2 ** 16; + charCode += surrogate * surrogateShift; + } + + return charCode; + } +} \ No newline at end of file diff --git a/Hashes/test/PolynomialHash.test.js b/Hashes/test/PolynomialHash.test.js new file mode 100644 index 0000000000..4d1c15a043 --- /dev/null +++ b/Hashes/test/PolynomialHash.test.js @@ -0,0 +1,59 @@ +import PolynomialHash from '../PolynomialHash'; + +describe('PolynomialHash', () => { + it('should calculate new hash based on previous one', () => { + const bases = [3, 79, 101, 3251, 13229, 122743, 3583213]; + const mods = [79, 101]; + const frameSizes = [5, 20]; + + // @TODO: Provide Unicode support. + const text = 'Lorem Ipsum is simply dummy text of the printing and ' + + 'typesetting industry. Lorem Ipsum has been the industry\'s standard ' + + 'galley of type and \u{ffff} scrambled it to make a type specimen book. It ' + + 'electronic 耀 typesetting, remaining essentially unchanged. It was ' + // + 'popularised in the \u{20005} \u{20000}1960s with the release of Letraset sheets ' + + 'publishing software like Aldus PageMaker 耀 including versions of Lorem.'; + + // Check hashing for different prime base. + bases.forEach((base) => { + mods.forEach((modulus) => { + const polynomialHash = new PolynomialHash({ base, modulus }); + + // Check hashing for different word lengths. + frameSizes.forEach((frameSize) => { + let previousWord = text.substr(0, frameSize); + let previousHash = polynomialHash.hash(previousWord); + + // Shift frame through the whole text. + for (let frameShift = 1; frameShift < (text.length - frameSize); frameShift += 1) { + const currentWord = text.substr(frameShift, frameSize); + const currentHash = polynomialHash.hash(currentWord); + const currentRollingHash = polynomialHash.roll(previousHash, previousWord, currentWord); + + // Check that rolling hash is the same as directly calculated hash. + expect(currentRollingHash).toBe(currentHash); + + previousWord = currentWord; + previousHash = currentHash; + } + }); + }); + }); + }); + + it('should generate numeric hashed less than 100', () => { + const polynomialHash = new PolynomialHash({ modulus: 100 }); + + expect(polynomialHash.hash('Some long text that is used as a key')).toBe(41); + expect(polynomialHash.hash('Test')).toBe(92); + expect(polynomialHash.hash('a')).toBe(97); + expect(polynomialHash.hash('b')).toBe(98); + expect(polynomialHash.hash('c')).toBe(99); + expect(polynomialHash.hash('d')).toBe(0); + expect(polynomialHash.hash('e')).toBe(1); + expect(polynomialHash.hash('ab')).toBe(87); + + // @TODO: Provide Unicode support. + expect(polynomialHash.hash('\u{20000}')).toBe(92); + }); +}); \ No newline at end of file diff --git a/Sorts/BitonicSort.js b/Sorts/BitonicSort.js new file mode 100644 index 0000000000..9b91db3a3b --- /dev/null +++ b/Sorts/BitonicSort.js @@ -0,0 +1,74 @@ +/* + + * JS program for bitonic sort + * Bitonic Sort is a parallel sorting algorithm that works by dividing the + array into two parts, recursively sorting them, and then merging them in a + specific way. + * more information: https://en.wikipedia.org/wiki/Bitonic_sorter + + */ +function compAndSwap (a, i, j, order) { + if ((a[i]> a[j] && order === 1) || (a[i] < a[j] && order === 0)) { + // Swapping elements + const temp = a[i] + a[i] = a[j] + a[j] = temp + } +} + +// It recursively sorts a bitonic sequence in ascending +// order, if order = 1 +function bitonicMergeArr (a, low, cnt, dir) { + if (cnt> 1) { + const k = parseInt(cnt / 2) + for (let i = low; i < low + k; i++) { compAndSwap(a, i, i + k, dir) } + bitonicMergeArr(a, low, k, dir) + bitonicMergeArr(a, low + k, k, dir) + } +} + +function bitonicSort (a, low, cnt, order) { // (arr 0 arrLen AS-Ds-order) + if (cnt> 1) { + const k = parseInt(cnt / 2) + + // sort in ascending order since order here is 1 + bitonicSort(a, low, k, 1) + + // sort in descending order since order here is 0 + bitonicSort(a, low + k, k, 0) + + // Will merge whole sequence in ascending order + // since dir=1. + bitonicMergeArr(a, low, cnt, order) + } +} + +// Calling of bitonicSort func for sorting the entire array +// of length N in ASCENDING order +// here up=1 for ASCENDING & up=0 for DESCENDING +function sort (a, N, up) { + bitonicSort(a, 0, N, up) +} + +// displaying array +function logArray (arr) { + for (let i = 0; i < arr.length; ++i) { console.log(arr[i] + ' ') } +} + +export { sort } + +// Test Case method +const a = [4, 16, 8, 0, 100] +const up = 1 // change to 0 for Descending +sort(a, a.length, up) +console.log('Sorted array: ') +logArray(a) + +/** Output: + * Sorted array: + 0 + 4 + 8 + 16 + 100 + */ diff --git a/Sorts/test/BitonicSort.test.js b/Sorts/test/BitonicSort.test.js new file mode 100644 index 0000000000..076c3be772 --- /dev/null +++ b/Sorts/test/BitonicSort.test.js @@ -0,0 +1,36 @@ +import {sort} from '../BitonicSort' + +// Suppose 1 Array having elements as power of 2 + var a = [4, 16, 8, 0, 100]; + var up = 1; //change to 0 for Descending + sort(a, a.length, up); + console.log("Sorted array: "); + logArray(a); //[0, 4, 8, 16, 100] + +// Test Case 2 + var a = [-4, 32, -8, 40, 80]; + var up = 1; //change to 0 for Descending + sort(a, a.length, up); + console.log("Sorted array: "); + logArray(a); //[-8, -4, 32, 40, 80] + +// Test Case 3 + var a = [24, 32, 80, 40, 16]; + var up = 0; //change to 1 for Ascending + sort(a, a.length, up); + console.log("Sorted array: "); + logArray(a); //[80, 40, 32, 24, 16] + +// Test Case 4 + var a = [24, 32]; + var up = 0; //change to 0 for Descending + sort(a, a.length, up); + console.log("Sorted array: "); + logArray(a); [32, 24] + +// Test Case 5 + var a = [8]; + var up = 0; //change to 0 for Descending + sort(a, a.length, up); + console.log("Sorted array: "); + logArray(a); [8] \ No newline at end of file

AltStyle によって変換されたページ (->オリジナル) /