Skip to content
Snippets Groups Projects
Commit a91b5123 authored by David Sehnal's avatar David Sehnal
Browse files

collections

parent 6a80405a
No related branches found
No related tags found
No related merge requests found
import * as B from 'benchmark'
import * as Sort from '../structure/collections/sort'
function shuffle(a: number[]) {
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
const t = a[i];
a[i] = a[j];
a[j] = t;
}
return a;
}
function createTestData(n: number) {
const data = new Int32Array(n); //new Array(n);
for (let i = 0; i < n; i++) {
data[i] = (n * Math.random()) | 0;
data[i] = i;
//data[i] = (n * Math.random()) | 0;
}
shuffle(data as any);
return data;
}
......@@ -24,21 +36,51 @@ export function checkSorted(arr: ArrayLike<number>) {
return true;
}
const _data = createTestData(10000);
const data = () => copyArray(_data);
const suite = new B.Suite();
export function runTest(size: number) {
const _data = createTestData(size);
const dataCopies: number[][] = [];
let dataOffset = 0;
function prepareData() {
dataOffset = 0;
for (let i = 0; i < 100; i++) {
dataCopies[i] = copyArray(_data);
}
}
function getData() {
if (dataOffset < dataCopies.length) return dataCopies[dataOffset++];
return copyArray(_data);
}
prepareData();
const suite = new B.Suite();
function le(x: number, y: number) { return x - y; }
function le(x: number, y: number) { return x - y; }
function name(n: string) { return `${n} (${size} elems)` }
// TODO: the data copying skewes the benchmark -- write a simple benchmark util that allows for a preparation step.
suite
.add(name('native'), () => Array.prototype.sort.call(getData(), le))
.add(name('qsort'), () => Sort.sortArray(getData()))
//.add(name('qsort'), () => Sort.sort(getData(), 0, _data.length, Sort.arrayLess, Sort.arraySwap))
.add(name('native sorted'), () => Array.prototype.sort.call(_data, le))
.add(name('qsort sorted'), () => Sort.sortArray(_data))
//.add(name('qsort sorted'), () => Sort.sort(_data, 0, _data.length, Sort.arrayLess, Sort.arraySwap))
.on('cycle', (e: any) => {
prepareData();
console.log(String(e.target));
})
.run();
console.log('---------------------');
}
suite
.add('native', () => Array.prototype.sort.call(data(), le))
.add('qsort (array asc)', () => Sort.sortArray(data()))
.add('qsort (generic)', () => Sort.sort(data(), _data.length, Sort.arrayLess, Sort.arraySwap))
.add('native sorted', () => Array.prototype.sort.call(_data, le))
.add('qsort sorted (array asc)', () => Sort.sortArray(_data))
.add('qsort sorted (generic)', () => Sort.sort(_data, _data.length, Sort.arrayLess, Sort.arraySwap))
.on('cycle', (e: any) => {
console.log(String(e.target));
})
.run();
\ No newline at end of file
//runTest(10);
//runTest(100);
//runTest(1000);
runTest(10000);
//runTest(100000);
\ No newline at end of file
// TODO: fixed length doubly linked list used for graph traversal
\ No newline at end of file
......@@ -10,6 +10,7 @@ interface RangeSet {
readonly size: number,
has(x: number): boolean,
indexOf(x: number): number,
elementAt(i: number): number,
elements(): Iterator<number>
}
......@@ -51,6 +52,7 @@ namespace RangeSet {
for (let i = 0; i < this.size; i++) ret[i] = i + this.min;
return ret;
}
elementAt(i: number) { return this.min + i; }
elements() { return Iterator.Range(this.min, this.max); }
constructor(public min: number, public max: number) {
......@@ -65,6 +67,7 @@ namespace RangeSet {
has(x: number) { return x >= this.min && x <= this.max && binarySearch(this.values, x) >= 0; }
indexOf(x: number) { return x >= this.min && x <= this.max ? binarySearch(this.values, x) : -1; }
toArray() { return this.values; }
elementAt(i: number) { return this.values[i]; }
elements() { return Iterator.Array(this.values); }
constructor(public values: ArrayLike<number>) {
......
......@@ -7,12 +7,7 @@
export type Comparer<T = any> = (data: T, i: number, j: number) => number
export type Swapper<T = any> = (data: T, i: number, j: number) => void
type Ctx = {
cmp: Comparer,
swap: Swapper,
parts: number[],
data: any
}
type Ctx = { cmp: Comparer, swap: Swapper, parts: number[], data: any }
export function arrayLess(arr: ArrayLike<number>, i: number, j: number) {
return arr[i] - arr[j];
......@@ -30,7 +25,7 @@ function medianPivotIndex(data: any, cmp: Comparer, l: number, r: number) {
else return cmp(data, r, m) > 0 ? cmp(data, m, l) > 0 ? m : l : r;
}
function partitionGeneric(ctx: Ctx, l: number, r: number) {
function partition(ctx: Ctx, l: number, r: number) {
const { cmp, swap, data, parts } = ctx;
let equals = l + 1, tail = r;
......@@ -57,33 +52,6 @@ function partitionGeneric(ctx: Ctx, l: number, r: number) {
parts[1] = tail;
}
function partitionArrayAsc(data: number[], parts: number[], l: number, r: number) {
let equals = l + 1, tail = r;
// move the median to the 1st spot
arraySwap(data, l, medianPivotIndex(data, arrayLess, l, r));
const pivot = data[l];
while (data[tail] > pivot) { --tail; }
for (let i = l + 1; i <= tail; i++) {
const v = data[i];
if (v > pivot) {
arraySwap(data, i, tail);
--tail;
while (data[tail] > pivot) { --tail; }
i--;
} else if (v === pivot) {
arraySwap(data, i, equals);
++equals;
}
}
// move all medians to the correct spots
for (let i = l; i < equals; i++) { arraySwap(data, i, l + tail - i); }
parts[0] = tail - equals + l + 1;
parts[1] = tail;
}
function insertionSort({ data, cmp, swap }: Ctx, start: number, end: number) {
for (let i = start + 1; i <= end; i++) {
let j = i - 1;
......@@ -102,7 +70,7 @@ function quickSort(ctx: Ctx, low: number, high: number) {
return;
}
partitionGeneric(ctx, low, high);
partition(ctx, low, high);
const li = parts[0], ri = parts[1];
if (li - low < high - ri) {
......@@ -115,46 +83,17 @@ function quickSort(ctx: Ctx, low: number, high: number) {
}
}
function insertionSortArrayAsc(data: number[], start: number, end: number) {
for (let i = start + 1; i <= end; i++) {
const key = data[i];
let j = i - 1;
while (j >= start && data[j] > key) {
data[j + 1] = data[j];
j = j - 1;
}
data[j + 1] = key;
}
}
function quickSortArrayAsc(data: number[], parts: number[], low: number, high: number) {
while (low < high) {
if (high - low < 16) {
insertionSortArrayAsc(data, low, high);
return;
}
partitionArrayAsc(data, parts, low, high);
const li = parts[0], ri = parts[1];
if (li - low < high - ri) {
quickSortArrayAsc(data, parts, low, li - 1);
low = ri + 1;
} else {
quickSortArrayAsc(data, parts, ri + 1, high);
high = li - 1;
}
}
export function sortArray(data: ArrayLike<number>, cmp: Comparer<ArrayLike<number>> = arrayLess): ArrayLike<number> {
return sortArrayRange(data, 0, data.length, cmp);
}
export function sortArray(data: ArrayLike<number>, cmp: Comparer<ArrayLike<number>> = arrayLess): ArrayLike<number> {
if (cmp === arrayLess) quickSortArrayAsc(data as any, [0, 0], 0, data.length - 1);
else quickSort({ data, cmp, swap: arraySwap, parts: [0, 0] }, 0, data.length - 1);
export function sortArrayRange(data: ArrayLike<number>, start: number, end: number, cmp: Comparer<ArrayLike<number>> = arrayLess): ArrayLike<number> {
quickSort({ data, cmp, swap: arraySwap, parts: [0, 0] }, start, end - 1);
return data;
}
export function sort<T>(data: T, count: number, cmp: Comparer<T>, swap: Swapper<T>): T {
export function sort<T>(data: T, start: number, end: number, cmp: Comparer<T>, swap: Swapper<T>): T {
const ctx: Ctx = { data, cmp, swap, parts: [0, 0] };
quickSort(ctx, 0, count - 1);
quickSort(ctx, start, end - 1);
return data;
}
\ No newline at end of file
......@@ -82,10 +82,10 @@ describe('qsort-array generic', () => {
// [ 3, 1, 6, 4, 4, 6, 4, 2, 6, 1, 2, 3 ];
if (randomize) {
for (let i = 0; i < 10; i++) {
expect(Sort.sort(shuffleArray(data), data.length, Sort.arrayLess, Sort.arraySwap)).toEqual(data);
expect(Sort.sort(shuffleArray(data), 0, data.length, Sort.arrayLess, Sort.arraySwap)).toEqual(data);
}
} else {
expect(Sort.sort([...data], data.length, Sort.arrayLess, Sort.arraySwap)).toEqual(data);
expect(Sort.sort([...data], 0, data.length, Sort.arrayLess, Sort.arraySwap)).toEqual(data);
}
});
}
......@@ -108,10 +108,10 @@ describe('qsort-dual array', () => {
// [ 3, 1, 6, 4, 4, 6, 4, 2, 6, 1, 2, 3 ];
if (randomize) {
for (let i = 0; i < 10; i++) {
expect(Sort.sort(shuffle(src, len, clone, swap), len, cmp, swap)).toEqual(data);
expect(Sort.sort(shuffle(src, len, clone, swap), 0, len, cmp, swap)).toEqual(data);
}
} else {
expect(Sort.sort(clone(src), len, cmp, swap)).toEqual(data);
expect(Sort.sort(clone(src), 0, len, cmp, swap)).toEqual(data);
}
});
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment