让我们说,我想按百分比机会触发一个功能
.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(); }
非常长而且凌乱
问题是我有什么方法可以做得更好更有效吗?与上面显示的这两种方法不同。
* 公平正方 也很重要(听起来像游戏)
很抱歉,如果我严重解释情况,我们将不胜感激。感谢。
答案 0 :(得分:3)
您可以创建要调用的函数列表以及调用它们的机会。然后使用机会将随机数的范围划分为块。在这个例子中,它将是:
0.0 0.5 0.6 0.7
0.5/A 0.1/B 0.1/C
不需要根据机会对条目进行排序。如果机会的总和大于1.0,则不会调用数组的最后元素。这是确保自己采用这种方法所需的一切。
代码可能如下所示:
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;
答案 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)现在我们将找到一种方法,用应该触发的函数映射范围。
最简单的方法是创建另一个数组,其函数放在与其对应的范围上限相同的索引中,并且 - 如前所示: -
我们会得到
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}`);
}
希望这有帮助。