Skip to content

Commit

Permalink
Add weighted() support for fractional weight as suggested by @davmillar
Browse files Browse the repository at this point in the history
… in #76
  • Loading branch information
victorquinn committed Sep 2, 2014
1 parent d922d5e commit f1fdcfe
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 1 deletion.
14 changes: 13 additions & 1 deletion chance.js
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,19 @@
throw new RangeError("Chance: length of array and weights must match");
}

// @todo figure out best way to handle fractions (probaby scaling so they are whole)
// If any of the weights are less than 1, we want to scale them up to whole
// numbers for the rest of this logic to work
if (weights.some(function(weight) { return weight < 1; })) {
var min = weights.reduce(function(min, weight) {
return (weight < min) ? weight : min;
}, weights[0]);

var scaling_factor = 1 / min;

weights = weights.map(function(weight) {
return weight * scaling_factor;
});
}

var sum = weights.reduce(function(total, weight) {
return total + weight;
Expand Down
20 changes: 20 additions & 0 deletions test/test.helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,26 @@ define(['Chance', 'mocha', 'chai', 'underscore'], function (Chance, mocha, chai,
expect((picked.c / picked.b) * 100).to.be.within(50, 150);
});
});

it("works with fractional weights", function() {
// Use Math.random as the random function rather than our Mersenne twister just to
// speed things up here because this test takes awhile to gather enough data to
// have a large enough sample size to adequately test. This increases performance
// by a few orders of magnitude
var chance = new Chance(Math.random);
_(10).times(function() {
var picked = { a: 0, b: 0, c: 0, d: 0 };
// This makes it a tad slow, but we need a large enough sample size to adequately test
_(100000).times(function () {
picked[chance.weighted(['a', 'b', 'c', 'd'], [0.001, 0.1, 0.1, 0.001])]++;
});

// This range is somewhat arbitrary, but good enough to test our constraints
expect(picked.b / picked.a).to.be.within(80, 120);
expect(picked.c / picked.d).to.be.within(80, 120);
expect((picked.c / picked.b) * 100).to.be.within(50, 150);
});
});
});

describe("shuffle()", function () {
Expand Down

0 comments on commit f1fdcfe

Please sign in to comment.