当从初始化程序调用时,Ruby随机数生成器出现偏差

3
我正在尝试模拟蒙提霍尔问题,其中用户被要求选择三个门之一,然后提供更改选择的选项。我正在寻找随机选择的变化,应该在50%更改和50%保持不变的范围内。
class Scenario
  def initialize
    # switched?
  end

  def switched?
    @sw ||= [true, false].sample
  end
end

results = { switched: [], stayed: [] }

1000.times do
  s = Scenario.new

  if s.switched?
    results[:switched].push(s)
  else
    results[:stayed].push(s)
  end
end

puts results[:switched].count
puts results[:stayed].count

当我检查结果哈希表时,在这个例子中,数组计数 tend to be about 500/500 as expected。
然而,如果我在 initializer 方法中取消注释 switched?,结果 tend to be split about 750/250。结果仍然看起来是随机的(例如738 到 262),但它们总是倾向于成为该问题的错误解决方案。
我还尝试使用其他机制比如 rand(2).zero? 来生成数据,但是同样的问题发生了。
为什么或者怎么会调用这个记忆化函数在初始化器中导致随机变异如此远离正确结果,而且始终如此?

你能展示两种情况下的 initialize 方法吗?你在 initialize 方法中使用了 ||= 吗? - Ray Toal
请提供一个 [mcve]。您已经对行为进行了很好的解释,但是生成此行为的不完整代码示例 - 因此我不知道如何复现您的问题。 - Tom Lord
4
不过猜测一下,问题不是你调用了 switched? 两次,只有当变量为 true 时才会被记忆化吗?如果你随机选择 false,那么 ||= 就会被重新评估;因此 true 会被选中 75% 的时间。 - Tom Lord
汤姆,你干得好。我一直认为条件赋值运算符只在nil时重新分配,但你是对的,它也适用于false。第一次通过了50%,下一次再通过50%意味着大约75%会被切换。 - Violet Shreve
1个回答

5
在 Ruby 中,所有值都被视为“真值”,除了 nilfalse
当你调用 foo ||= bar 时,只有在 foo 是“假值”(即等于 nil 或者 false),bar 才会被计算。(或者如果它没有被定义!)
在你的代码中,你拥有以下内容:
def switched?
  @sw ||= [true, false].sample
end

因此,只有当[true, false].sample返回true时,@sw变量才会备忘方法调用的结果!!这意味着,如果您多次调用switched?,则为@sw变量提供了“多次尝试”随机选择true的机会。如果您调用switched?一次,则有50%的概率为true。如果调用两次,则有75%的概率(如您所观察到的)。如果调用3次,则有87.5%的机会。等等。为了备忘可能的false值,您需要使用更明确的语法 - 例如:
def switched?
  return @sw if defined?(@sw)
  @sw = [true, false].sample 
end

现在您可以安全地多次调用switched?方法。即使第一个结果为false,它也会记住它,并且不会重新计算。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接