【问题标题】:Ruby - Picking an element in an array with 50% chance for a[0], 25% chance for a[1]Ruby - 在数组中选择一个元素,a[0] 有 50% 的机会,a[1] 有 25% 的机会
【发布时间】:2011-05-16 16:15:04
【问题描述】:

没什么太复杂的,基本上我只是想从数组中挑选一个元素,就好像我在为每个索引掷硬币并在我第一次得到正面时选择索引一样。也没有人头意味着我选择了最后一个箱子。

我想出了以下方法,想知道是否有更好/更有效的方法。

def coin_toss(size)
  random_number = rand(2**size)
  if random_number == 0
    return size-1
  else
    return (0..size-1).detect { |n| random_number[n] == 1 }
  end
end

【问题讨论】:

  • 不应该是索引(0..index-1)吗?索引大小超出范围。另外,为什么你传递数组的大小而不是数组本身(然后返回值 a[i] 而不是 i)
  • 已修复,谢谢。只是通过了大小,因为我正在计时各种方法并且实际上拥有一个数组并不重要。

标签: ruby probability


【解决方案1】:

首先猜测...在 1 和 2**size 之间选择一个随机数,找到它的对数基数 2,然后从末尾选择多个元素的数字。

原谅我可怕的红宝石技能。

return a[-((Math.log(rand(2**size-1)+1) / Math.log(2)).floor) - 1]

如果rand 返回 0,则应选择最后一个元素。 1 或 2,倒数第二。 3、4、5 或 6,倒数第三个。等等。假设随机数分布均匀,每个元素被选中的机会是其后一个元素的两倍。

编辑:实际上,似乎有一个 log2 函数,所以我们不必做 log/log(2) 的事情。

return a[-(Math.log2(rand(2**size - 1)+1).floor) - 1]

您也许可以完全摆脱这些日志调用,例如

return a[-((rand(2**size-1)+1).to_s(2).length)]

但是您正在创建一个额外的String。不确定这是否比复杂的数学更好。 :)

编辑:实际上,如果你要走字符串路线,你可以完全摆脱 +1 和 -1。它会使概率更准确,因为最后两个元素应该有平等的机会被选中。 (如果未选择倒数第二个值,则始终是最后一个值。)

编辑:我们还可以将** 转换为位移位,这应该会更快一些(除非 Ruby 已经足够聪明,可以做到这一点)。

return a[-(rand(1<<size).to_s(2).length)]

【讨论】:

  • 数学有点复杂......我希望看到它更简单。用一种额外的、更简单(但更混乱)的方式进行了更新。
  • 非常有趣的方法。我用 size = 20 (实际上对我来说最常见的值)对它进行了基准测试,你的方法花了我一半的时间,但不幸的是一半的时间它只是失败了,“Errno::EDOM: Numerical argument out of domain - log”
  • 有点奇怪。 +1 应该将rand 的返回值轻推到日志函数的域中。 (log(1) 为零;log(0) 是未定义或 -infinity,我忘了)
  • 啊,我在输入 irb 时错过了 + 1。对此感到抱歉。
  • to_s(2).length 以 70% 的日志版本时间获胜,太好了!
【解决方案2】:

一个不聪明的简单方法是:

def coin_toss( arr )
  arr.detect{ rand(2) == 0 } || arr.last
end

【讨论】:

  • 不如日志版本快,但仍然比我的快。谢谢
猜你喜欢
  • 1970-01-01
  • 2023-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多