从 5 次掷骰子中,生成一个范围为 [1 - 100] 的随机数

Posted

技术标签:

【中文标题】从 5 次掷骰子中,生成一个范围为 [1 - 100] 的随机数【英文标题】:From 5 dice rolls, generate a random number in the range [1 - 100] 【发布时间】:2014-10-28 14:32:54 【问题描述】:

我正在做一些编码练习,但遇到了这个问题:

从 5 个骰子(6 面)掷骰中,生成一个范围为 [1 - 100] 的随机数。

我实现了以下方法,但是返回的数字不是随机的(调用函数1,000,000次,有几个数字从未出现在1 - 100中)。

public static int generator() 

     Random rand = new Random();
     int dices = 0;

     for(int i = 0; i < 5; i++) 
         dices += rand.nextInt(6) + 1;
     

     int originalStart = 5;
     int originalEnd = 30;
     int newStart = 1;
     int newEnd = 100;

     double scale = (double) (newEnd - newStart) / (originalEnd - originalStart);
     return (int) (newStart + ((dices - originalStart) * scale));

【问题讨论】:

不要每次都创建Random的新实例,为每个方法调用维护一个实例。虽然您可能仍然会“丢失”数字,但范围会更加随机。 只使用第一个骰子。它将返回一个 1-100 之间的随机数。那或者请澄清你的问题。 他需要模拟掷骰子。你最后一次看到有 99 个面的骰子是什么时候? 我以前见过一个有 100 个面的骰子。 @Slavic 不幸的是,他们存在 【参考方案1】:

将一个 6 面骰子滚动 5 次会产生 6^5 = 7776 个可能的序列,所有这些序列的可能性都相同。理想情况下,您希望将这些序列划分为 100 个大小相等的组,并且您将拥有 [1 - 100] rng,但由于 7776 不能被 100 整除,因此这是不可能的。为使偏差最小化,您可以做的最好的事情是 76 个组分别映射到 78 个序列和 24 个组,每个组映射到 77 个序列。将(有序)掷骰子编码为基数为 6 的数字 n,并返回 1 + (n % 100)。

不仅没有办法用 5 次掷骰子来消除偏差,而且没有多少掷骰子可以完全消除偏差。没有 6^k 可以被 100 整除的 k 值(考虑素数分解)。这并不意味着没有办法消除偏差,它只是意味着您无法使用保证在任何特定数量的骰子掷骰后终止的程序来消除偏差。但是例如,您可以进行 3 次掷骰,产生 6^3 = 216 个编码为基数 6 数 n 的序列,如果 n = 200,您必须重复这个过程,并不断重复,直到你得到 n

【讨论】:

【参考方案2】:

问题是 5-30 中没有足够的随机值来将一对一映射到 1-100 区间。这意味着某些值注定永远不会出现;这些“丢失”值的数量取决于两个区间的大小比例。

不过,您可以以更有效的方式利用骰子的力量。以下是我的做法:

方法 1

    使用第一个骰子的结果从 6 个相等的子区间,大小为 16.5 (99/6)。 使用第二个骰子的结果从您在第一步中选择的子区间的 6 个相等的子子区间中选择一个子区间。 使用...我想你知道接下来会发生什么。

方法 2

使用 base-6 系统中的数字构造您的随机数。 IE。第一个骰子将是基数为 6 的数字的第一个数字,第二个骰子 - 第二个数字,等等。 然后转换为以 10 为底,除以 (46656/99)。你应该有你的随机数。实际上你可以只用 3 个骰子,当然,剩下的两个只是多余的。

【讨论】:

为什么不呢? 1-100 中的任何数字都有相同的机会出现。或者至少现在在我看来是这样。 你将不得不采用一个 floor 函数,并且你最终切入的间隔不会相对于整数之间的线均匀分布。试着在一行上写下刻度 3,6,9,12,15... 然后打勾 2,4,6,8,... 你就会明白我的意思了。 或者更多的数字,整数的任意组合 (a,b,c,d,e,f) --> 一个数字 1-100。你真的认为你可以均匀地做到这一点吗?不;和原来的数学问题是一样的,浮点法只是为了计算绕道而行。【参考方案3】:

好的,所以 5 个骰子,每个有 6 个选项。如果它们未订购,则如上所述,您的范围为 5-30 - 1-100 永远不够。

您需要假设一个订单,这会给您一个 1,1,1,1,1 - 6,6,6,6,6(以 6 为底)的比例,假设 1 --> 0 值,您有一个生成 5 位基数为 6 的数字。众所周知,6^5 = 7776 种独特的可能性。 ;)

为此,我将为您提供一个有偏见的随机解决方案。

int total = 0;
int[] diceRolls;
for (int roll : diceRolls) 
    total = total*6 + roll - 1;


return total % 100 + 1;

感谢 JosEdu 澄清括号要求

此外,如果您想消除偏差,您可以将范围除以我在上面的描述中给出的 maxval,然后乘以您的总数(然后添加偏移量),但您仍然需要确定您使用的舍入规则.

【讨论】:

有趣地类似于斯拉夫人的答案,他一定是在我写这篇文章的同时写的,只是我使用整数而不是小数。 :) 不需要括号,% 运算符优先于 +。

以上是关于从 5 次掷骰子中,生成一个范围为 [1 - 100] 的随机数的主要内容,如果未能解决你的问题,请参考以下文章

将随机范围从 1-5 扩展到 1-7

计蒜客 掷骰子 dp

使用概率分布扩展整数的随机范围

基础入门题049骰子游戏

生成一维整数数组,数组长度为 10,范围从 1 到 20

JAVA怎样随机生成10W个数字, 要求: 10W个数字总等于50W而且每个数字最小1最大100, 求代码及思路