Skip to content

Commit acaee76

Browse files
committed
Merge branch 'REXpartition' into dev
2 parents 5e79193 + f8e6e35 commit acaee76

File tree

6 files changed

+103
-67
lines changed

6 files changed

+103
-67
lines changed

src/algorithms/controllers/MSDRadixSort.js

Lines changed: 56 additions & 57 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'
@@ -159,7 +161,6 @@ export default {
159161
// where mid is undefined until after partition
160162
const finished_stack_frames = [];
161163
const real_stack = [];
162-
let leftCheck = false
163164

164165
// ----------------------------------------------------------------------------------------------------------------------------
165166
// Define helper functions
@@ -169,83 +170,81 @@ export default {
169170
// This function is the only way information is cached and incremented properly in the while loop
170171
const partitionChunker = (bookmark, i, j, prev_i, prev_j, left, right, depth, arr, mask) => {
171172
assert(bookmark !== undefined); // helps catch bugs early, and trace them in stack
172-
const args_array = [real_stack, finished_stack_frames, i, j, prev_i, prev_j, left, right, depth, leftCheck, 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]
173174
chunker.add(bookmark, refreshStack, args_array, depth)
174175
}
175176

176-
// we use a global flag in case we scan through the whole partition
177-
// and stop at the end without finding the mask bit we are looking
178-
// for
179-
const refreshStack = (vis, cur_real_stack, cur_finished_stack_frames, cur_i, cur_j, prev_i, prev_j, left, right, cur_depth, checkingLeft, maxIndex, arr, mask) => {
177+
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) => {
180178
// If we fall off the start/end of the array we just use the
181179
// first/last element and give the actual value of j/i
180+
let cur_i_too_low;
182181
let cur_i_too_high;
183182
let cur_j_too_low;
183+
let cur_j_too_high;
184184
let tmp_j = cur_j; // used to determine context later
185185
if (cur_i === A.length) {
186186
cur_i = undefined;
187187
cur_i_too_high = A.length - 1;
188-
} else {
189-
cur_i_too_high = undefined;
188+
} else if (cur_i === -1) {
189+
cur_i = undefined;
190+
cur_i_too_low = 0;
190191
}
191-
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) {
192196
cur_j = undefined;
193197
cur_j_too_low = 0;
194-
} else {
195-
cur_j_too_low = undefined;
196198
}
197199

198200
assert(vis.array);
199201
assert(cur_real_stack && cur_finished_stack_frames);
200202

201-
vis.array.setStackDepth(cur_real_stack.length);
202-
vis.array.setStack(
203-
deriveStack(cur_real_stack, cur_finished_stack_frames, cur_i, cur_j, cur_depth)
204-
);
203+
// This was getting very messy - colors depends a
204+
// lot on where we are and we had a bunch of tricky testing of
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
205209

206-
// XXX This is getting very messy - (un)highlighting depends a
207-
// lot on where we are and we have a bunch of tricky testing of
208-
// various vars to determine that. Could pass in bookmark to
209-
// simplify some things at least??
210210
// Show the binary representation for the current index
211-
// plus (un)highlight appropriate element(s)
212-
updateMask(vis, mask) // only needed for start of recursive function
213-
if (maxIndex !== undefined) { // top level call to recursive fn
211+
// plus color appropriate element(s)
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
214215
unhighlight(vis, maxIndex)
215216
updateBinary(vis, 0)
216217
}
217-
if (cur_i !== undefined && tmp_j === undefined && prev_i !== undefined) {
218+
if (whereAreWe === MSD_BOOKMARKS.pre_sort_left) {
218219
// just before first recursive call
219-
if (i <= right)
220-
vis.array.selectColor(prev_i, partRColor)
221-
if (prev_j >= left) // might have fallen off array
222-
vis.array.selectColor(prev_j, partLColor)
223220
for (let k = cur_i; k <= right; k++)
224221
vis.array.deselect(k);
225-
} else if (checkingLeft) {
222+
} else if (whereAreWe === MSD_BOOKMARKS.partition_left) {
226223
// note i can fall off RHS of array...
227-
if (prev_i !== undefined && prev_i !== cur_j) {
228-
let real_i = cur_i;
229-
if (cur_i === undefined)
230-
real_i = right + 1;
231-
for (let k = prev_i; k < real_i && k <= right; k++)
232-
vis.array.selectColor(k, partLColor)
233-
if (cur_i !== undefined && cur_i <= right && (arr[cur_i] >> mask & 1) === 1)
224+
if (cur_i !== undefined && cur_i <= right ) {
225+
updateBinary(vis, arr[cur_i]);
226+
if ((arr[cur_i] >> mask & 1) === 1)
234227
vis.array.selectColor(cur_i, partRColor)
228+
else {
229+
vis.array.selectColor(cur_i, partLColor);
230+
}
235231
}
236-
if (arr && cur_i !== undefined)
237-
updateBinary(vis, arr[cur_i])
238-
} else {
232+
} else if (whereAreWe === MSD_BOOKMARKS.partition_right) {
239233
// note j can fall off LHS of array...
240-
if (prev_j !== undefined && prev_j !== cur_i)
241-
for (let k = prev_j; k >= cur_j && k >= cur_i; k--)
242-
vis.array.selectColor(k, partRColor)
243-
if (prev_j !== undefined && cur_j !== undefined && cur_j > cur_i) {
244-
vis.array.selectColor(cur_j, partLColor);
245-
// XXX probably best avoid updateBinary at swap
246-
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+
}
247241
}
248242
}
243+
244+
vis.array.setStackDepth(cur_real_stack.length);
245+
vis.array.setStack(
246+
deriveStack(cur_real_stack, cur_finished_stack_frames, cur_i, cur_j, cur_depth)
247+
);
249248
if (left < A.length) // shouldn't happen with initial mask choice
250249
assignVariable(vis, VIS_VARIABLE_STRINGS.left, left);
251250
if (right >= 0) {
@@ -257,11 +256,13 @@ export default {
257256
}
258257
if (isPartitionExpanded() || isRecursionExpanded()) {
259258
assignVariable(vis, VIS_VARIABLE_STRINGS.i_left_index, cur_i);
259+
assignVariable(vis, VIS_VARIABLE_STRINGS.i_eq_0, cur_i_too_low);
260260
assignVariable(vis, VIS_VARIABLE_STRINGS.i_gt_n, cur_i_too_high);
261261
}
262262
if (isPartitionExpanded()) {
263263
assignVariable(vis, VIS_VARIABLE_STRINGS.j_right_index, cur_j);
264264
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);
265266
}
266267
};
267268

@@ -334,8 +335,8 @@ export default {
334335
let DELAY_POPPER_SWAP = 700;
335336

336337
const partition = (arr, left, right, mask, depth) => {
337-
i = left
338-
j = right
338+
i = left-1
339+
j = right+1
339340

340341
const partitionChunkerWrapper = (bookmark) => {
341342
partitionChunker(bookmark, i, j, prev_i, prev_j, left, right, depth, arr, mask)
@@ -354,8 +355,6 @@ cur_i, cur_j, cur_depth, A) => {
354355

355356
vis.array.swapElements(_n1, _n2);
356357
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)
357-
vis.array.selectColor(_n1, partLColor);
358-
vis.array.selectColor(_n2, partRColor);
359358
// redo poppers: swapping the elements keeps the
360359
// contents of the poppers correct but the position needs
361360
// to change. The documentation suggests update()
@@ -376,25 +375,25 @@ arr],
376375
depth);
377376
}
378377

379-
partitionChunkerWrapper(MSD_BOOKMARKS.set_i)
378+
partitionChunkerWrapper(MSD_BOOKMARKS.set_i);
380379
// partitionChunkerWrapper(MSD_BOOKMARKS.set_j)
381380
while (i < j) {
382381
prev_i = i; // save prev value for unhighlighting
383382
prev_j = j;
384383
// Build the left group until it reaches the mask (find the big element)
385-
leftCheck = true
386-
while (i <= j && (arr[i] >> mask & 1) === 0) {
387-
// partitionChunkerWrapper(MSD_BOOKMARKS.partition_left)
384+
i++;
385+
partitionChunkerWrapper(MSD_BOOKMARKS.partition_left)
386+
while (i < j && (arr[i] >> mask & 1) === 0) {
388387
i++
388+
partitionChunkerWrapper(MSD_BOOKMARKS.partition_left)
389389
}
390-
partitionChunkerWrapper(MSD_BOOKMARKS.partition_left)
391390
// Build the right group until it fails the mask (find the small element)
392-
leftCheck = false
391+
j--;
392+
partitionChunkerWrapper(MSD_BOOKMARKS.partition_right)
393393
while (j > i && (arr[j] >> mask & 1) === 1) {
394-
// partitionChunkerWrapper(MSD_BOOKMARKS.partition_right)
395394
j--
395+
partitionChunkerWrapper(MSD_BOOKMARKS.partition_right)
396396
}
397-
partitionChunkerWrapper(MSD_BOOKMARKS.partition_right)
398397

399398
// Swap if the bigger element is not in the right place
400399
if (i < j) {

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: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,15 @@ 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
79-
\\Expl{ When the indices cross, all the large elements at the left of
81+
\\Expl{ When the indices meet/cross, all the large elements at the left of
8082
the array segment have been swapped with small elements from the
8183
right of the array segment. The coding here can be simplified
8284
if we use "break" or similar to exit from this loop.
@@ -86,16 +88,16 @@ 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
98-
\\Expl{ If the indices cross, we exit the loop.
100+
\\Expl{ If the indices meet/cross, we exit the loop.
99101
\\Expl}
100102
\\In{
101103
swap(A[i], A[j]) \\B 310

0 commit comments

Comments
 (0)