Skip to content

Commit f8e6e35

Browse files
committed
REX partition now like QS, exercises
1 parent 4aa155f commit f8e6e35

File tree

6 files changed

+85
-72
lines changed

6 files changed

+85
-72
lines changed

src/algorithms/controllers/MSDRadixSort.js

Lines changed: 40 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,10 @@ const STACK_FRAME_COLOR = {
3434
const VIS_VARIABLE_STRINGS = {
3535
i_left_index: 'i',
3636
j_right_index: 'j',
37+
i_eq_0: 'i==0',
3738
i_gt_n: 'i==n+1',
3839
j_eq_0: 'j==0',
40+
j_gt_n: 'j==n+1',
3941
left: 'left',
4042
right: 'right',
4143
right_eq_0: 'right==0'
@@ -54,7 +56,6 @@ const MSD_BOOKMARKS = {
5456
partition_right: 305,
5557
swap_condition: 309,
5658
swap: 310,
57-
inc_dec: 'inc_dec',
5859
pre_sort_left: 400,
5960
sort_left: 401,
6061
pre_sort_right: 500,
@@ -160,13 +161,6 @@ export default {
160161
// where mid is undefined until after partition
161162
const finished_stack_frames = [];
162163
const real_stack = [];
163-
// refreshStack does lots of work with highlighting etc but is
164-
// called from other functions which are called from other
165-
// function and somewhere along the line one of the functions is
166-
// chunker.add. Often the arguments are not given explicitly -
167-
// it's a mess. We assign the current bookmark to whereAreWe for
168-
// now, to be used in refreshStack. Maybe worth a re-write XXX.
169-
let whereAreWe;
170164

171165
// ----------------------------------------------------------------------------------------------------------------------------
172166
// Define helper functions
@@ -176,51 +170,53 @@ export default {
176170
// This function is the only way information is cached and incremented properly in the while loop
177171
const partitionChunker = (bookmark, i, j, prev_i, prev_j, left, right, depth, arr, mask) => {
178172
assert(bookmark !== undefined); // helps catch bugs early, and trace them in stack
179-
const args_array = [real_stack, finished_stack_frames, i, j, prev_i, prev_j, left, right, depth, whereAreWe, maxIndex, arr, mask]
173+
const args_array = [real_stack, finished_stack_frames, i, j, prev_i, prev_j, left, right, depth, bookmark, maxIndex, arr, mask]
180174
chunker.add(bookmark, refreshStack, args_array, depth)
181175
}
182176

183-
// see comment above on whereAreWe
184177
const refreshStack = (vis, cur_real_stack, cur_finished_stack_frames, cur_i, cur_j, prev_i, prev_j, left, right, cur_depth, whereAreWe, maxIndex, arr, mask) => {
185178
// If we fall off the start/end of the array we just use the
186179
// first/last element and give the actual value of j/i
180+
let cur_i_too_low;
187181
let cur_i_too_high;
188182
let cur_j_too_low;
183+
let cur_j_too_high;
189184
let tmp_j = cur_j; // used to determine context later
190185
if (cur_i === A.length) {
191186
cur_i = undefined;
192187
cur_i_too_high = A.length - 1;
193-
} else {
194-
cur_i_too_high = undefined;
188+
} else if (cur_i === -1) {
189+
cur_i = undefined;
190+
cur_i_too_low = 0;
195191
}
196-
if (cur_j === -1) {
192+
if (cur_j === A.length) {
193+
cur_j = undefined;
194+
cur_j_too_high = A.length - 1;
195+
} else if (cur_j === -1) {
197196
cur_j = undefined;
198197
cur_j_too_low = 0;
199-
} else {
200-
cur_j_too_low = undefined;
201198
}
202199

203200
assert(vis.array);
204201
assert(cur_real_stack && cur_finished_stack_frames);
205202

206-
// XXX This was getting very messy - colors depends a
203+
// This was getting very messy - colors depends a
207204
// lot on where we are and we had a bunch of tricky testing of
208-
// various vars to determine that. Now we use whereAreWe to
209-
// simplify some things at least.
205+
// various vars to determine that. Now we use whereAreWe
206+
// (current bookmark) simplify some things at least.
207+
// XXX prev_i and prev_j are no longer used so could be deleted
208+
// as parameters and elsewhere
210209

211210
// Show the binary representation for the current index
212211
// plus color appropriate element(s)
213-
updateMask(vis, mask) // only needed for start of recursive function
214-
if (maxIndex !== undefined) { // top level call to recursive fn
212+
updateMask(vis, mask) // needed at call/return of recursive function
213+
if (whereAreWe === MSD_BOOKMARKS.rec_function) {
214+
// top level call to recursive fn
215215
unhighlight(vis, maxIndex)
216216
updateBinary(vis, 0)
217217
}
218-
if (cur_i !== undefined && tmp_j === undefined && prev_i !== undefined) {
218+
if (whereAreWe === MSD_BOOKMARKS.pre_sort_left) {
219219
// just before first recursive call
220-
if (i <= right)
221-
vis.array.selectColor(prev_i, partRColor)
222-
if (prev_j >= left) // might have fallen off array
223-
vis.array.selectColor(prev_j, partLColor)
224220
for (let k = cur_i; k <= right; k++)
225221
vis.array.deselect(k);
226222
} else if (whereAreWe === MSD_BOOKMARKS.partition_left) {
@@ -231,31 +227,17 @@ export default {
231227
vis.array.selectColor(cur_i, partRColor)
232228
else {
233229
vis.array.selectColor(cur_i, partLColor);
234-
cur_i++; // show incremented i XXX fix i_too_high
235230
}
236231
}
237-
/*
238-
if (prev_i !== undefined && prev_i !== cur_j) {
239-
let real_i = cur_i;
240-
if (cur_i === undefined)
241-
real_i = right + 1;
242-
for (let k = prev_i; k < real_i && k <= right; k++)
243-
vis.array.selectColor(k, partLColor)
244-
if (cur_i !== undefined && cur_i <= right && (arr[cur_i] >> mask & 1) === 1)
245-
vis.array.selectColor(cur_i, partRColor)
246-
}
247-
if (arr && cur_i !== undefined)
248-
updateBinary(vis, arr[cur_i])
249-
*/
250232
} else if (whereAreWe === MSD_BOOKMARKS.partition_right) {
251233
// note j can fall off LHS of array...
252-
if (prev_j !== undefined && prev_j !== cur_i)
253-
for (let k = prev_j; k >= cur_j && k >= cur_i; k--)
254-
vis.array.selectColor(k, partRColor)
255-
if (prev_j !== undefined && cur_j !== undefined && cur_j > cur_i) {
256-
vis.array.selectColor(cur_j, partLColor);
257-
// XXX probably best avoid updateBinary at swap
258-
updateBinary(vis, arr[cur_j])
234+
if (cur_j !== undefined && cur_j >= left ) {
235+
updateBinary(vis, arr[cur_j]);
236+
if ((arr[cur_j] >> mask & 1) === 0)
237+
vis.array.selectColor(cur_j, partLColor)
238+
else {
239+
vis.array.selectColor(cur_j, partRColor);
240+
}
259241
}
260242
}
261243

@@ -274,11 +256,13 @@ export default {
274256
}
275257
if (isPartitionExpanded() || isRecursionExpanded()) {
276258
assignVariable(vis, VIS_VARIABLE_STRINGS.i_left_index, cur_i);
259+
assignVariable(vis, VIS_VARIABLE_STRINGS.i_eq_0, cur_i_too_low);
277260
assignVariable(vis, VIS_VARIABLE_STRINGS.i_gt_n, cur_i_too_high);
278261
}
279262
if (isPartitionExpanded()) {
280263
assignVariable(vis, VIS_VARIABLE_STRINGS.j_right_index, cur_j);
281264
assignVariable(vis, VIS_VARIABLE_STRINGS.j_eq_0, cur_j_too_low);
265+
assignVariable(vis, VIS_VARIABLE_STRINGS.j_gt_n, cur_j_too_high);
282266
}
283267
};
284268

@@ -351,8 +335,8 @@ export default {
351335
let DELAY_POPPER_SWAP = 700;
352336

353337
const partition = (arr, left, right, mask, depth) => {
354-
i = left
355-
j = right
338+
i = left-1
339+
j = right+1
356340

357341
const partitionChunkerWrapper = (bookmark) => {
358342
partitionChunker(bookmark, i, j, prev_i, prev_j, left, right, depth, arr, mask)
@@ -371,8 +355,6 @@ cur_i, cur_j, cur_depth, A) => {
371355

372356
vis.array.swapElements(_n1, _n2);
373357
refreshStack(vis, cur_real_stack, cur_finished_stack_frames, cur_i, cur_j, prev_i, prev_j, left, right, cur_depth, false, undefined, A, mask)
374-
vis.array.selectColor(_n1, partLColor);
375-
vis.array.selectColor(_n2, partRColor);
376358
// redo poppers: swapping the elements keeps the
377359
// contents of the poppers correct but the position needs
378360
// to change. The documentation suggests update()
@@ -393,35 +375,30 @@ arr],
393375
depth);
394376
}
395377

396-
partitionChunkerWrapper(MSD_BOOKMARKS.set_i)
378+
partitionChunkerWrapper(MSD_BOOKMARKS.set_i);
397379
// partitionChunkerWrapper(MSD_BOOKMARKS.set_j)
398380
while (i < j) {
399381
prev_i = i; // save prev value for unhighlighting
400382
prev_j = j;
401383
// Build the left group until it reaches the mask (find the big element)
402-
// leftCheck = true
403-
whereAreWe = MSD_BOOKMARKS.partition_left;
404-
while (i <= j && (arr[i] >> mask & 1) === 0) {
405-
partitionChunkerWrapper(MSD_BOOKMARKS.partition_left)
384+
i++;
385+
partitionChunkerWrapper(MSD_BOOKMARKS.partition_left)
386+
while (i < j && (arr[i] >> mask & 1) === 0) {
406387
i++
388+
partitionChunkerWrapper(MSD_BOOKMARKS.partition_left)
407389
}
408-
partitionChunkerWrapper(MSD_BOOKMARKS.partition_left)
409390
// Build the right group until it fails the mask (find the small element)
410-
// leftCheck = false
411-
whereAreWe = MSD_BOOKMARKS.partition_right;
391+
j--;
392+
partitionChunkerWrapper(MSD_BOOKMARKS.partition_right)
412393
while (j > i && (arr[j] >> mask & 1) === 1) {
413-
partitionChunkerWrapper(MSD_BOOKMARKS.partition_right)
414394
j--
395+
partitionChunkerWrapper(MSD_BOOKMARKS.partition_right)
415396
}
416-
partitionChunkerWrapper(MSD_BOOKMARKS.partition_right)
417397

418398
// Swap if the bigger element is not in the right place
419399
if (i < j) {
420400
partitionChunkerWrapper(MSD_BOOKMARKS.swap_condition)
421401
swapAction(MSD_BOOKMARKS.swap, i, j)
422-
i++;
423-
j--;
424-
partitionChunkerWrapper(MSD_BOOKMARKS.inc_dec)
425402
} else {
426403
// about to return i from partition. We update the "mid" of
427404
// the partition on the stack here so it is displayed at the

src/algorithms/explanations/MSDRadixSortExp.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,16 @@ mask bit are put at the right (similar to partition in quicksort).
3434
indices for the sub-arrays boundaries and and the mask shifted one bit
3535
position to the right.
3636

37-
### 3. Partitioning Based on Current Bit
38-
* The indices ```i``` and ```j```, are set at the left and right of the array segment.
39-
* These indices move towards each other, adjusting positions based on the
40-
mask bit of each key:
37+
### 3. Partitioning Based on Current Mask Bit
38+
* The indices ```i``` and ```j```, are set at the left and right of the
39+
array segment, respectively.
40+
* These indices scan towards each other, adjusting positions based on the mask bit of each key:
4141
* Increment ```i``` until an element with a 1 as the mask bit is reached
4242
* Decrement ```j``` until an element with a 0 as the mask bit is reached
4343

44-
* If the two indices have not met/crossed, perform a swap to ensure that 0s are placed on the left and 1s on the right.
44+
* If the two indices have met/crossed, stop. Otherwise, swap the
45+
elements (so the 0 element is on the left and the 1 element is on the
46+
right) and continue scanning as described above.
4547

4648
## Complexity
4749
### Time Complexity

src/algorithms/extra-info/MSDRadixSortInfo.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,8 @@ Sort)
2020

2121
[G4GLink]: https://www.geeksforgeeks.org/msd-most-significant-digit-radix-sort/
2222

23+
## Exercises/Exploration
2324

25+
The coding of partition used in AIA is based on the coding in quicksort.
26+
Compare and contrast the two. What other approaches to coding partition
27+
could be used?

src/algorithms/extra-info/QSInfo.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,17 @@ Geeks for Geeks Link: [**Quicksort**][G4GLink]
2020

2121
[G4GLink]: https://www.geeksforgeeks.org/quick-sort/
2222

23+
## Exercises/Exploration
24+
25+
Look into some of the history of quicksort. The basic idea is relatively
26+
simple and was invented long ago. However, it has had a *huge* amount
27+
of work done on it over the years to refine it in many ways - far more
28+
than any other relatively simple algorithm.
29+
30+
The coding of partition used in AIA goes back to some early versions of
31+
quicksort where in order to maximize efficiency, the "inner loop(s)" of
32+
the algorithm (incrementing or decrementing the array index and testing
33+
an array element) was written so as to minimise the number of instructions
34+
executed by the hardware. Hardware has changed a lot since then! Look
35+
into some more recent efficient ways of coding partition.
36+

src/algorithms/extra-info/QSM3Info.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,18 @@ Geeks for Geeks Link: [**Quick Sort - Median of Three**][G4GLink]
2020

2121
[G4GLink]: https://www.geeksforgeeks.org/quick-sort/
2222

23+
## Exercises/Exploration
24+
25+
Look into some of the history of quicksort. The basic idea is relatively
26+
simple and was invented long ago. However, it has had a *huge* amount
27+
of work done on it over the years to refine it in many ways (median
28+
of three partitioning is just one of these) - far more than any other
29+
relatively simple algorithm. What are some of the other refinements?
30+
31+
The coding of partition used in AIA goes back to some early versions of
32+
quicksort where in order to maximize efficiency, the "inner loop(s)" of
33+
the algorithm (incrementing or decrementing the array index and testing
34+
an array element) was written so as to minimise the number of instructions
35+
executed by the hardware. Hardware has changed a lot since then! Look
36+
into some more recent efficient ways of coding partition.
37+

src/algorithms/pseudocode/MSDRadixSort.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,12 @@ RexsortRecursive(A, i, right, mask/2) \\B 501
7070
7171
\\Code{
7272
Partition
73-
i,j <- left,right \\B 301
73+
i,j <- left-1,right+1 \\B 301
7474
\\Expl{ i scans from left to right stopping at "large" elements
7575
(with "1" as the mask bit) and j scans from right to left
7676
stopping at "small" elements (with "0" as the mask bit).
77+
The scanning does a pre-increment of i (and pre-decrement of j) before
78+
testing the array element.
7779
\\Expl}
7880
while i < j \\B 303
7981
\\Expl{ When the indices meet/cross, all the large elements at the left of
@@ -86,13 +88,13 @@ while i < j \\B 303
8688
\\Expl{ Scan right looking for a "large" element that is out of
8789
place (mask bit is one). Bitwise "and" between A[i] and mask can be used to
8890
extract the desired bit.
89-
Note we do the tests before incrementing i.
91+
Note we do the tests after incrementing i at each step.
9092
\\Expl}
9193
Decrement j until the mask bit of A[j] = 0 or j <= i \\B 305
9294
\\Expl{ Scan left looking for a "small" element that is out of
9395
place (mask bit is zero). Bitwise "and" between A[i] and mask can be used to
9496
extract the desired bit.
95-
Note we do the tests before decrementing j.
97+
Note we do the tests after decrementing j at each step.
9698
\\Expl}
9799
if i < j \\B 309
98100
\\Expl{ If the indices meet/cross, we exit the loop.
@@ -102,7 +104,6 @@ while i < j \\B 303
102104
\\Expl{ Swap the larger element (A[i]) with the smaller
103105
element (A[j]).
104106
\\Expl}
105-
Increment i and decrement j \\B inc_dec
106107
\\In}
107108
\\In}
108109
\\Code}

0 commit comments

Comments
 (0)