【问题标题】:Where am I losing randomness?我在哪里失去随机性?
【发布时间】:2019-08-08 12:07:51
【问题描述】:

所以我正在构建一个石头、纸、剪刀游戏。我想让 math.random() 给我一个随机结果,我将其与用户选择的结果进行比较。

它主要是有效的。我什至认为它在一段时间内完全有效,但我在某些时候失去了随机性。

为了尝试它,我输入了一个固定的“用户”选项,并使用 setInterval(myfunction, 20) 运行代码几百次。他们总是不平衡的胜利,而且总是有相同的结果:

如果我用 playerPick = 1 运行它,计算机总是获胜。

如果我使用 playerPick = 2 或 3 运行它,用户总是获胜。

任何人都可以看到我犯错的地方吗?

//Global variable and constants.

const ROCK = 1;
const PAPER = 2;
const SCISSORS = 3;

//This is the game.

function whoWins(){
    const playerPick = 2; //for debugging, it can be 1, 2, or 3.
    const computer = computerPick();
    if (playerPick == computer){
        return draw();
    } else if (playerPick == 1 && computer == 2){
        return lose();
    } else if (playerPick == 2 && computer == 3){
        return lose();
    } else if (playerPick == 3 && computer == 1){
        return lose();
    } else {
        return win();
    }
}

//These are the inputs for the game.

rockButton.addEventListener('click', () => {
    playerPick = ROCK;
    return whoWins()});
paperButton.addEventListener('click', () => {
    playerPick = PAPER;
    return whoWins()});
scissorsButton.addEventListener('click', () => {
    playerPick = SCISSORS;
    return whoWins()});

function computerPick() {
    let computerChoice = '';
    const getRandom = Math.random;
    if (getRandom() >= 2/3) {
        computerChoice = ROCK;
    } else if (getRandom() >= 1/3){
        computerChoice = PAPER;
    } else {
        computerChoice = SCISSORS;
    }
    return computerChoice;
}

我对这一切都很陌生,但仍然不是随机的。

【问题讨论】:

  • 您在 const getRandom = Math.random; 上使用方法 Math.random 您必须实际调用该方法 (Math.random()),通过省略括号,您只是在使用方法引用而不是实际生成随机数号码。
  • 一个错误...您为每台计算机调用两次 getRandom() - 但是,这并不能解释结果的“总是”部分
  • 我会考虑将 getRandom() 乘以 100 并处理整数值(例如 > 66、> 33)。分数和计算机可能很棘手。
  • @Joe - 嗯? 1/3 和 2/3 并不棘手
  • @LukasBach 他们稍后会使用getRandom() 调用该函数 - 但可能不需要在一个 if-else 语句中多次调用它

标签: javascript


【解决方案1】:

这应该是一个简单的修复,如 cmets 中所述,您需要调用一次 Math.random,否则概率会出现偏差。

我认为使用原始代码的 PAPER 的概率应该是 0.66 * 0.66 = ~ 44%,而 SCISSORS 的概率应该是 0.66 * 0.33 = ~ 22%。新功能应该可以解决这个问题。

const ROCK = 1;
const PAPER = 2;
const SCISSORS = 3;

// Original computerPick function
function computerPickOriginal() {
    let computerChoice = '';
    const getRandom = Math.random;
    if (getRandom() >= 2/3) {
        computerChoice = ROCK;
    } else if (getRandom() >= 1/3){
        computerChoice = PAPER;
    } else {
        computerChoice = SCISSORS;
    }
    return computerChoice;
}

// Fixed computerPick function.
function computerPick() {
    let computerChoice = '';
    const choice = Math.random();
    if (choice >= 2/3) {
        computerChoice = ROCK;
    } else if (choice >= 1/3){
        computerChoice = PAPER;
    } else {
        computerChoice = SCISSORS;
    }
    return computerChoice;
}

function decodeChoice(choice) {
    if (choice == ROCK) return "Rock";
    if (choice == PAPER) return "Paper";
    if (choice == SCISSORS) return "Scissors";
}

// Check the distribution of each version of the code.
console.log("Checking distributions (10000 picks)..");
let original_counts = {};
let counts = {};
for(let i = 0; i < 10000; i++) {
  let k = computerPick();
  counts[k] = (counts[k] || 0) + 1;
  let k2 = computerPickOriginal();
  original_counts[k2] = (original_counts[k2] || 0) + 1;
}

console.log('Computer Pick Distribution (original): ', Object.entries(original_counts).map(([key,value]) => `${decodeChoice(key)}: ${value}`));
console.log('Computer Pick Distribution (fixed): ', Object.entries(counts).map(([key,value]) => `${decodeChoice(key)}: ${value}`));

【讨论】:

  • 您好,感谢您的回答,已解决。我不确定代码的第二部分是做什么的(从 const getRandom ... 开始)。只是为了检查分布吗?我还注意到您在顶部使用 math.random 并在底部使用 math.random() (带和不带括号)。有什么区别?
  • 哦,是的,对不起@Billedale,我可能只需要稍微清理一下代码:).. 是的,第二部分检查原始发行版和固定版本。我们可以看到你的原始版本给出了大约 44% 的机会和 22% (0.66 * 0.33) 的机会。这是一个非常微妙的错误!
【解决方案2】:

为了将来参考,您可能会发现使用数组更容易。

const getRandomChoice = () => {
  const options = ['Rock', 'Paper', 'Scissors'];

  const randomIndex = Math.floor(Math.random() * options.length);

  const choice = options[randomIndex];
  return choice;
}

const counts = {};

for(let i = 0; i < 10000; i++) {
  let p = getRandomChoice();
  counts[p] = (counts[p] || 0) + 1;
}

console.log(counts);

通过将Math.random 的结果乘以数组 (3) 的长度,我们可以得到一个介于 [0, 3) 之间的值(不包括 3)。然后我们调用Math.floor 来“删除”任何小数。

【讨论】:

  • 谢谢,我会记住的。我只有一个问题,根据我从 getRandomChoice 的了解,Scissors 是如何获得与其他两个一样多的命中的?为什么在使用 Math.floor 时它不会被截断,因为它会向下舍入到最接近的整数?因此例如:应用 Math.floor 时 randomIndex = 0.8*3 = 2.4 = 2?
  • @Billedale - 因为生成的数字是 0、1 和 2,所以它的命中数一样多。数组从索引 0 开始访问,因此它们被命中的机会相同跨度>
  • 这样想。 Math.random 生成一个介于 0 和 1 之间的数字(包括 0,但不包括 1)。所以拿下面的例子| 0.1 * 3 ==> 0.3 ==> 0 | | 0.35 * 3 ==> 1.05 ==> 1 | | 0.9999999 * 3 ==> 2.9999997 => 2 |
  • 啊!让我困惑的是开头的0。非常感谢,我将来肯定会使用它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-03
相关资源
最近更新 更多