如何解决从一个范围到另一个范围的均匀分布的随机数的节俭转换
是否有一种方法可以节省将一个范围的均匀分布的随机数转换为另一范围的均匀分布的随机数?
让我解释一下“ 节俭”的意思。
在给定范围内(例如r∈[0..10))生成随机数的典型方法是采用一些固定的随机位,例如31,这将导致非负随机数小于2147483648。然后确保值小于2147483640(因为2147483648不能被10整除,因此可能导致分布不均)。如果该值大于或等于2147483640,则将其丢弃并重试(获取下31个随机位,依此类推)。如果值小于2147483640,则仅返回除以10的余数。此方法每十进制数字至少消耗31位。由于理论极限是log 2 (10)= 3.321928 ...,所以非常浪费。
如果使用4位代替31,我们可以改善这一点。在这种情况下,每十进制数字将消耗4×1.6 = 6.4位。这虽然更省钱,但仍远非理想。
public int nextDit() {
int result;
do {
result = next4Bits();
} while (result >= 10);
return result;
}
我们可以尝试一次生成3个十进制数字。由于1024非常接近1000,因此拒绝原始原始随机数的可能性小于以前的情况。生成3个十进制数字后,我们将返回1个数字并保留其余2个数字。
类似以下内容
private int _decDigits = 0;
private int _decCount = 0;
public int nextDit() {
if (_decCount > 0) {
// take numbers from the reserve
int result = _decDigits % 10;
_decDigits /= 10;
_decCount -= 1;
return result;
} else {
int result;
do {
result = next10Bits();
} while (result >= 1000);
// reserve 2 decimal digits
_decCount = 2;
_decDigits = result % 100;
result /= 100;
return result;
}
}
这种方法更省钱:每十进制数字消耗10×1.024 / 3 = 3.41(3)位。
如果我们尝试重用以前丢弃的数字,我们甚至可以走得更远。随机数r∈[0,1024)属于3个范围之一:[0,1000),[1000,1020),[1020,1024)。
如果它属于[0,1000],我们将像以前一样进行操作,保留2个十进制数字(保留十进制数字)并返回1个十进制数字。
如果它落入[1000,1020),我们减去1000转换为范围[0,20)。然后,我们将其除以10得到1位,并得到除以10的余数得到1个十进制数字。将其放入二进制数保留区并返回十进制数字。
如果它属于[1020,1024),我们减去1020转换为[0,4)范围。在这里,我们只得到2位,并将其放入二进制数字保留区。
// decimal digit reserve
private int _decDigits = 0;
private int _decCount = 0;
// binary digit reserve
private int _binDigits = 0;
private int _binCount = 0;
private int nextBits(int bits,int n) {
for (int i = 0; i < n; i += 1) {
bits = (bits << 1) + _bitRandomDevice.nextBit();
}
return bits;
}
private int next10Bits() {
// take bits from the binary reserve first,then from _bitRandomDevice
int result;
if (_binCount >= 10) {
result = _binDigits >> (_binCount - 10);
_binDigits = _binDigits & (1 << (_binCount - 10) - 1);
_binCount -= 10;
} else {
result = nextBits(_binDigits,10 - _binCount);
_binCount = 0;
_binDigits = 0;
}
return result;
}
public int nextDit() {
if (_decCount > 0) {
// take numbers from the decimal reserve
int result = _decDigits % 10;
_decDigits /= 10;
_decCount -= 1;
return result;
} else {
int result;
while (true) {
result = next10Bits();
if (result < 1000) {
assert result >= 0 && result < 1000;
// reserve 2 decimal digits
_decCount = 2;
_decDigits = result % 100;
result /= 100;
// return 1 decimal digit
return result;
} else if (result < 1020) {
result -= 1000;
assert result >= 0 && result < 20;
// reserve 1 binary digit
_binCount += 1;
_binDigits = (_binDigits << 1) + (result / 10);
// return 1 decimal digit
return result % 10;
} else {
result -= 1020;
assert result >= 0 && result < 4;
// reserve 2 binary digits
_binCount += 2;
_binDigits = (_binDigits << 2) + result;
}
}
}
}
此方法每十进制数字消耗约3.38 ...位。这是我能找到的最节约的方法,但它仍然浪费/丢失了随机性来源的一些信息。
因此,我的问题是:是否存在一种通用方法/算法将一个任意范围[0,s]的均匀分布的随机数(后来称为源数)转换为另一个任意范围[0,t的均匀分布的随机数)(后来称为目标编号),每个目标编号仅消耗log s (t)+ C源代码?其中C是一些常数。 如果没有这种方法,为什么呢?是什么导致无法达到理想极限?
节俭的目的是减少对RNG的呼叫次数。当我们使用True RNG时,尤其值得这样做,因为吞吐量通常很有限。
对于“节俭优化”,它们基于以下假设:
- 给定均匀随机数r∈[0,N),在检查 r
r > = M 之后,我们可以假定它均匀地分布在[M,N)中。
给定统一随机数r∈[A,B),导出的随机数(r + C)均匀分布在[A + C,B + C)中。即我们可以对随机数添加或减去任何常量以改变其范围。
- 给定均匀随机数r∈[0,N),其中N = P×Q,导出的随机数(r%P)均匀地分布在[0,P)中,而(r / P)均匀地分布在[0,P]中[0,Q)。即我们可以将一个统一的随机数分成几个。
- 给定统一的随机数p∈[0,P)和q∈[0,Q),导出的随机数(q×P + p)均匀分布在[0,P×Q)中。即我们可以将统一的随机数组合成一个。
解决方法
您的目标最终是在不浪费随机性的情况下,只给定 p 侧边的骰子来滚动 k 侧边的骰子。
从这个意义上说,B。Kloeckner在“ Simulating a dice with a dice”中的引理3中,除非“除以 k 的每个素数也除以 p ,否则这种浪费是不可避免的。 >”。因此,例如,如果 p 是2的幂(并且任何随机位块都与以2个数量的面的幂滚动骰子相同)和 k 的主要因素不是2,最好的办法是任意获取,几乎不会浪费随机性。
此外,除了分批处理比特以减少“比特浪费”(另请参见Math Forum)之外,还有{em> {em> {em}}中讨论的随机抽取技术,在我的Devroye and Gravel 2015-2020中。
另请参阅问题:Note on Randomness Extraction,尤其是我在那的答案。
,继续添加更多数字。这是一些用于计算预期收益的Python(对于n
的特定值,此方法比您的方法要稍差一些,因为它不会保存剩余的比特,但足以说明我的意思):
import math
def expected_digits(n,b):
total = 0
p = 1
while n >= b:
p *= 1 - (n % b) / n
total += p
n //= b
return total
def expected_yield(k):
return expected_digits(2 ** k,10) / k
print(expected_yield(10))
print(expected_yield(30))
print(expected_yield(100000))
print(math.log10(2))
输出为
0.294921875
0.2952809327592452
0.301018918814536
0.3010299956639812
并且您会看到,100000
个二进制数字(倒数第二行)非常接近Shannon限制(最后一行)。
从理论上讲,我们将arithmetic decoder应用于所有输出数字对无限位流(解释为0到1之间的随机数)具有相同概率的情况。渐近效率趋于完美,但是您抽取的样本越多,算术运算就越重。这往往是个权衡。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。