我有一串(均匀)随机比特流,想要生成在区间[0,n]内均匀分布的随机整数,同时尽量节省比特。 (假设总能使用不超过floor(log_2(n))+1个比特,那么多余的比特视为浪费)例如,如果n = 5,则我要寻找的算法应该使用不超过3个比特。如何实现?
我有一串(均匀)随机比特流,想要生成在区间[0,n]内均匀分布的随机整数,同时尽量节省比特。 (假设总能使用不超过floor(log_2(n))+1个比特,那么多余的比特视为浪费)例如,如果n = 5,则我要寻找的算法应该使用不超过3个比特。如何实现?
log2(n)
和最多log2(n)+2
位。(因此,即使是最优算法也有浪费位的可能性。)下面是最优算法的示例。function randomInt(minInclusive, maxExclusive) {
var maxInclusive = (maxExclusive - minInclusive) - 1
var x = 1
var y = 0
while(true) {
x = x * 2
var randomBit = nextBit()
y = y * 2 + randomBit
if(x > maxInclusive) {
if (y <= maxInclusive) { return y + minInclusive }
// Rejection
x = x - maxInclusive - 1
y = y - maxInclusive - 1
}
}
}
以下版本返回一个BigInt,它是JavaScript最近版本中支持的任意精度整数:
function randomInt(minInclusive, maxExclusive) {
minInclusive=BigInt(minInclusive)
maxExclusive=BigInt(maxExclusive)
var maxInclusive = (maxExclusive - minInclusive) - BigInt(1)
var x = BigInt(1)
var y = BigInt(0)
while(true) {
x = x * BigInt(2)
var randomBit = BigInt(Math.random()<0.5 ? 1 : 0)
y = y * BigInt(2) + randomBit
if(x > maxInclusive) {
if (y <= maxInclusive) { return y + minInclusive }
// Rejection
x = x - maxInclusive - BigInt(1)
y = y - maxInclusive - BigInt(1)
}
}
}
回想一下,“最优”的整数生成器(例如上面的快速掷骰子)平均使用至少log2(n)
位(下限),或者平均接近这个下限的2位。有各种技术可以用来使算法(即使是不太优化的算法)更接近这个理论下限,包括批处理和随机抽取。这些在以下文献中讨论:
这相当于在两个不同(有限)基数集之间找到一个双向函数,这是不可能的。
# Generate a number from 0 to n inclusive without wasting bits.
function RandomInteger(n)
if n <= 0
error
else
i = Floor(Log2(n))
x = i
r = 0
while x >= 0
r = r + (2 ^ x) * NextRandomBit()
if r > n
# Selected number too large so begin again.
x = i
r = 0
else
# Still in range. Calculate the next bit.
x = x - 1
return r
上述算法是为了清晰易懂而编写的,而不是追求速度。如果重写以同时处理多个位,则速度将非常快。
t=0 : 101 = 5
t=1: 010 = 2
t=2: 010 = 2
t=3: 111 = 7 -> too large, rotates won't work, so we use 2's complement: 001 = 1
t=4: 001 = 1
t=5: 101 = 5
首先确定您想要生成的可能值的数量。对于范围在0..5之间的整数,这是6个值。它们可以用ceil(log(6)/log(2))位表示。
// in C++
std::bitset< 3 > bits;
// fill the bitset
// interpret as a number
long value = bits.to_ulong();
然后找到从n位到最终表示格式的转换:它需要从范围[0..2N]缩放到范围[from,to]:
double out_from=-1, out_to=5;
double in_from=0, in_to = std::bitset<3>().flip().to_ulong();
double factor = (out_to-out_from)/(in_to-in_from)
double constant = out_from - in_from;
double rescaled = in_value * scale + constant;
long out = floor( rescaled );
n
不是2的幂,则我怀疑使用固定位数的情况下获得均匀分布是不可能的。可能存在可以更快地(使用更少的位)到达目标的算法,但我怀疑是否能找到一种能够使用固定位数完成此任务的算法。 - Snowbearfloor(log_2(n)) + 1
吗? - John Cromartie