我正在写一些从远程随机数生成源读取字节(只是一个List<int>
)的东西,它非常慢。为了这个和我的个人要求,我想从源中检索尽可能少的字节。
现在我正在尝试实现签名看起来像的方法:
int getRandomInteger(int min, int max)
我有两种理论可以从随机源中获取字节,并将它们转换为整数。
方法#1是天真的。获取(max - min) / 256
个字节并添加它们。它可以工作,但它会从我的慢速随机数生成器源获取大量字节。例如,如果我想获得一百万到零之间的随机整数,它将获取近4000字节...这是不可接受的。
方法#2对我来说听起来很理想,但我无法想出算法。它是这样的:
让我们以min:0,max:1000为例。
ceil(rangeSize / 256)
,在这种情况下为ceil(1000 / 256) = 4
。现在从源中获取一(1)个字节。750 + 120 = 870
。在那种情况下,我们只需要总共获取2个字节。然而,它要复杂得多,好像我们的范围是0-1000000,我们需要几个“组”。
我如何实现这样的事情?我对Java / C#/ JavaScript代码或伪代码没问题。
我还希望保持结果不会丢失熵/随机性。所以,我有点担心缩放整数。
答案 0 :(得分:2)
不幸的是你的方法#1被打破了。例如,如果min为0且max为510,则添加2个字节。只有一种方法可以获得0结果:两个字节都为零。这个机会是(1/256)^ 2。然而,有很多方法可以获得其他值,例如100 = 100 + 0,99 + 1,98 + 2 ......所以100的概率要大得多:101(1/256)^ 2.
或多或少标准的做法是:
Let R = max - min + 1 -- the number of possible random output values
Let N = 2^k >= mR, m>=1 -- a power of 2 at least as big as some multiple of R that you choose.
loop
b = a random integer in 0..N-1 formed from k random bits
while b >= mR -- reject b values that would bias the output
return min + floor(b/m)
这被称为拒绝方法。它会丢弃随机选择的二进制数字,这会对输出产生偏差。如果min-max+1
恰好是2的幂,那么你将被拒绝。
如果你有m=1
且min-max+1
只是2的一个大的力量,那么拒绝接近一半。在这种情况下,你肯定想要更大的m
。
通常,较大的m值会导致较少的拒绝,但当然它们每个数字需要更多的位数。有一种可能的最佳算法来挑选m
。
此处介绍的其他一些解决方案存在问题,但我很抱歉,我现在没有时间发表评论。如果有兴趣,也许在几天内。
答案 1 :(得分:1)
3个字节(一起)为您提供0..16777215范围内的随机整数。您可以使用此值的20位来获取范围0..1048575并丢弃值&gt;百万
答案 2 :(得分:1)
range 1 to r
256^a >= r
first find 'a'
get 'a' number of bytes into array A[]
num=0
for i=0 to len(A)-1
num+=(A[i]^(8*i))
next
random number = num mod range
答案 3 :(得分:1)
您的随机来源为每次通话提供8个随机位。对于[min,max]范围内的整数,您需要ceil(log2(max-min + 1))位。
假设您可以使用某个函数从源获取随机字节:
bool RandomBuf(BYTE* pBuf , size_t nLen); // fill buffer with nLen random bytes
现在,您可以使用以下函数在给定范围内生成随机值:
// --------------------------------------------------------------------------
// produce a uniformly-distributed integral value in range [nMin, nMax]
// T is char/BYTE/short/WORD/int/UINT/LONGLONG/ULONGLONG
template <class T> T RandU(T nMin, T nMax)
{
static_assert(std::numeric_limits<T>::is_integer, "RandU: integral type expected");
if (nMin>nMax)
std::swap(nMin, nMax);
if (0 == (T)(nMax-nMin+1)) // all range of type T
{
T nR;
return RandomBuf((BYTE*)&nR, sizeof(T)) ? *(T*)&nR : nMin;
}
ULONGLONG nRange = (ULONGLONG)nMax-(ULONGLONG)nMin+1 ; // number of discrete values
UINT nRangeBits= (UINT)ceil(log((double)nRange) / log(2.)); // bits for storing nRange discrete values
ULONGLONG nR ;
do
{
if (!RandomBuf((BYTE*)&nR, sizeof(nR)))
return nMin;
nR= nR>>((sizeof(nR)<<3) - nRangeBits); // keep nRangeBits random bits
}
while (nR >= nRange); // ensure value in range [0..nRange-1]
return nMin + (T)nR; // [nMin..nMax]
}
由于您总是得到8位的倍数,因此可以在调用之间保存额外的位(例如,您可能只需要16位中的9位)。它需要一些比特操作,并且由你决定是否值得努力。
如果您使用“半位”,您可以节省更多:我们假设您要生成[1..5]范围内的数字。每个随机值都需要log2(5)= 2.32位。使用32个随机位,您实际上可以在此范围内生成楼层(32 / 2.32)= 13个随机值,但需要额外的工作量。