124 lines
2.2 KiB
JavaScript
124 lines
2.2 KiB
JavaScript
/**
|
|
* @class WeightedList
|
|
* @constructor
|
|
*/
|
|
|
|
module.exports = WeightedList;
|
|
|
|
var _ = require('../../../../src/lib/utils');
|
|
|
|
function WeightedList(list) {
|
|
Array.call(this);
|
|
|
|
_.forEach(list, _.bindKey(this, 'push'));
|
|
|
|
}
|
|
_.inherits(WeightedList, Array);
|
|
|
|
/**
|
|
* Add a value to the weighted list.
|
|
* @method add
|
|
* @param {Any} value
|
|
* @param {Number} [weight] Optional. Defaults to 1.
|
|
* @return {Number} The index of the item that was added.
|
|
*/
|
|
WeightedList.prototype.push = function (weight, value) {
|
|
if (!weight && weight !== 0) {
|
|
weight = 1;
|
|
}
|
|
|
|
Array.prototype.push.call(this, {
|
|
value: value,
|
|
weight: weight
|
|
});
|
|
|
|
delete this._sum;
|
|
delete this._totals;
|
|
|
|
return this.length - 1;
|
|
};
|
|
|
|
WeightedList.prototype.get = function () {
|
|
return this[this._randomIndex()].value;
|
|
};
|
|
|
|
/**
|
|
* Returns an index by weighted random distribution.
|
|
* @method _randomIndex
|
|
* @protected
|
|
* @return {Number}
|
|
*/
|
|
WeightedList.prototype._randomIndex = function () {
|
|
var maximumIndex,
|
|
me = this,
|
|
middleIndex,
|
|
minimumIndex = 0,
|
|
random,
|
|
sum = me._sum,
|
|
total,
|
|
totals = me._totals;
|
|
|
|
if (!sum || !totals) {
|
|
me._update();
|
|
|
|
sum = me._sum;
|
|
totals = me._totals;
|
|
|
|
if (!sum || !totals || !totals.length) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
maximumIndex = totals.length - 1;
|
|
random = Math.random() * sum;
|
|
|
|
while (maximumIndex >= minimumIndex) {
|
|
middleIndex = (maximumIndex + minimumIndex) / 2;
|
|
|
|
if (middleIndex < 0) {
|
|
middleIndex = 0;
|
|
} else {
|
|
middleIndex = Math.floor(middleIndex);
|
|
}
|
|
|
|
total = totals[middleIndex];
|
|
|
|
if (random === total) {
|
|
middleIndex += 1;
|
|
break;
|
|
} else if (random < total) {
|
|
if (middleIndex && random > totals[middleIndex - 1]) {
|
|
break;
|
|
}
|
|
|
|
maximumIndex = middleIndex - 1;
|
|
} else if (random > total) {
|
|
minimumIndex = middleIndex + 1;
|
|
}
|
|
}
|
|
|
|
return middleIndex;
|
|
};
|
|
|
|
/**
|
|
* Updates chached data for achieving weighted random distribution.
|
|
* @method _update
|
|
* @chainable
|
|
* @protected
|
|
*/
|
|
WeightedList.prototype._update = function () {
|
|
var me = this,
|
|
sum = 0,
|
|
totals = [];
|
|
|
|
_.each(me, function (item) {
|
|
sum += item.weight;
|
|
totals.push(sum);
|
|
});
|
|
|
|
me._sum = sum;
|
|
me._totals = totals;
|
|
|
|
return me;
|
|
};
|