Skip to content

Commit c51d205

Browse files
committed
Add Selection (random, determinstic) test
1 parent e48780c commit c51d205

File tree

2 files changed

+139
-106
lines changed

2 files changed

+139
-106
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import selection from "./selection";
2+
3+
describe("Selection Test", () => {
4+
const nTimes = 100;
5+
const SIZE = 100;
6+
// ith element to be returned
7+
const i = Math.floor(Math.random() * SIZE);
8+
test("Compare return from Random Select with ith number from sorted arr", () => {
9+
for (let j = 0; j < nTimes; j++) {
10+
const arr = Array.from({ length: SIZE }, () =>
11+
Math.floor(Math.random() * 1000)
12+
);
13+
const ans = selection(arr, i);
14+
const sortedArr = arr.slice().sort((a, b) => a - b);
15+
expect(ans).toBe(sortedArr[i]);
16+
}
17+
});
18+
test("Compare return from Deterministic Select with ith number from sorted arr", () => {
19+
for (let j = 0; j < nTimes; j++) {
20+
const arr = Array.from({ length: SIZE }, () =>
21+
Math.floor(Math.random() * 1000)
22+
);
23+
const ans = selection(arr, i, 1);
24+
const sortedArr = arr.slice().sort((a, b) => a - b);
25+
expect(ans).toBe(sortedArr[i]);
26+
}
27+
});
28+
});
Lines changed: 111 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,110 +1,115 @@
11
// Returns a random number between [min,max)
22
const random = (min: number, max: number) => {
3-
return Math.floor(Math.random() * (max - min)) + min;
4-
};
5-
6-
// Returns a new list if each group of 5 inside of l[i]
7-
// eg:
8-
// arr := [0,1,2,3,4,5,6,7,8,9]:
9-
// l:= [[0,1,2,3,4],[5,6,7,8,9]]
10-
const groupOf5 = (arr:number[]) => {
11-
let l = [];
12-
let g = [];
13-
for (let i = 0; i < arr.length; i++) {
14-
if (g.length < 5) g.push(arr[i]);
15-
else {
16-
l.push(g);
17-
g = [];
18-
g.push(arr[i]);
19-
}
3+
return Math.floor(Math.random() * (max - min)) + min;
4+
};
5+
6+
// Returns a new list if each group of 5 inside of l[i]
7+
// eg:
8+
// arr := [0,1,2,3,4,5,6,7,8,9]:
9+
// l:= [[0,1,2,3,4],[5,6,7,8,9]]
10+
const groupOf5 = (arr: number[]) => {
11+
let l = [];
12+
let g = [];
13+
for (let i = 0; i < arr.length; i++) {
14+
if (g.length < 5) g.push(arr[i]);
15+
else {
16+
l.push(g);
17+
g = [];
18+
g.push(arr[i]);
2019
}
21-
l.push(g);
22-
return l;
23-
};
24-
25-
// Returns the median of a list with 5 values or less
26-
const medianOf5 = (arr:number[]) => {
27-
if (arr.length > 5) throw new Error('Arr cannot have more than 5 numbers');
28-
arr.sort(function (a, b) {
29-
return a - b;
30-
});
31-
const m = Math.floor(arr.length / 2);
32-
return arr[m];
33-
};
34-
35-
// Returns 'the median of the medians'
36-
const medianOfMedians = (arr: number[]) => {
37-
// break arr into arr.length/5 of sie 5 each
38-
const l = groupOf5(arr);
39-
// array with arr.length/5 medians
40-
const c = [];
41-
for (let i = 0; i < l.length; i++) {
42-
c.push(medianOf5(l[i]));
20+
}
21+
l.push(g);
22+
return l;
23+
};
24+
25+
// Returns the median of a list with 5 values or less
26+
const medianOf5 = (arr: number[]) => {
27+
if (arr.length > 5) throw new Error("Arr cannot have more than 5 numbers");
28+
arr.sort(function (a, b) {
29+
return a - b;
30+
});
31+
const m = Math.floor(arr.length / 2);
32+
return arr[m];
33+
};
34+
35+
// Returns 'the median of the medians'
36+
// - Recursively compute "median of medians":
37+
// - Break arr in groups of five elements
38+
// - Calculate medians = each median from each "Group of 5"
39+
// - Recusrively compute the median from medians
40+
const medianOfMedians = (arr: number[]) => {
41+
// break arr into arr.length/5 of sie 5 each
42+
const l = groupOf5(arr);
43+
// array with arr.length/5 medians
44+
const c = [];
45+
for (let i = 0; i < l.length; i++) {
46+
c.push(medianOf5(l[i]));
47+
}
48+
//middle of c
49+
let mc = Math.floor(c.length / 2);
50+
//recursively compute median of c
51+
const p = selection(c, mc, 1);
52+
const idx = arr.indexOf(p);
53+
// swap to the 1st position
54+
[arr[0], arr[idx]] = [arr[idx], arr[0]];
55+
return p;
56+
};
57+
58+
// Returns a pivot
59+
// chooseP:
60+
// 0: Random Pivot
61+
// 1: Deterministic Pivot ('median of medians')
62+
const choosePivot = (arr: number[], chooseP: number) => {
63+
switch (chooseP) {
64+
case 0:
65+
default:
66+
const idx = random(0, arr.length);
67+
[arr[0], arr[idx]] = [arr[idx], arr[0]];
68+
return arr[0];
69+
case 1:
70+
return medianOfMedians(arr);
71+
}
72+
};
73+
74+
// Returns: the order statistic of a given pivot (its correct position if the arr was ordered)
75+
// also make a partition around the pivot
76+
// chooseP:
77+
// 0: Randomized Selection
78+
// 1: Deterministic Selection
79+
const partition = (arr: number[], chooseP = 0) => {
80+
// const p = choosePivot(arr, chooseP);
81+
const p = choosePivot(arr, chooseP);
82+
// keep track of how many elements are smaller than the pivot
83+
// this will be its 'right' position if arr is sorted
84+
let i = 1;
85+
// elements with idx < j : partitioned
86+
// elements with idx > j unpartitioned
87+
for (let j = 1; j < arr.length; j++) {
88+
// check if the next element is smaller than the pivot
89+
if (arr[j] < p) {
90+
// swap and move i
91+
[arr[i], arr[j]] = [arr[j], arr[i]];
92+
i++;
4393
}
44-
//middle of c
45-
let mc = Math.floor(c.length / 2);
46-
//recursively compute median of c
47-
const p = selection(c, mc, 1);
48-
return p;
49-
};
50-
51-
// Returns a pivot
52-
// chooseP:
53-
// 0: Random Pivot
54-
// 1: Deterministic Pivot ('median of medians')
55-
const choosePivot = (arr:number[], chooseP:number) => {
56-
switch (chooseP) {
57-
case 0:
58-
default:
59-
const idx = random(0, arr.length);
60-
[arr[0], arr[idx]] = [arr[idx], arr[0]];
61-
return arr[0];
62-
case 1:
63-
return medianOfMedians(arr);
64-
}
65-
};
66-
67-
// Returns: the order statistic of a given pivot (its correct position if the arr was ordered)
68-
// also make a partition around the pivot
69-
// chooseP:
70-
// 0: Randomized Selection
71-
// 1: Deterministic Selection
72-
const partition = (arr: number[], chooseP = 0) => {
73-
const p = choosePivot(arr, chooseP);
74-
// keep track of how many elements are smaller than the pivot
75-
// this will be its 'right' position if arr is sorted
76-
let i = 1;
77-
// elements with idx < j : partitioned
78-
// elements with idx > j unpartitioned
79-
for (let j = 1; j < arr.length; j++) {
80-
// check if the next element is smaller than the pivot
81-
if (arr[j] < p) {
82-
// swap and move i
83-
[arr[i], arr[j]] = [arr[j], arr[i]];
84-
i++;
85-
}
86-
}
87-
// swap p to its right position and returns its idx
88-
[arr[0], arr[i - 1]] = [arr[i - 1], arr[0]];
89-
return i - 1;
90-
};
91-
92-
// Returns the ith smallest element of arr
93-
const selection = (arr: number[], i: number, chooseP = 0):number|boolean => {
94-
// out of bounds
95-
if (i > arr.length - 1) return false;
96-
if (arr.length === 1) return arr[0];
97-
// j is the index of p
98-
const j = partition(arr, chooseP);
99-
if (j === i) return arr[j];
100-
if (j > i) {
101-
const left = arr.slice(0, j);
102-
return selection(left, i);
103-
}
104-
// i < j
105-
const right = arr.slice(j + 1);
106-
return selection(right, i - j - 1);
107-
};
108-
109-
export = selection
110-
94+
}
95+
// swap p to its right position and returns its idx
96+
[arr[0], arr[i - 1]] = [arr[i - 1], arr[0]];
97+
return i - 1;
98+
};
99+
100+
// Returns the ith smallest element of arr
101+
const selection = (arr: number[], i: number, chooseP = 0): number => {
102+
if (arr.length === 1) return arr[0];
103+
// j is the index of the pivot
104+
const j = partition(arr, chooseP);
105+
if (j === i) return arr[j];
106+
if (j > i) {
107+
const left = arr.slice(0, j);
108+
return selection(left, i);
109+
}
110+
// i < j
111+
const right = arr.slice(j + 1);
112+
return selection(right, i - j - 1);
113+
};
114+
115+
export default selection;

0 commit comments

Comments
 (0)