分多次累计随机出某指定整数(多次随机整数,其和固定)的方法
Spads
Shane Loo Li
本文分为 5 个部分
---------- ---------- ---------- ----------
1、提出问题
2、解法程序
3、测试结果
4、测试程序
5、公式证明
【提出问题】
---------- ---------- ---------- ----------
有 n 次机会,每次随机一个整数。希望这 n 个整数之和是 m ;该怎么随机呢?
对于编程语言,惯例是提供了随机函数 r() ,得到 [0, 1) 之间的一个随机浮点数。所以从编程角度来说,随机一个整数,最常见的方式就是通过对 L * r() 向下取整来获取某一个范围内的整数。以上问题就转变成为了,如何获得合适的 L ,来满足 n 次随机的整数之和为 m 。
传统的做法,就是用 2 * m / n 来做这个 L 。问题是因为取整这个操作,让这种算法会产生比较大的误差。具体误差有多大,下边测试结果一栏会详细描述。
【解法程序】 —— 以 Java 程序为例
【测试结果】
---------- ---------- ---------- ----------
目标总和为 5000000
随机次数 20000
简易方法: 实际随机数的总和 = 4993051, 误差 = 0.1389%
Spads Shane的新方法: 实际随机数的总和 = 4997315, 误差 = 0.0537%
随机次数 50000
简易方法: 实际随机数的总和 = 4946686, 误差 = 1.10661%
Spads Shane的新方法:
Spads
Shane Loo Li
本文分为 5 个部分
---------- ---------- ---------- ----------
1、提出问题
2、解法程序
3、测试结果
4、测试程序
5、公式证明
【提出问题】
---------- ---------- ---------- ----------
有 n 次机会,每次随机一个整数。希望这 n 个整数之和是 m ;该怎么随机呢?
对于编程语言,惯例是提供了随机函数 r() ,得到 [0, 1) 之间的一个随机浮点数。所以从编程角度来说,随机一个整数,最常见的方式就是通过对 L * r() 向下取整来获取某一个范围内的整数。以上问题就转变成为了,如何获得合适的 L ,来满足 n 次随机的整数之和为 m 。
传统的做法,就是用 2 * m / n 来做这个 L 。问题是因为取整这个操作,让这种算法会产生比较大的误差。具体误差有多大,下边测试结果一栏会详细描述。
【解法程序】 —— 以 Java 程序为例
---------- ---------- ---------- ----------
/**
* <b>获取总量固定多次随机的倍率</b><br/>
* 当程序需要通过一定次数随机,每次随机一个整数,最终获取总和一定的值,
* 可通过此方法获得随机倍率。<br/>
* 在获取此倍率 <code>randomLimit</code> 之后,每次随机时通过
* <code>(int) (new Random().nextDouble() * randomLimit)</code> 获得随机
* 结果。<br/><br/>
* 本方法的核心算法,基于证明了如下二个关系式:
* <pre>
* (int) (2 * totalNum / chanceCount) + 1 < randomLimit
* (int) (2 * totalNum / chanceCount) + 2 > randomLimit
* </pre>
* 具体推算方法,请见 Spads 的 Shane Loo Li 发表的日志。<br/>
* http://blog.csdn.net/shanelooli/article/details/10831811
* @param totalNum 最终希望各随机值相加后的总量
* @param chanceCount 随机次数
* @return 每次随机,[0, 1) 标准随机值应该乘以的倍率
*/
static public double getRandomLimit(int totalNum, int chanceCount)
{
double calculateBase = 2.0 * totalNum / chanceCount;
int calculateBaseInt = (int) calculateBase;
double randomLimit = (calculateBaseInt + 2) * (calculateBaseInt + 1)
/ (2 * calculateBaseInt - calculateBase + 2);
return randomLimit;
}
【测试结果】
---------- ---------- ---------- ----------
目标总和为 5000000
随机次数 20000
简易方法: 实际随机数的总和 = 4993051, 误差 = 0.1389%
Spads Shane的新方法: 实际随机数的总和 = 4997315, 误差 = 0.0537%
随机次数 50000
简易方法: 实际随机数的总和 = 4946686, 误差 = 1.10661%
Spads Shane的新方法: