Skip to content

Commit 56ffc5c

Browse files
committed
🎨 improve(knuth-shuffle): support to shuffle sub-range of array
1 parent 0c4f7a9 commit 56ffc5c

File tree

3 files changed

+54
-8
lines changed

3 files changed

+54
-8
lines changed

Diff for: packages/knuth-shuffle/README.md

+9
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,15 @@ If you are interested in this algorithm, you can check [here][knuth-shuffle].
101101
knuthShuffle(nodes)
102102
```
103103

104+
* Shuffle a contiguous range of the original array.
105+
106+
```typescript
107+
import knuthShuffle from '@algorithm.ts/knuth-shuffle'
108+
109+
// shuffle { a[2], a[3], a[4], a[5], a[6] }
110+
knuthShuffle([1, 2, 3, 4, 5, 6, 7, 8, 9], 2, 7)
111+
```
112+
104113
## Related
105114

106115
* [洗牌问题和 knuth-shuffle 算法][knuth-shuffle]

Diff for: packages/knuth-shuffle/__test__/knuth-shuffle.spec.ts

+33-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { knuthShuffle } from '../src'
22

33
describe('knuth-shuffle', function () {
4-
const ebs = 5
4+
const ebs = 2
55
const size = 1e2
66
const shuffleTimes = 1e4
77

@@ -21,10 +21,39 @@ describe('knuth-shuffle', function () {
2121
})
2222

2323
test('uniformly distribute', function () {
24+
const mean: number = count[0] / shuffleTimes
2425
for (let i = 1; i < size; ++i) {
25-
expect(
26-
Math.abs(count[i] - count[i - 1]) / shuffleTimes,
27-
).toBeLessThanOrEqual(ebs)
26+
expect(Math.abs(count[i] / shuffleTimes - mean)).toBeLessThanOrEqual(ebs)
2827
}
2928
})
29+
30+
test('sub-array', function () {
31+
const nums: number[] = new Array(size)
32+
for (let i = 0; i < size; ++i) nums[i] = i
33+
34+
const start = 10
35+
const end = 70
36+
knuthShuffle(nums, start, end)
37+
for (let i = 0; i < start; ++i) expect(nums[i]).toBe(i)
38+
for (let i = end; i < size; ++i) expect(nums[i]).toBe(i)
39+
40+
let diff = 0
41+
for (let i = start; i < end; ++i) diff = nums[i] === i ? 0 : 1
42+
expect(diff).toBeGreaterThan(0)
43+
})
44+
45+
test('out of boundary', function () {
46+
const nums: number[] = new Array(size)
47+
for (let i = 0; i < size; ++i) nums[i] = i
48+
49+
knuthShuffle(nums, 200, 1)
50+
expect(nums.every((x, i) => x === i)).toBe(true)
51+
52+
knuthShuffle(nums, -2, 50)
53+
for (let i = 50; i < size; ++i) expect(nums[i]).toBe(i)
54+
55+
let diff = 0
56+
for (let i = 0; i < 50; ++i) diff = nums[i] === i ? 0 : 1
57+
expect(diff).toBeGreaterThan(0)
58+
})
3059
})

Diff for: packages/knuth-shuffle/src/index.ts

+12-4
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,18 @@
22
* Shuffle array.
33
* @param nodes
44
*/
5-
export function knuthShuffle<T extends unknown = unknown>(nodes: T[]): void {
6-
const N = nodes.length
7-
for (let i = N - 1, j: number, x: T; i > 0; --i) {
8-
j = Math.floor(Math.random() * (i + 1))
5+
export function knuthShuffle<T extends unknown = unknown>(
6+
nodes: T[],
7+
_start = 0,
8+
_end: number = nodes.length,
9+
): void {
10+
const start = Math.max(0, _start)
11+
const end = Math.min(nodes.length, _end)
12+
if (start + 1 >= end) return
13+
14+
const N = end - start
15+
for (let n = N - 1, i: number, j = end - 1, x: T; n > 0; --n, --j) {
16+
i = ((Math.random() * n) >> 0) + start
917
x = nodes[i]
1018
// eslint-disable-next-line no-param-reassign
1119
nodes[i] = nodes[j]

0 commit comments

Comments
 (0)