Skip to content

Commit

Permalink
modernize, ESM, first-class TS
Browse files Browse the repository at this point in the history
  • Loading branch information
mourner committed Jul 3, 2024
1 parent 26a2414 commit b11f20c
Show file tree
Hide file tree
Showing 14 changed files with 3,134 additions and 79 deletions.
19 changes: 19 additions & 0 deletions .github/workflows/node.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Node
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20

- name: Install dependencies
run: npm ci

- name: Run tests
run: npm test
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
quickselect.js
node_modules
*.log
4 changes: 0 additions & 4 deletions .travis.yml

This file was deleted.

2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
ISC License

Copyright (c) 2018, Vladimir Agafonkin
Copyright (c) 2024, Vladimir Agafonkin

Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## quickselect [![Build Status](https://travis-ci.org/mourner/quickselect.svg?branch=master)](https://travis-ci.org/mourner/quickselect)
## quickselect

A tiny and fast [selection algorithm](https://en.wikipedia.org/wiki/Selection_algorithm) in JavaScript
(specifically, [Floyd-Rivest selection](https://en.wikipedia.org/wiki/Floyd%E2%80%93Rivest_algorithm)).
Expand All @@ -19,7 +19,7 @@ The `k`-th element will have the `(k - left + 1)`-th smallest value in `[left, r
Example:

```js
var arr = [65, 28, 59, 33, 21, 56, 22, 95, 50, 12, 90, 53, 28, 77, 39];
const arr = [65, 28, 59, 33, 21, 56, 22, 95, 50, 12, 90, 53, 28, 77, 39];

quickselect(arr, 8);

Expand Down
12 changes: 5 additions & 7 deletions bench.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@

import quickselect from './';
import quickselect from './index.js';

var N = 10000000;
var arr = [];
for (var i = 0; i < N; i++) arr.push(Math.random());
const N = 10000000;
const arr = [];
for (let i = 0; i < N; i++) arr.push(Math.random());

console.time('quickselect');
quickselect(arr, Math.floor(N / 2), 0, N - 1, function (a, b) {
return a < b ? -1 : a > b ? 1 : 0;
});
quickselect(arr, Math.floor(N / 2), 0, N - 1, (a, b) => (a < b ? -1 : a > b ? 1 : 0));
console.timeEnd('quickselect');
1 change: 1 addition & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {default} from 'eslint-config-mourner';
19 changes: 7 additions & 12 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,11 @@
* Rearranges items so that all items in the [left, k] are the smallest.
* The k-th element will have the (k - left + 1)-th smallest value in [left, right].
*
* @param arr the array to partially sort (in place)
* @param k middle index for partial sorting (as defined above)
* @param left left index of the range to sort (0 by default)
* @param right right index (last index of the array by default)
* @param compare compare function
* @template T
* @param {T[]} arr the array to partially sort (in place)
* @param {number} k middle index for partial sorting (as defined above)
* @param {number} [left=0] left index of the range to sort
* @param {number} [right=arr.length-1] right index
* @param {(a: T, b: T) => number} [compare = (a, b) => a - b] compare function
*/
export default function quickselect<T>(
arr: T[],
k: number,
left?: number,
right?: number,
compare?: (x: T, y: T) => number
): void;
export default function quickselect<T>(arr: T[], k: number, left?: number | undefined, right?: number | undefined, compare?: ((a: T, b: T) => number) | undefined): void;
54 changes: 37 additions & 17 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,33 @@

export default function quickselect(arr, k, left, right, compare) {
quickselectStep(arr, k, left || 0, right || (arr.length - 1), compare || defaultCompare);
}

function quickselectStep(arr, k, left, right, compare) {
/**
* Rearranges items so that all items in the [left, k] are the smallest.
* The k-th element will have the (k - left + 1)-th smallest value in [left, right].
*
* @template T
* @param {T[]} arr the array to partially sort (in place)
* @param {number} k middle index for partial sorting (as defined above)
* @param {number} [left=0] left index of the range to sort
* @param {number} [right=arr.length-1] right index
* @param {(a: T, b: T) => number} [compare = (a, b) => a - b] compare function
*/
export default function quickselect(arr, k, left = 0, right = arr.length - 1, compare = defaultCompare) {

while (right > left) {
if (right - left > 600) {
var n = right - left + 1;
var m = k - left + 1;
var z = Math.log(n);
var s = 0.5 * Math.exp(2 * z / 3);
var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
quickselectStep(arr, k, newLeft, newRight, compare);
const n = right - left + 1;
const m = k - left + 1;
const z = Math.log(n);
const s = 0.5 * Math.exp(2 * z / 3);
const sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
const newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
const newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
quickselect(arr, k, newLeft, newRight, compare);
}

var t = arr[k];
var i = left;
var j = right;
const t = arr[k];
let i = left;
/** @type {number} */
let j = right;

swap(arr, left, k);
if (compare(arr[right], t) > 0) swap(arr, left, right);
Expand All @@ -43,12 +51,24 @@ function quickselectStep(arr, k, left, right, compare) {
}
}

/**
* @template T
* @param {T[]} arr
* @param {number} i
* @param {number} j
*/
function swap(arr, i, j) {
var tmp = arr[i];
const tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}

/**
* @template T
* @param {T} a
* @param {T} b
* @returns {number}
*/
function defaultCompare(a, b) {
return a < b ? -1 : a > b ? 1 : 0;
}
Loading

0 comments on commit b11f20c

Please sign in to comment.