当前位置: 首页 > 知识库问答 >
问题:

如何在Javascript中选择加权随机数组元素?

左丘峰
2023-03-14

例如:数组中有四个项目。我想随机获取一个,如下所示:

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
]

共有3个答案

赫连昕
2023-03-14

这里有一个比其他答案更快的方法。。。

您可以通过以下方式实现您想要的:

  1. 根据每个元素的概率将0到1段划分为部分(例如,概率为60%的元素将占用60%的段)。
  2. 生成一个随机数并检查它落在哪个段。

第一步

为概率数组创建一个前缀和数组,其中的每个值将表示其相应部分的结束位置。

例如:如果我们有概率:60%(0.6),30%,5%,3%,2%。前缀和数组将是:[0.6,0.9,0.95,0.98,1]

所以我们将有一个这样(近似)划分的部分:[| | | |]

第二步

生成一个介于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)),可以非常快地找到每个值的下界。

孟胤
2023-03-14

一些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;
桓高澹
2023-03-14

以上两个答案都依赖于快速变慢的方法,尤其是公认的方法。

function weighted_random(items, weights) {
    var i;

    for (i = 0; i < weights.length; i++)
        weights[i] += weights[i - 1] || 0;
    
    var random = Math.random() * weights[weights.length - 1];
    
    for (i = 0; i < weights.length; i++)
        if (weights[i] > random)
            break;
    
    return items[i];
}

从2020年12月起,我用这个解决方案替换了我的旧ES6解决方案,因为旧浏览器不支持ES6,我个人认为这个更具可读性。

如果您更愿意使用属性为重量的对象:

function weighted_random(options) {
    var i;

    var weights = [];

    for (i = 0; i < options.length; i++)
        weights[i] = options[i].weight + (weights[i - 1] || 0);
    
    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]的输入时发生的情况。通过计算权重的部分和,你只需要找到第一个和随机数一样大的,那就是随机选择的项目。

如果在两个权重的边界上选择一个随机数,比如图中的715,我们选择较长的一个。这是因为数学可以选择0。随机但是1不能,所以我们得到了一个公平的分布。如果我们选择较短的一个,A可以在18次选择中选择6次(01234),赋予它更高的权重。

 类似资料:
  • 问题内容: 我想从集合中选择一个随机项目,但是选择任何项目的机会应与相关的权重成比例 输入示例: 因此,如果我有4种可能的物品,那么没有重量的任何一件物品的机会将是四分之一。 在这种情况下,用户遭受痛苦之剑的可能性应该是三刃剑的十倍。 如何在Java中进行加权随机选择? 问题答案: Apache Commons中现在有一个用于此的类: 这里是,像(假设Item接口阿恩的答案): 或在Java 8中

  • 问题内容: 我正在寻找从整数数组中随机选择数字的解决方案。 例如我有一个数组,如何随机选择一个数字? 问题答案:

  • 本文向大家介绍在JavaScript中随机选择元素的功能,包括了在JavaScript中随机选择元素的功能的使用技巧和注意事项,需要的朋友参考一下 假设我们有一个不包含像这样的重复元素的文字数组- 我们需要编写一个JavaScript函数,该函数接受一个唯一文字数组和一个数字n。该函数应返回n个元素的数组,这些元素都是从输入数组中随机选择的,并且任何元素在输出数组中不应出现多次。 因此,让我们为该

  • 问题内容: 假设我有一个数组,我想随机选择一个元素。 最简单的方法是什么? 明显的方法是。但是也许有红宝石之类的东西?或者如果不能通过扩展创建这种方法? 问题答案: Swift 4.2及更高版本 推荐的新方法是Collection协议的内置方法:。它返回一个可选参数以避免我以前假设的空情况。 如果不创建数组并且不能保证count> 0,则应执行以下操作: Swift 4.1及以下 只是为了回答您的

  • 假设我有一个数组,我想随机选择一个元素。 最简单的方法是什么? 最明显的方法是数组[随机索引]。但可能有类似ruby的数组。示例 ?或者,如果不是,那么可以使用扩展创建这样的方法吗?

  • 假设我有一个随机选择的项目池。 我使用一个简单的加权选择算法来做到这一点: 计算项目权重总和 在0和权重和之间选择一个随机数 迭代项目,并按项目权重减少,选择项目时 同时,约束传播算法更新可用项目池。 例如,假设我们有一个N乘N的网格,每个单元格可以选择一个数字 使用上述算法,通过加权选择完成选择 一旦一个单元选择了它的编号,它还会使用一些规则限制相邻单元的可用编号 我的问题是: 假设牢房A和B是