Mark的解决方案(已接受的解决方案)几乎是完美的。
int x;
do {
x = rand();
} while (x >= (RAND_MAX - RAND_MAX % n));
x %= n;
于2016年3月25日23:16编辑
Mark Amery 39k21170211
但是,它有一个警告,在RAND_MAX(RM)小于N的倍数(其中N =可能的有效结果的数量)的任何情况下,丢弃1个有效的结果集。
即,当“丢弃的值的数量”(D)等于N时,它们实际上是有效集合(V),而不是无效集合(I)。
使用Mark的解决方案,在以下情况下丢弃值:X => RM - RM%N
EG:
Ran Max Value (RM) = 255
Valid Outcome (N) = 4
When X => 252, Discarded values for X are: 252, 253, 254, 255
So, if Random Value Selected (X) = {252, 253, 254, 255}
Number of discarded Values (I) = RM % N + 1 == N
IE:
I = RM % N + 1
I = 255 % 4 + 1
I = 3 + 1
I = 4
X => ( RM - RM % N )
255 => (255 - 255 % 4)
255 => (255 - 3)
255 => (252)
Discard Returns $True
正如您在上面的示例中所看到的,当X的值(我们从初始函数得到的随机数)为252,253,254或255时,我们会丢弃它,即使这四个值包含一组有效的返回值。
IE:当值的计数Discarded(I)= N(有效结果的数量)时,原始函数将丢弃有效的返回值集。
如果我们将值N和RM之间的差异描述为D,即:
D = (RM - N)
然后随着D的值变小,由于该方法而导致的不需要的重新滚动的百分比在每个自然乘法处增加。 (当RAND_MAX不等于素数时,这是有效关注的)
例如:
RM=255 , N=2 Then: D = 253, Lost percentage = 0.78125%
RM=255 , N=4 Then: D = 251, Lost percentage = 1.5625%
RM=255 , N=8 Then: D = 247, Lost percentage = 3.125%
RM=255 , N=16 Then: D = 239, Lost percentage = 6.25%
RM=255 , N=32 Then: D = 223, Lost percentage = 12.5%
RM=255 , N=64 Then: D = 191, Lost percentage = 25%
RM=255 , N= 128 Then D = 127, Lost percentage = 50%
由于Rerolls所需的百分比增加,N越接近RM,这可能是许多不同值的有效关注点,这取决于运行代码的系统的约束和所寻找的值。
为了否定这一点,我们可以做一个简单的修改如下所示:
int x;
do {
x = rand();
} while (x > (RAND_MAX - ( ( ( RAND_MAX % n ) + 1 ) % n) );
x %= n;
这提供了一个更通用的公式版本,它解释了使用模数来定义最大值的额外特性。
使用RAND_MAX的小值的示例,其是N的乘法。
Mark'original版本:
RAND_MAX = 3, n = 2, Values in RAND_MAX = 0,1,2,3, Valid Sets = 0,1 and 2,3.
When X >= (RAND_MAX - ( RAND_MAX % n ) )
When X >= 2 the value will be discarded, even though the set is valid.
广义版本1:
RAND_MAX = 3, n = 2, Values in RAND_MAX = 0,1,2,3, Valid Sets = 0,1 and 2,3.
When X > (RAND_MAX - ( ( RAND_MAX % n ) + 1 ) % n )
When X > 3 the value would be discarded, but this is not a vlue in the set RAND_MAX so there will be no discard.
另外,在N应该是RAND_MAX中的值的数量的情况下; 在这种情况下,您可以设置N = RAND_MAX +1,除非RAND_MAX = INT_MAX。
循环方式你可以使用N = 1,然后接受任何X值,并将IF语句放入最终的乘数。 但也许你有一些代码可能有正当理由在n = 1时调用函数时返回1 ...
所以当你想要n = RAND_MAX + 1时,最好使用0,这通常会提供Div 0错误
广义版本2:
int x;
if n != 0 {
do {
x = rand();
} while (x > (RAND_MAX - ( ( ( RAND_MAX % n ) + 1 ) % n) );
x %= n;
} else {
x = rand();
}
当RM + 1是n的乘积时,这两种解决方案都解决了不必要地丢弃的有效结果的问题。
当您需要n等于RAND_MAX中包含的总可能值集时,第二个版本还涵盖了边缘情况。
两者中的修改方法是相同的,并且允许更一般地解决提供有效随机数和最小化丢弃值的需要。
重申:
扩展标记示例的基本通用解决方案:
int x;
do {
x = rand();
} while (x > (RAND_MAX - ( ( ( RAND_MAX % n ) + 1 ) % n) );
x %= n;
扩展通用解决方案允许RAND_MAX + 1 = n的另一个场景:
int x;
if n != 0 {
do {
x = rand();
} while (x > (RAND_MAX - ( ( ( RAND_MAX % n ) + 1 ) % n) );
x %= n;
} else {
x = rand();
}