例如:有一个数组包含四个项目,我想随机获取其中一个,如下所示:
array items = [
"bike" //40% chance to select
"car" //30% chance to select
"boat" //15% chance to select
"train" //10% chance to select
"plane" //5% chance to select
]
例如:有一个数组包含四个项目,我想随机获取其中一个,如下所示:
array items = [
"bike" //40% chance to select
"car" //30% chance to select
"boat" //15% chance to select
"train" //10% chance to select
"plane" //5% chance to select
]
function weighted_random(items, weights) {
var i;
for (i = 1; i < weights.length; i++)
weights[i] += weights[i - 1];
var random = Math.random() * weights[weights.length - 1];
for (i = 0; i < weights.length; i++)
if (weights[i] > random)
break;
return items[i];
}
function weighted_random(options) {
var i;
var weights = [options[0].weight];
for (i = 1; i < options.length; i++)
weights[i] = options[i].weight + weights[i - 1];
var random = Math.random() * weights[weights.length - 1];
for (i = 0; i < weights.length; i++)
if (weights[i] > random)
break;
return options[i].item;
}
解释:
我制作了这个图表,展示了这个工作原理:
这个图示展示了当给定权重为[5, 2, 8, 3]的输入时会发生什么。通过对权重进行部分求和,你只需要找到第一个大于等于一个随机数的部分求和,这个部分求和对应的项就是随机选择的项。for
循环倒序遍历数组,检查i
(for循环变量)是否等于items.indexOf(items[i])
。如果不是,则表示i
处的项是重复的。然后,您只需要将任何非重复项及其权重.push
到空数组中。类似于这样的东西。 - Radvylf Programsi++
应该改为 i--
,而 >
应该改为 >=
。如果你只想在普通数组中删除重复项,而不是在这个答案中使用的 items
/weights
,你可以使用 Array.from(new Set(x))
,其中 x
是要从中删除重复项的数组。 - Radvylf Programs1
、4
和0.5
的选项,它们分别有18.2%、72.7%和9.1%的概率被选择。 - undefined使用一些ES6方法进行通配符处理:
const randomizer = (values) => {
let i, pickedValue,
randomNr = Math.random(),
threshold = 0;
for (i = 0; i < values.length; i++) {
if (values[i].probability === '*') {
continue;
}
threshold += values[i].probability;
if (threshold > randomNr) {
pickedValue = values[i].value;
break;
}
if (!pickedValue) {
//nothing found based on probability value, so pick element marked with wildcard
pickedValue = values.filter((value) => value.probability === '*');
}
}
return pickedValue;
}
示例用法:
let testValues = [{
value : 'aaa',
probability: 0.1
},
{
value : 'bbb',
probability: 0.3
},
{
value : 'ccc',
probability: '*'
}]
randomizer(testValues); // will return "aaa" in 10% calls,
//"bbb" in 30% calls, and "ccc" in 60% calls;
这里有一个O(1)(常数时间)算法来解决您的问题。
生成0到99(共100个数字)之间的随机数。如果在给定的子范围内有40个数字(从0到39),那么随机选择的数字落在此范围内的概率为40%。请参见下面的代码。
const number = Math.floor(Math.random() * 99); // 0 to 99
let element;
if (number >= 0 && number <= 39) {
// 40% chance that this code runs. Hence, it is a bike.
element = "bike";
}
else if (number >= 40 && number <= 69) {
// 30% chance that this code runs. Hence, it is a car.
element = "car";
}
else if (number >= 70 && number <= 84) {
// 15% chance that this code runs. Hence, it is a boat.
element = "boat";
}
else if (number >= 85 && number <= 94) {
// 10% chance that this code runs. Hence, it is a train.
element = "train";
}
else if (number >= 95 && number <= 99) {
// 5% chance that this code runs. Hence, it is a plane.
element = "plane";
}
还记得这个来自小学的数学原理吗?“指定分布中的所有数字在随机选择时有相等的概率。”
这告诉我们,在特定范围内,无论这个范围有多大或多小,每个随机数字都有相等的概率出现。
就是这样。应该可以了!
ES2015版本的Radvylf Programs的答案
function getWeightedRandomItem(items) {
const weights = items.reduce((acc, item, i) => {
acc.push(item.weight + (acc[i - 1] || 0));
return acc;
}, []);
const random = Math.random() * weights[weights.length - 1];
return items[weights.findIndex((weight) => weight > random)];
}
还有ES2022
function getWeightedRandomItem(items) {
const weights = items.reduce((acc, item, i) => {
acc.push(item.weight + (acc[i - 1] ?? 0));
return acc;
}, []);
const random = Math.random() * weights.at(-1);
return items[weights.findIndex((weight) => weight > random)];
}
[0.6,0.9,0.95,0.98,1]
因此,我们将得到一个如下划分的段(大约):[ | | ||]
步骤2
生成0到1之间的随机数,并在前缀和数组中找到其下限。您将找到的索引是随机数所在部分的索引。let obj = {
"Common": "60",
"Uncommon": "25",
"Rare": "10",
"Legendary": "0.01",
"Mythical": "0.001"
}
// turning object into array and creating the prefix sum array:
let sums = [0]; // prefix sums;
let keys = [];
for(let key in obj) {
keys.push(key);
sums.push(sums[sums.length-1] + parseFloat(obj[key])/100);
}
sums.push(1);
keys.push('NONE');
// Step 2:
function lowerBound(target, low = 0, high = sums.length - 1) {
if (low == high) {
return low;
}
const midPoint = Math.floor((low + high) / 2);
if (target < sums[midPoint]) {
return lowerBound(target, low, midPoint);
} else if (target > sums[midPoint]) {
return lowerBound(target, midPoint + 1, high);
} else {
return midPoint + 1;
}
}
function getRandom() {
return lowerBound(Math.random());
}
console.log(keys[getRandom()], 'was picked!');
希望这对你有所帮助。
注意:
(在计算机科学中)列表/数组中一个值的下限是大于或等于该值的最小元素。例如,数组:[1,10,24,99]
和值12。下限将是值为24的元素。
当数组按从小到大排序时(就像我们的情况一样),可以通过二分查找(O(log(n)))非常快地找到每个值的下限。
static weight_random(arr, weight_field){
if(arr == null || arr === undefined){
return null;
}
const totals = [];
let total = 0;
for(let i=0;i<arr.length;i++){
total += arr[i][weight_field];
totals.push(total);
}
const rnd = Math.floor(Math.random() * total);
let selected = arr[0];
for(let i=0;i<totals.length;i++){
if(totals[i] > rnd){
selected = arr[i];
break;
}
}
return selected;
}
像这样运行它(提供数组和权重属性):
const wait_items = [
{"w" : 20, "min_ms" : "5000", "max_ms" : "10000"},
{"w" : 20, "min_ms" : "10000", "max_ms" : "20000"},
{"w" : 20, "min_ms" : "40000", "max_ms" : "80000"}
]
const item = weight_random(wait_items, "w");
console.log(item);
最近我遇到了这个问题,并通过使用switch case解决了它,这很有帮助,因为您可以为每个元素分配多个“case”,从而给它们赋予权重。
我有一个简单的、常见的随机数函数,我用它来生成随机数:
const array = [el1, el2, el3];
const getWeightedElement = () => {
switch (randNum(1, 10)) {
case 10:
return array[2];
case 8:
case 9:
return array[1];
default:
return array[0];
}
}
...
case 1:
case 2:
case 3:
return array[randNum(0, rangeMax)];
当然可以。这里是一个简单的代码来实现它:
// Object or Array. Which every you prefer.
var item = {
bike:40, // Weighted Probability
care:30, // Weighted Probability
boat:15, // Weighted Probability
train:10, // Weighted Probability
plane:5 // Weighted Probability
// The number is not really percentage. You could put whatever number you want.
// Any number less than 1 will never occur
};
function get(input) {
var array = []; // Just Checking...
for(var item in input) {
if ( input.hasOwnProperty(item) ) { // Safety
for( var i=0; i<input[item]; i++ ) {
array.push(item);
}
}
}
// Probability Fun
return array[Math.floor(Math.random() * array.length)];
}
console.log(get(item)); // See Console.