diff --git a/README.md b/README.md index 0e82cf8aa..55c652186 100644 --- a/README.md +++ b/README.md @@ -113,10 +113,10 @@ xarray.permutations([1, 2, 3]); | | | | [count] | Count values which satisfy a test. | | [countEach] | Count occurrences of each distinct value. | -| [min] | Find first smallest value. | -| [minEntry] | Find first smallest entry. | -| [max] | Find first largest value. | -| [maxEntry] | Find first largest entry. | +| [minimum] | Find first smallest value. | +| [minimumEntry] | Find first smallest entry. | +| [maximum] | Find first largest value. | +| [maximumEntry] | Find first largest entry. | | [range] | Find smallest and largest values. | | [rangeEntries] | Find smallest and largest entries. | | | | @@ -324,10 +324,10 @@ xarray.permutations([1, 2, 3]); [removePath$]: https://github.com/nodef/extra-array/wiki/removePath$ [count]: https://github.com/nodef/extra-array/wiki/count [countEach]: https://github.com/nodef/extra-array/wiki/countEach -[min]: https://github.com/nodef/extra-array/wiki/min -[minEntry]: https://github.com/nodef/extra-array/wiki/minEntry -[max]: https://github.com/nodef/extra-array/wiki/max -[maxEntry]: https://github.com/nodef/extra-array/wiki/maxEntry +[minimum]: https://github.com/nodef/extra-array/wiki/minimum +[minimumEntry]: https://github.com/nodef/extra-array/wiki/minimumEntry +[maximum]: https://github.com/nodef/extra-array/wiki/maximum +[maximumEntry]: https://github.com/nodef/extra-array/wiki/maximumEntry [range]: https://github.com/nodef/extra-array/wiki/range [rangeEntries]: https://github.com/nodef/extra-array/wiki/rangeEntries [slice]: https://github.com/nodef/extra-array/wiki/slice diff --git a/TODO b/TODO index 580d8dc22..3a3957a93 100644 --- a/TODO +++ b/TODO @@ -1,97 +1,6 @@ -SORTS, IN PAIR - -Instead of mapFn, provide pair array to define sort mapping. -selectionSort, insertionSort, bubbleSort, mergeSort, (are all stable?) -quickSort, shellSort, radixSort, timSort, heapSort -https://en.wikipedia.org/wiki/Sorting_algorithm -https://www.npmjs.com/package/sort-algorithms-js -https://dl.acm.org/doi/10.5555/1778580.1778601 -https://stackoverflow.com/questions/463105/in-place-radix-sort/474040#474040 - - -MINNTH, MAXNTH, SORTEDSLICE (ARRAYVIEW) - -Need only part of sorted array? [bag][nth] ... -Selection algorithm: Quickselect -https://stackoverflow.com/questions/34395680/what-is-the-fastest-way-to-find-nth-biggest-number-of-an-int-array -https://www.geeksforgeeks.org/kth-smallestlargest-element-unsorted-array/ - - PARSE, OTHERS - (fm, fc)? INDEX -VE - moveWithin - - -OLD SORT ALGORITHMS - -/** - * Arrange values in order! - * @param x an array (updated!) - * @param fc compare function (a, b) - * @param fm map function (v, i, x) - * @param fs swap function (x, i, j) - * @returns x | x[i] ≤ x[j] ∀ i ≤ j - */ -function bubbleSort$(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null, fs: SwapFunction | null=null): T[] { - var fc = fc || COMPARE; - var fm = fm || IDENTITY; // TODO: use map function - var fs = fs || swap$; - var X = x.length; - for (var i=0; i 0) fs(x, i, j); - } - return x; -} - - -/** - * Arrange values in order! - * @param x an array (updated!) - * @param fc compare function (a, b) - * @param fm map function (v, i, x) - * @param fs swap function (x, i, j) - * @returns x | x[i] ≤ x[j] ∀ i ≤ j - */ -function selectionSort$(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null, fs: SwapFunction | null=null): T[] { - var fc = fc || COMPARE; - var fm = fm || IDENTITY; // TODO: use map function - var fs = fs || swap$; - var X = x.length; - for (var i=0; i 0) l = j; - fs(x, i, l); - } - return x; -} - - -// TODO: Check if this is correct! -/** - * Arrange values in order! - * @param x an array (updated!) - * @param fc compare function (a, b) - * @param fm map function (v, i, x) - * @param fs swap function (x, i, j) - * @returns x | x[i] ≤ x[j] ∀ i ≤ j - */ -function insertionSortOld$(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null, fs: SwapFunction | null=null): T[] { - var fc = fc || COMPARE; - var fm = fm || IDENTITY; // TODO: use map function - var fs = fs || swap$; // TODO: use swap function - var X = x.length; - for (var i=X-2; i>=0; --i) { - var xv = x[i], mv = x[i]; - for (var j=i+1; j(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): T { +export function minimum(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): T { var i = searchMinimumValue(x, fc, fm); return x[i]; } +export {minimum as min}; /** @@ -686,10 +687,11 @@ export function min(x: T[], fc: CompareFunction | null=null, fm: Ma * @param fm map function (v, i, x) * @returns [min_index, min_value] */ -export function minEntry(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): [number, T] { +export function minimumEntry(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): [number, T] { var i = searchMinimumValue(x, fc, fm); return [i, x[i]]; } +export {minimumEntry as minEntry}; /** @@ -699,10 +701,11 @@ export function minEntry(x: T[], fc: CompareFunction | null=null, f * @param fm map function (v, i, x) * @returns v | v ≥ vᵢ; vᵢ ∈ x */ -export function max(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): T { +export function maximum(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): T { var i = searchMaximumValue(x, fc, fm); return x[i]; } +export {maximum as max}; /** @@ -712,10 +715,11 @@ export function max(x: T[], fc: CompareFunction | null=null, fm: Ma * @param fm map function (v, i, x) * @returns [max_index, max_value] */ -export function maxEntry(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): [number, T] { +export function maximumEntry(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): [number, T] { var i = searchMaximumValue(x, fc, fm); return [i, x[i]]; } +export {maximumEntry as maxEntry}; /** @@ -753,6 +757,62 @@ export function rangeEntries(x: T[], fc: CompareFunction | null=nul } return [[mi, mv], [ni, nv]]; } + + +/** + * Find smallest values. + * @param x an array + * @param n number of values + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns n smallest values in ascending order + */ +export function minimums(x: T[], n: number, fc: CompareFunction | null=null, fm: MapFunction | null=null): T[] { + var is = searchMinimumValues(x, n, fc, fm); + return is.map(i => x[i]); +} + + +/** + * Find smallest entries. + * @param x an array + * @param n number of values + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns n smallest entries in ascending order + */ +export function minimumEntries(x: T[], n: number, fc: CompareFunction | null=null, fm: MapFunction | null=null): [number, T][] { + var is = searchMinimumValues(x, n, fc, fm); + return is.map(i => [i, x[i]]); +} + + +/** + * Find largest values. + * @param x an array + * @param n number of values + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns n largest values in descending order + */ +export function maximums(x: T[], n: number, fc: CompareFunction | null=null, fm: MapFunction | null=null): T[] { + var is = searchMaximumValues(x, n, fc, fm); + return is.map(i => x[i]); +} + + +/** + * Find largest entries. + * @param x an array + * @param n number of values + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns n largest entries in descending order + */ +export function maximumEntries(x: T[], n: number, fc: CompareFunction | null=null, fm: MapFunction | null=null): [number, T][] { + var is = searchMaximumValues(x, n, fc, fm); + return is.map(i => [i, x[i]]); +} // #endregion @@ -1498,6 +1558,52 @@ export function searchMaximumValue(x: T[], fc: CompareFunction | nu } +/** + * Find indices of minimum values. + * @param x an array + * @param n number of values + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns indices of minimum values in ascending order + */ +export function searchMinimumValues(x: T[], n: number, fc: CompareFunction | null=null, fm: MapFunction | null=null): number[] { + var fc = fc || COMPARE; + var fm = fm || IDENTITY; + var X = x.length; + // Create a max heap of indices. + var IH = Math.min(n, X); + var ih = fromRange(0, IH); + buildMaxHeap$(ih, 0, IH, fc, i => fm(x[i], i, x), swapRaw$); + var wr = fm(x[ih[0]], ih[0], x); + // Search for minimum values, and update heap. + for (var i=n; i= 0) continue; + ih[0] = i; + maxHeapify$(ih, 0, IH, 0, fc, i => fm(x[i], i, x), swapRaw$); + var wr = fm(x[ih[0]], ih[0], x); + } + // Sort max heap in ascending order. + ih.sort((i, j) => fc(fm(x[i], i, x), fm(x[j], j, x))); + return ih; +} + + +/** + * Find indices of maximum values. + * @param x an array + * @param n number of values + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns indices of maximum values in descending order + */ +export function searchMaximumValues(x: T[], n: number, fc: CompareFunction | null=null, fm: MapFunction | null=null): number[] { + var fc = fc || COMPARE; + var fd = (a: T|U, b: T|U) => -fc(a, b); + return searchMinimumValues(x, n, fd, fm); +} + + /** * Find first index of an unsorted value. * @param x an array @@ -3272,8 +3378,8 @@ function buildReverseMinHeap$(x: T[], i: number, I: number, fc: CompareF */ function reverseMinHeapify$(x: T[], i: number, I: number, r: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): void { var s = r; // Index of smallest value - var lt = 2*r - I; // Reverse of lt = 2*r+1 - var rt = lt - 1; // Reverse of rt = 2*r+2 + var lt = 2*r - I; // Left child, reverse of lt = 2*r+1 + var rt = lt - 1; // Right child, reverse of rt = 2*r+2 if (lt>=i && fc(fm(x[lt], lt, x), fm(x[s], s, x)) < 0) s = lt; // Left child is smaller? if (rt>=i && fc(fm(x[rt], rt, x), fm(x[s], s, x)) < 0) s = rt; // Right child is smaller? if (s !== r) { // Smallest is not root? @@ -3283,6 +3389,36 @@ function reverseMinHeapify$(x: T[], i: number, I: number, r: number, fc: } +// Build a max-heap, where root node is the smallest and placed at the beginning. +function buildMaxHeap$(x: T[], i: number, I: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): void { + for (var r=i+Math.floor((I-i)/2)-1; r>=i; --r) + maxHeapify$(x, i, I, r, fc, fm, fs); +} + + +/** + * Max-heapify values, such that root node is the largest and placed at the beginning. + * @param x an array (updated!) + * @param i start index + * @param I end index (exclusive) + * @param r root index + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @param fs swap function (x, i, j) + */ +function maxHeapify$(x: T[], i: number, I: number, r: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): void { + var s = r; // Index of largest value + var lt = 2*r - i + 1; // Left child, like lt = 2*r+1 + var rt = lt + 1; // Right child, like rt = 2*r+2 + if (lt 0) s = lt; // Left child is larger? + if (rt 0) s = rt; // Right child is larger? + if (s !== r) { // Largest is not root? + fs(x, s, r); // Swap root with largest + maxHeapify$(x, i, I, s, fc, fm, fs); // Rebuild heap + } +} + + /** * Partially arrange values in order! * @param x an array (updated!) diff --git a/tests/index.test.ts b/tests/index.test.ts index 480c55646..1d2b4c8ff 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -76,6 +76,7 @@ import { searchValue, searchValueRight, searchValueAll, + searchMinimumValues, searchInfix, searchInfixRight, searchInfixAll, @@ -983,6 +984,13 @@ test("searchValueAll", () => { }); +test("searchMinimumValues", () => { + var x = [2, 5, 3, 1, 4]; + var a = searchMinimumValues(x, 3); + expect(a).toStrictEqual([3, 0, 2]); +}); + + test("searchInfix", () => { var x = [1, 2, 3, 4]; var y = [2, 3]; diff --git a/wiki b/wiki index 1706a1f2f..9d06898b8 160000 --- a/wiki +++ b/wiki @@ -1 +1 @@ -Subproject commit 1706a1f2fa91905fb0ffd79900a065aee3ea20a7 +Subproject commit 9d06898b8e64747cc0fb083b3197c186dea5b254