Javascript以随机百分比机会执行函数

时间:2018-05-10 08:35:23

标签: javascript random

让我们说,我想按百分比机会触发一个功能

.p12

现在我想添加更多功能以偶然触发,我所做的是

.pfx

但是这使得function A () { console.log('A triggered'); } //50% chance to trigger if (Math.random() >= 0.5) A(); //method 1 function B () { console.log('B triggered'); } //10% chance to trigger function C () { console.log('C triggered'); } //10% chance to trigger if (Math.random() >= 0.5) { A(); } else if (Math.random() > (0.5 + 0.1)) { B(); } else if (Math.random() > (0.5 + 0.1 + 0.1)) { C(); } 之前获得优先权。因此我将代码更改为

A()

此方法看起来 合理 ,但它看起来效率低下,因为它需要在每个B() and C()函数中都有硬编码的机会,如果我有一个列表长< em>功能和触发机会,我需要让//method 2 var randNumber = Math.random(); if (randNumber <= 0.5) { A(); } else if (randNumber > 0.5 && randNumber <= (0.5 + 0.1)) { B(); } else if (randNumber > (0.5 + 0.1) && randNumber <= (0.5 + 0.1 + 0.1)) { C(); } 非常长而且凌乱

问题是我有什么方法可以做得更好更有效吗?与上面显示的这两种方法不同。

* 公平正方 也很重要(听起来像游戏)

很抱歉,如果我严重解释情况,我们将不胜感激。感谢。

4 个答案:

答案 0 :(得分:3)

您可以创建要调用的函数列表以及调用它们的机会。然后使用机会将随机数的范围划分为块。在这个例子中,它将是:

0.0         0.5           0.6           0.7
     0.5/A         0.1/B         0.1/C

不需要根据机会对条目进行排序。如果机会的总和大于1.0,则不会调用数组的最后元素。这是确保自己采用这种方法所需的一切。

代码可能如下所示:

&#13;
&#13;
function A() {
  console.log('A was called')
}

function B() {
  console.log('B was called')
}

function C() {
  console.log('C was called')
}


var list = [
  {chance: 0.5, func: A},
  {chance: 0.1, func: B},
  {chance: 0.1, func: C}
];

function callRandomFunction(list) {
  var rand = Math.random() // get a random number between 0 and 1
  var accumulatedChance = 0 // used to figure out the current

  var found = list.find(function(element) { // iterate through all elements 
    accumulatedChance += element.chance // accumulate the chances
    return accumulatedChance >= rand // tests if the element is in the range and if yes this item is stored in 'found'
  })

  if( found ) {
    console.log('match found for: ' + rand)
    found.func()
  } else {
    console.log('no match found for: ' + rand)
  }
}

callRandomFunction(list)
&#13;
&#13;
&#13;

答案 1 :(得分:2)

  

这种方法看起来很公平但看起来效率低下,因为它需要在每一个if else

中都有硬编码的机会

这是真的,虽然下限是不必要的,因为你正在使用else。此外,使用数学使它看起来比它更复杂:

var randNumber = Math.random();
if (randNumber      <= 0.5) { A(); }
else if (randNumber <= 0.6) { B(); }
else if (randNumber <= 0.7) { C(); }

如果它们之间的间隔是固定的,则可以避免使用这些硬编码值:

var randNumber = Math.random();
var chance = 0.5;
var interval = 0.1;
if (randNumber      <= chance)               { A(); }
else if (randNumber <= (chance += interval)) { B(); }
else if (randNumber <= (chance += interval)) { C(); }

或者实际上,我想即使它不是:

var randNumber = Math.random();
var chance = 0.5;
if (randNumber      <= chance)          { A(); }
else if (randNumber <= (chance += 0.1)) { B(); }
else if (randNumber <= (chance += 0.2)) { C(); } // Intentionally different from
                                                 // yours to show different spacing

另一种方法是将只能转换为可预测字符串¹(例如0-9)并使用调度对象的数字:

var dispatch = {
    0: A,
    1: A,
    2: A,
    3: A,
    4: A,
    5: B,
    6: C
};
var f = dispatch[Math.floor(Math.random() * 10)];
if (f) {
    f();
}

请注意,虽然使用上面的数字文字编写,但dispatch对象的属性名称实际上是字符串("0""1"等。)。

但是,带有列表的

t.niese's approach优于此调度对象。

¹基本上这意味着避免(某些)小数值并保持在合理的数值范围内。例如,如果您的计算最终执行0.009 / 3之类的操作而您(合理地)希望能够使用"0.003"作为您的密钥,那么它将无效,因为{{1}由于IEEE-754双精度二进制浮点不精确,因此是String(0.009 / 3),而不是"0.0029999999999999996"。 (虽然"0.003"没问题,但我们会0.09 / 3)。

答案 2 :(得分:1)

这就是我要做的事情。

let outcomes = [
    { chance: 50, action:() => { console.log('A'); } },
    { chance: 10, action:() => { console.log('B'); } },
    { chance: 10, action:() => { console.log('C'); } }
]

function PerformAction(actions){
    let totalChance = actions.reduce((accum, current)=>{
        return accum + current.chance;
    }, 0);

    if(totalChance > 100){
        throw Error('Total chance cannnot exceed 100%');
    }

    if(totalChance < 100) {
        actions.push({
            chance: 100 - totalChance,
            action: function(){
                console.log('No action taken');
            }
        })
    }

    let outcome = Math.random() * 100;

    actions.find((current)=>{        
        outcome = outcome - current.chance;
        return outcome <= 0;
    }).action();
}

PerformAction(outcomes);

答案 3 :(得分:1)

让我们把它分成几部分:

1)范围 创建一个包含所有范围的数组&#39;例如,上限,假设你想要以下范围0 - 10,11 - 20和21到100,那么数组将是:

const arr = [10, 20, 100]

为了找到正确的范围:

let randNumber = Math.random() * 100;
let rangeFound = false;

for (i = 0; i < arr.length && !rangeFound; i++) { 

    let lowerBound = i>0?arr[i-1]:-1,
        upperBound = arr[i];

    if(randNumber > lowerBound && randNumber <= upperBound){
        //save a reference to the current range (index?)
        rangeFound = true;
    }
}

此时我们知道randNumber在哪个范围内。

2)现在我们将找到一种方法,用应该触发的函数映射范围。

最简单的方法是创建另一个数组,其函数放在与其对应的范围上限相同的索引中,并且 - 如前所示: -

  • 0到10 - 功能A()
  • 11至20 - 功能B()
  • 21到100 - 功能C()

我们会得到

const func = [A, B, C]

为了保存&#34;范围索引&#34;并且知道应该触发哪个函数,我将在下面的代码中使用变量rangeIndex

function A() { /*do something A*/}
function B() { /*do something B*/}
function C() { /*do something C*/}


    const arr = [10, 20, 100];
const func = [A, B, C];

let randNumber = Math.random() * 100;
let rangeFound = false;
let rangeIndex = -1;

for (i = 0; i < arr.length && !rangeFound; i++) { 

    let lowerBound = i>0?arr[i-1]:-1,
        upperBound = arr[i];

    if(randNumber > lowerBound && randNumber <= upperBound){
        rangeIndex = i;
        rangeFound = true;
    }
}

if(rangeFound){
    let f = func[rangeIndex];
    f();
} else {
    console.warn(`There is no suitable range for ${randNumber}`);
}

希望这有帮助。