From dfa606a10867a345928f18d8686f8b5554e2df63 Mon Sep 17 00:00:00 2001 From: lee-naish Date: Mon, 27 Jan 2025 17:25:14 +1100 Subject: [PATCH] straightRadixSort various improvements + minor one for msort_arr_nat --- .../controllers/straightRadixSort.js | 103 +++++++++++++----- src/algorithms/pseudocode/msort_arr_nat.js | 2 +- .../pseudocode/straightRadixSort.js | 53 ++++----- 3 files changed, 104 insertions(+), 54 deletions(-) diff --git a/src/algorithms/controllers/straightRadixSort.js b/src/algorithms/controllers/straightRadixSort.js index a0fac4b3..16f33254 100644 --- a/src/algorithms/controllers/straightRadixSort.js +++ b/src/algorithms/controllers/straightRadixSort.js @@ -1,3 +1,4 @@ +// Some animation deleted, some added; could delete some commented out stuff import ArrayTracer from '../../components/DataStructures/Array/Array1DTracer'; import MaskTracer from '../../components/DataStructures/Mask/MaskTracer'; import { areExpanded } from './collapseChunkPlugin'; @@ -8,10 +9,10 @@ const SRS_BOOKMARKS = { radix_sort: 1, max_number: 2, counting_sort_for_loop: 3, - counting_sort: 4, - count_nums: 5, - cumulative_sum: 6, - populate_array: 7, + // counting_sort: 4, // no longer used + // count_nums: 5, // no longer used + // cumulative_sum: 6, // no longer used + // populate_array: 7, // no longer used populate_for_loop: 8, insert_into_array: 9, copy: 10, @@ -20,6 +21,9 @@ const SRS_BOOKMARKS = { add_count_for_loop: 13, cum_sum_for_loop: 14, add_cum_sum: 15, + zero_counts: 16, + get_digit1: 17, + dec_count: 18, }; const isCountExpanded = () => { @@ -63,6 +67,8 @@ const bitsAtIndex = (num, index, bits) => { return num >> (index * bits) & ((1 << bits) - 1); }; +// could check that isArray is defined and avoid some of the +// isCountExpanded() checks elsewhere const setArray = (visArray, array) => { visArray.set(array, 'straightRadixSort'); }; @@ -83,11 +89,21 @@ export default { const count = Array.apply(null, Array(1 << bits)).map(() => 0); let lastBit = -1; - chunker.add(SRS_BOOKMARKS.count_nums); + chunker.add(SRS_BOOKMARKS.zero_counts, + (vis, count) => { + if (isCountExpanded()) { + setArray(vis.countArray, count); + } + }, + [count] + ); for (let i = 0; i < n; i++) { chunker.add(SRS_BOOKMARKS.add_count_for_loop, - (vis, i, lastBit) => { + (vis, i, lastBit, count) => { + if (isCountExpanded()) { + setArray(vis.countArray, count); + } if (i !== 0) { unhighlight(vis.array, i - 1); } @@ -99,7 +115,7 @@ export default { highlight(vis.array, i); updateBinary(vis, A[i]); }, - [i, lastBit] + [i, lastBit, count] ); const bit = bitsAtIndex(A[i], k, bits); @@ -118,6 +134,7 @@ export default { lastBit = bit; } +/* chunker.add(SRS_BOOKMARKS.cumulative_sum, (vis, n, lastBit) => { unhighlight(vis.array, n - 1); @@ -128,15 +145,23 @@ export default { }, [n, lastBit] ); +*/ for (let i = 1; i < count.length; i++) { chunker.add(SRS_BOOKMARKS.cum_sum_for_loop, - (vis, i) => { - if (i === 1 && isCountExpanded()) { - highlight(vis.countArray, 0); + (vis, i, n, lastBit) => { + if (isCountExpanded()) { + if (i === 1) { + unhighlight(vis.array, n - 1); + } else + unhighlight(vis.countArray, i-1, false); + if (i === 1 && isCountExpanded()) { + unhighlight(vis.countArray, lastBit); + } + highlight(vis.countArray, i); } }, - [i] + [i, n, lastBit] ); count[i] += count[i - 1]; @@ -145,15 +170,16 @@ export default { (vis, count, i) => { if (isCountExpanded()) { setArray(vis.countArray, count); - highlight(vis.countArray, i); + highlight(vis.countArray, i, false); } }, [count, i] ) } - const sortedA = Array.apply(null, Array(n)).map(() => 0); + const sortedA = Array.apply(null, Array(n)).map(() => undefined); +/* chunker.add(SRS_BOOKMARKS.populate_array, (vis, countLength) => { if (isCountExpanded()) { @@ -162,31 +188,54 @@ export default { }, [count.length] ); +*/ - chunker.add(SRS_BOOKMARKS.populate_for_loop); + // chunker.add(SRS_BOOKMARKS.populate_for_loop); let bit; for (let i = n - 1; i >= 0; i--) { const num = A[i]; + chunker.add(SRS_BOOKMARKS.populate_for_loop, + (vis, num, i, bit, count, sortedA) => { + if (i === n - 1) { + if (isCountExpanded()) { + unhighlight(vis.countArray, count.length - 1, false); + } + } else { + unhighlight(vis.array, i + 1); + if (isCountExpanded()) { + setArray(vis.countArray, count); + setArray(vis.tempArray, sortedA); + unhighlight(vis.countArray, bit); + unhighlight(vis.tempArray, count[bit]); + } + } + updateBinary(vis, num); + highlight(vis.array, i); + }, + [num, i, bit, count, sortedA] + ); bit = bitsAtIndex(num, k, bits); count[bit]--; + chunker.add(SRS_BOOKMARKS.dec_count, + (vis, num, i, bit, count, sortedA) => { + + if (isCountExpanded()) { + setArray(vis.countArray, count); + highlight(vis.countArray, bit); + } + }, + [num, i, bit, count, sortedA] + ); sortedA[count[bit]] = num; chunker.add(SRS_BOOKMARKS.insert_into_array, (vis, num, i, bit, count, sortedA) => { - if (i !== n - 1) { - unhighlight(vis.array, i + 1); - } if (isCountExpanded()) { - setArray(vis.countArray, count); setArray(vis.tempArray, sortedA); - highlight(vis.countArray, bit); highlight(vis.tempArray, count[bit]); } - - updateBinary(vis, num); - highlight(vis.array, i); }, [num, i, bit, count, sortedA] ); @@ -249,7 +298,7 @@ export default { A = countingSort(A, k, n, BITS); - chunker.add(SRS_BOOKMARKS.counting_sort); + // chunker.add(SRS_BOOKMARKS.counting_sort); } chunker.add(SRS_BOOKMARKS.done, @@ -273,7 +322,7 @@ export function initVisualisers() { order: 0, }, array: { - instance: new ArrayTracer('array', null, 'Array view', { arrayItemMagnitudes: false }), + instance: new ArrayTracer('array', null, 'Array A', { arrayItemMagnitudes: false }), order: 1, }, countArray: { @@ -281,7 +330,7 @@ export function initVisualisers() { order: 1, }, tempArray: { - instance: new ArrayTracer('tempArray', null, 'Temp array', { arrayItemMagnitudes: false }), + instance: new ArrayTracer('tempArray', null, 'Temp array B', { arrayItemMagnitudes: false }), order: 1, }, }; @@ -292,9 +341,9 @@ export function initVisualisers() { order: 0, }, array: { - instance: new ArrayTracer('array', null, 'Array view', { arrayItemMagnitudes: true }), + instance: new ArrayTracer('array', null, 'Array A', { arrayItemMagnitudes: true }), order: 1, }, }; } -} \ No newline at end of file +} diff --git a/src/algorithms/pseudocode/msort_arr_nat.js b/src/algorithms/pseudocode/msort_arr_nat.js index 15994995..dad465d5 100644 --- a/src/algorithms/pseudocode/msort_arr_nat.js +++ b/src/algorithms/pseudocode/msort_arr_nat.js @@ -32,7 +32,7 @@ MergeAll \\Expl{ We compute mid to get the longest sequence where A[left] <= A[left+1] <= ... <= A[mid]. \\Expl} - find the second run, A[mid+1..right] it could be empty \\Ref SecondRun + find the second run, A[mid+1..right] (possibly empty) \\Ref SecondRun \\Expl{ We compute right to get the longest sequence where A[mid+1] <= A[mid+2] <= ... <= A[right]. If mid = size this will be empty. \\Expl} diff --git a/src/algorithms/pseudocode/straightRadixSort.js b/src/algorithms/pseudocode/straightRadixSort.js index 25b38687..12fc62fa 100644 --- a/src/algorithms/pseudocode/straightRadixSort.js +++ b/src/algorithms/pseudocode/straightRadixSort.js @@ -20,14 +20,18 @@ would be good for the counts array. \\Code{ Main -Radixsort(A, n) // Sort array A[1]..A[n] in ascending order. \\B 1 +Radixsort(A, n) // Sort array A[0]..A[n-1] in ascending order. \\B 1 +\\Expl{ Note that all arrays start at index zero. +\\Expl} Find maximum number of "digits" used in the data \\B 2 \\Expl{ This depends on the radix (base) we use to view the data. + Here we use radix 4 for illustration (the digits are 0-3 and use + two bits; we show the binary representation of the keys and the + mask used to extract the current digit). We could use radix 10 (decimal digits), radix 2 - (binary) or anything else. Here we use radix 4 for illustration - (the digits are 0-3 and use two bits). Radix 256 (one byte) is a - better choice in practice and we can set the maximum to be the + (binary) or anything else. Radix 256 (one byte) is a + good choice in practice and we can set the maximum to be the word size rather than scanning all the input data as we do here. \\Expl} @@ -48,36 +52,36 @@ Radixsort(A, n) // Sort array A[1]..A[n] in ascending order. \\B 1 \\Code{ Countingsort -// Countingsort(A, k, n) \\B 4 -// Count number of 1s and 0s in B -Array C <- counts of each kth digit value \\Ref CountNums -\\Expl{ We count the number of occurrences of each digit value (0-3 - here) in the kth digits of the data. +Array Count <- counts of each kth digit value \\Ref CountNums +\\Expl{ We count the number of occurrences of each digit value (here 0-3 + or binary 00-11) in the kth digits of the data. \\Expl} Cumulatively sum digit value counts \\Ref CumSum \\Expl{ For each digit value, we compute the count for that digit value plus all smaller digit values. This allows us to determine where the - last occurrence of each digit value will appear in the sorted array. + last occurrence of each digit value will appear in the sorted array + (the counts are one greater than the index because we start at index 0). \\Expl} -Populate temporary array B with sorted numbers \\Ref Populate +Array B <- sorted numbers \\Ref Populate \\Expl{ We copy the data to temporary array B, using the digit - value counts to determine where each element is copied to. + value counts to determine where each element is copied to, so the result + is sorted on this digit. \\Expl} Copy B back to A \\B 10 -\\Expl{ Array A is now sorted on digit k and all smaller digits - (because the smaller digits were sorted previously and counting +\\Expl{ Array A is now sorted on digit k and all less significant digits + (because the less significant digits were sorted previously and counting sort is stable). \\Expl} \\Code} \\Code{ CountNums -// Put counts of each kth digit value in array C \\B 5 -initialise array C to all zeros \\B 16 +initialise array Count to all zeros \\B 16 for num in A \\B 13 \\In{ digit <- kth digit value in num \\B 17 - \\Expl{ To extract the kth digit we can use div and mod operations. + \\Expl{ The digit is the two highlighted bits in the binary representation. + To extract the kth digit we can use div and mod operations. If the radix is a power of two we can use bit-wise operations (right shift and bit-wise and) instead. \\Expl} @@ -86,27 +90,23 @@ for num in A \\B 13 highlighted, and the digit value 0-3 (maybe the latter can be done by just highlighting B[digit] instead). \\Note} - C[digit] <- C[digit]+1 \\B 12 + Count[digit] <- Count[digit]+1 \\B 12 \\In} \\Code} \\Code{ CumSum -// Cumulatively sum counts \\B 6 -\\Note{ Best remove this comment line and move bookmark -\\Note} for i = 1 to maximum digit value \\B 14 \\Expl{ We must scan left to right. The count for digit 0 remains unchanged. \\Expl} \\In{ - B[i] = B[i-1] + B[i] \\B 15 + Count[i] = Count[i-1] + Count[i] \\B 15 \\In} \\Code} \\Code{ Populate -// Populate new array C with sorted numbers \\B 7 for each num in A in reverse order \\B 8 \\Expl{ We go from right to left so that we preserve the order of numbers with the same digit. @@ -114,7 +114,8 @@ for each num in A in reverse order \\B 8 \\Expl} \\In{ digit <- kth digit value in num \\B 19 - \\Expl{ To extract the kth digit value we can use div and mod operations. + \\Expl{ The digit is the two highlighted bits in the binary representation. + To extract the kth digit we can use div and mod operations. If the radix is a power of two we can use bit-wise operations (right shift and bit-wise and) instead. \\Expl} @@ -123,8 +124,8 @@ for each num in A in reverse order \\B 8 highlighted, and the digit value 0-3 (maybe the latter can be done by just highlighting B[digit] instead). \\Note} - B[digit] = B[digit]-1 \\B 18 - C[B[digit]] = num \\B 9 + Count[digit] = Count[digit]-1 \\B 18 + B[Count[digit]] = num \\B 9 \\In} \\Code}