Comparing version 0.2.0 to 0.3.0
@@ -0,1 +1,6 @@ | ||
<a name="0.3.0"></a> | ||
# [0.3.0](https://github.com/mljs/random/compare/v0.2.0...v0.3.0) (2018-05-23) | ||
<a name="0.2.0"></a> | ||
@@ -2,0 +7,0 @@ # [0.2.0](https://github.com/mljs/random/compare/v0.1.0...v0.2.0) (2018-05-16) |
function randomChoice(values, options = {}, random = Math.random) { | ||
const { size = 1, replace = false } = options; | ||
const { size = 1, replace = false, probabilities } = options; | ||
let valuesArr; | ||
let cumSum; | ||
if (typeof values === 'number') { | ||
@@ -10,2 +11,18 @@ valuesArr = getArray(values); | ||
} | ||
if (probabilities) { | ||
if (!replace) { | ||
throw new Error('choice with probabilities and no replacement is not implemented'); | ||
} | ||
// check input is sane | ||
if (probabilities.length !== valuesArr.length) { | ||
throw new Error('the length of probabilities option should be equal to the number of choices'); | ||
} | ||
cumSum = [probabilities[0]]; | ||
for (let i = 1; i < probabilities.length; i++) { | ||
cumSum[i] = cumSum[i - 1] + probabilities[i]; | ||
} | ||
if (Math.abs(1 - cumSum[cumSum.length - 1]) > Number.EPSILON) { | ||
throw new Error('probabilities should sum to 1'); | ||
} | ||
} | ||
if (replace === false && size > valuesArr.length) { | ||
@@ -16,3 +33,3 @@ throw new Error('size option is too large'); | ||
for (let i = 0; i < size; i++) { | ||
const index = randomIndex(valuesArr.length, random); | ||
const index = randomIndex(valuesArr.length, random, cumSum); | ||
result.push(valuesArr[index]); | ||
@@ -32,5 +49,15 @@ if (!replace) { | ||
} | ||
function randomIndex(n, random) { | ||
return Math.floor(random() * n); | ||
function randomIndex(n, random, cumSum) { | ||
const rand = random(); | ||
if (!cumSum) { | ||
return Math.floor(rand * n); | ||
} | ||
else { | ||
let idx = 0; | ||
while (rand > cumSum[idx]) { | ||
idx++; | ||
} | ||
return idx; | ||
} | ||
} | ||
export default randomChoice; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
function randomChoice(values, options = {}, random = Math.random) { | ||
const { size = 1, replace = false } = options; | ||
const { size = 1, replace = false, probabilities } = options; | ||
let valuesArr; | ||
let cumSum; | ||
if (typeof values === 'number') { | ||
@@ -12,2 +13,18 @@ valuesArr = getArray(values); | ||
} | ||
if (probabilities) { | ||
if (!replace) { | ||
throw new Error('choice with probabilities and no replacement is not implemented'); | ||
} | ||
// check input is sane | ||
if (probabilities.length !== valuesArr.length) { | ||
throw new Error('the length of probabilities option should be equal to the number of choices'); | ||
} | ||
cumSum = [probabilities[0]]; | ||
for (let i = 1; i < probabilities.length; i++) { | ||
cumSum[i] = cumSum[i - 1] + probabilities[i]; | ||
} | ||
if (Math.abs(1 - cumSum[cumSum.length - 1]) > Number.EPSILON) { | ||
throw new Error('probabilities should sum to 1'); | ||
} | ||
} | ||
if (replace === false && size > valuesArr.length) { | ||
@@ -18,3 +35,3 @@ throw new Error('size option is too large'); | ||
for (let i = 0; i < size; i++) { | ||
const index = randomIndex(valuesArr.length, random); | ||
const index = randomIndex(valuesArr.length, random, cumSum); | ||
result.push(valuesArr[index]); | ||
@@ -34,5 +51,15 @@ if (!replace) { | ||
} | ||
function randomIndex(n, random) { | ||
return Math.floor(random() * n); | ||
function randomIndex(n, random, cumSum) { | ||
const rand = random(); | ||
if (!cumSum) { | ||
return Math.floor(rand * n); | ||
} | ||
else { | ||
let idx = 0; | ||
while (rand > cumSum[idx]) { | ||
idx++; | ||
} | ||
return idx; | ||
} | ||
} | ||
exports.default = randomChoice; |
@@ -13,2 +13,6 @@ /** | ||
replace?: boolean; | ||
/** | ||
* The probabilities associated with each element. Probabilities should sum to 1 and be the same length as the values. If not specified a uniform distribution is assumed. | ||
*/ | ||
probabilities?: number[]; | ||
} |
{ | ||
"name": "ml-random", | ||
"version": "0.2.0", | ||
"version": "0.3.0", | ||
"description": "choose randomly from a selection of elements", | ||
@@ -15,2 +15,3 @@ "main": "lib/index.js", | ||
"test-only": "jest", | ||
"test-coverage": "jest --coverage", | ||
"tsc": "run-s clean tsc-es6 tsc-es5", | ||
@@ -17,0 +18,0 @@ "tsc-es5": "tsc", |
@@ -53,2 +53,36 @@ import * as XSAdd from 'ml-xsadd'; | ||
}); | ||
it('should produce choice given probabilities', () => { | ||
const samples = 10000; | ||
const r = random.choice(2, { | ||
size: samples, | ||
replace: true, | ||
probabilities: [0.3, 0.7] | ||
}); | ||
const count = r.reduce( | ||
(prev, current) => (current === 0 ? prev + 1 : prev), | ||
0 | ||
); | ||
expect(count / samples).toBeCloseTo(0.3, 2); | ||
}); | ||
it('should throw if probabilities option has the wrong size', () => { | ||
expect(() => { | ||
random.choice(3, { replace: true, probabilities: [0.5, 0.5] }); | ||
}).toThrow( | ||
'the length of probabilities option should be equal to the number of choices' | ||
); | ||
}); | ||
it('should throw if probabilities option does not sum to 1', () => { | ||
expect(() => { | ||
random.choice(3, { replace: true, probabilities: [0.1, 0.5, 0.4001] }); | ||
}).toThrow('probabilities should sum to 1'); | ||
}); | ||
it('choice with probabilities and no replacement not implemented', () => { | ||
expect(() => { | ||
random.choice(3, { replace: false, probabilities: [0.1, 0.5, 0.4] }); | ||
}).toThrow(/not implemented/); | ||
}); | ||
}); | ||
@@ -55,0 +89,0 @@ |
@@ -19,4 +19,6 @@ import { IChoiceOptions } from './Options'; | ||
): Array<T | number> { | ||
const { size = 1, replace = false } = options; | ||
const { size = 1, replace = false, probabilities } = options; | ||
let valuesArr; | ||
let cumSum; | ||
if (typeof values === 'number') { | ||
@@ -28,2 +30,24 @@ valuesArr = getArray(values); | ||
if (probabilities) { | ||
if (!replace) { | ||
throw new Error( | ||
'choice with probabilities and no replacement is not implemented' | ||
); | ||
} | ||
// check input is sane | ||
if (probabilities.length !== valuesArr.length) { | ||
throw new Error( | ||
'the length of probabilities option should be equal to the number of choices' | ||
); | ||
} | ||
cumSum = [probabilities[0]]; | ||
for (let i = 1; i < probabilities.length; i++) { | ||
cumSum[i] = cumSum[i - 1] + probabilities[i]; | ||
} | ||
if (Math.abs(1 - cumSum[cumSum.length - 1]) > Number.EPSILON) { | ||
throw new Error('probabilities should sum to 1'); | ||
} | ||
} | ||
if (replace === false && size > valuesArr.length) { | ||
@@ -34,3 +58,3 @@ throw new Error('size option is too large'); | ||
for (let i = 0; i < size; i++) { | ||
const index = randomIndex(valuesArr.length, random); | ||
const index = randomIndex(valuesArr.length, random, cumSum); | ||
result.push(valuesArr[index]); | ||
@@ -52,6 +76,15 @@ if (!replace) { | ||
function randomIndex(n: number, random: IRandomGenerator) { | ||
return Math.floor(random() * n); | ||
function randomIndex(n: number, random: IRandomGenerator, cumSum?: number[]) { | ||
const rand = random(); | ||
if (!cumSum) { | ||
return Math.floor(rand * n); | ||
} else { | ||
let idx = 0; | ||
while (rand > cumSum[idx]) { | ||
idx++; | ||
} | ||
return idx; | ||
} | ||
} | ||
export default randomChoice; |
@@ -13,2 +13,6 @@ /** | ||
replace?: boolean; | ||
/** | ||
* The probabilities associated with each element. Probabilities should sum to 1 and be the same length as the values. If not specified a uniform distribution is assumed. | ||
*/ | ||
probabilities?: number[]; | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
19302
463