-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
93 lines (82 loc) · 2.32 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
const quickselect = require('quickselect')
const pickWithoutWeight = (values, options) => {
const { random = Math.random, count = 1 } = options
const length = values.length
const result = []
for (let i = 0; i < count; i++) {
const rand = Math.floor(random() * (length - i) + i)
result.push(values[rand])
const tmp = values[rand]
values[rand] = values[i]
values[i] = tmp
}
return result
}
const pickWithWeight = (values, weights, options) => {
const { random = Math.random, count = 1 } = options
// https://stackoverflow.com/questions/2140787/select-k-random-elements-from-a-list-whose-elements-have-weights
const result = values.map((value, index) => {
const weight = weights[index]
return {
value,
weight: -Math.log(random()) / weight,
}
})
quickselect(
result,
count,
0,
result.length - 1,
(a, b) => a.weight - b.weight
)
return result.slice(0, count).map((item) => item.value)
// let sum = weights.reduce((prev, weight) => prev + weight, 0)
// const weightMap = new Map()
// values.forEach((value, index) => {
// weightMap.set(value, weights[index])
// })
// for (let i = 0; i < count; i++) {
// let rand = random() * sum
// for (const [value, weight] of weightMap) {
// if (weight > rand) {
// result.push(value)
// sum -= weight
// weightMap.delete(value)
// break
// }
// rand -= weight
// }
// }
}
const pickx = (values, weights, options = {}) => {
if ({}.toString.call(weights) === '[object Object]') {
options = weights
weights = null
}
if (!weights && {}.toString.call(values) === '[object Object]') {
const _values = []
weights = []
Object.keys(values).forEach((key) => {
_values.push(key)
weights.push(values[key])
})
values = _values
}
if (weights) {
if (weights.some((weight) => !(typeof weight === 'number' && weight > 0))) {
throw new Error('weight should be a positive number')
}
if (weights.length !== values.length) {
throw new Error("values don't match weights")
}
}
const { count = 1 } = options
if (count >= values.length) {
return values
}
if (!weights) {
return pickWithoutWeight(values, options)
}
return pickWithWeight(values, weights, options)
}
module.exports = pickx