C 在32字节的随机数据范围内生成随机数,无需bignum库

C 在32字节的随机数据范围内生成随机数,无需bignum库,c,bignum,C,Bignum,我有32字节的随机数据 我想在0-9和0-100之间的可变范围内生成随机数 如果我使用任意精度的算术bignum库,并将32字节视为一个大数字,我可以简单地执行以下操作: random = random_source % range; random_source = random_source / range; 我经常喜欢不同的范围,直到范围的乘积接近2^256 有没有一种方法可以只使用固定大小的整数算法来实现这一点?当然,您可以通过执行基256长除法或上推乘法来实现这一点。这就像你在小学里学

我有32字节的随机数据

我想在0-9和0-100之间的可变范围内生成随机数

如果我使用任意精度的算术bignum库,并将32字节视为一个大数字,我可以简单地执行以下操作:

random = random_source % range;
random_source = random_source / range;
我经常喜欢不同的范围,直到范围的乘积接近2^256


有没有一种方法可以只使用固定大小的整数算法来实现这一点?

当然,您可以通过执行基256长除法或上推乘法来实现这一点。这就像你在小学里学的长除法,但是用字节而不是数字。它涉及到对每个字节依次进行除法和余数的级联。请注意,您还需要了解您是如何使用大数值的,并且当您使用大数值并且它变小时,对范围内较大值的偏差会越来越大。例如,如果您只剩下110,而您要求的是rnd100,那么0-9的值可能比10-99的值高10%

/*  The 32 bytes in data are treated as a base-256 numeral following a "." (a
    radix point marking where fractional digits start).  This routine
    multiplies that numeral by range, updates data to contain the fractional
    portion of the product, and returns the integer portion.

    8-bit bytes are assumed, or "t /= 256" could be changed to
    "t >>= CHAR_BIT". But then you have to check the sizes of int
    and unsigned char to consider overflow.
*/
int r(int range, unsigned char *data)
{
    // Start with 0 carried from a lower position.
    int t = 0;

    // Iterate through each byte.
    for (int i = 32; 0 < i;)
    {
        --i;

        // Multiply next byte by our multiplier and add the carried data.
        t = data[i] * range + t;

        // Store the low bits of the result.
        data[i] = t;

        // Carry the high bits of the result to the next position.
        t /= 256;
    }

    // Return the bits that carried out of the multiplication.
    return t;
}
但是,你不需要使用bignum技术,你可以使用算术编码压缩的思想,在这里你建立一个数字,而不需要处理整个事情

如果从向无符号uint_32缓冲区读取4个字节开始,则其范围为0..4294967295,非包含性最大值为4294967296。我将把这个综合值称为结转,这个唯一的最大值也很重要

[为简单起见,您可以从读取缓冲区中的3个字节开始,生成最大16M。这避免了必须处理不能保存在32位整数中的4G值。]

有两种方法可以使用此功能,这两种方法都具有准确性含义:

向下流:

做你的模范围。模是你的随机答案。分割结果是新的结转,范围较小。 假设你想要0..99,那么你除以100,你的上半部分有一个最大42949672 4294967296/100的范围,你可以为下一个随机请求结转这个范围 我们还不能再输入一个字节。。。 假设你现在想要0..9,那么你可以模10,现在你的上半部分的范围是0..4294967 42949672/100 由于max小于16M,我们现在可以引入下一个字节。将其乘以当前最大值4294967,并将其添加到结转中。最大值也乘以256->1099511552

此方法对较小的值有轻微的偏差,如在下一个最大时间中为1,值的可用范围将不是完整范围,因为最后一个值被截断,但通过选择在最大值中保持3-4个良好字节,该偏差被最小化。它最多只会在1600万次中出现1次

此算法的计算成本是div除以结转和max的随机范围,然后是每次输入新字节时的乘法。我假设编译器将优化模

向上流动: 假设你想要0..99 将最大值除以范围,得到下一个最大值,并将结转除以下一个最大值。现在,你的随机数出现在除法结果中,余数形成你结转的值,得到下一个随机数。 当nextmax小于16M时,只需将nextmax和您的结转乘以256,然后加上下一个字节。 如果此方法的缺点是,根据用于生成nextmax的除法,最高值结果(即99或9)严重偏向,或者有时您将生成超过100的值-这取决于您是向上还是向下进行第一次除法

这里的计算成本又是2分,假设编译器优化程序混合了div和mod操作。乘以256很快

在这两种情况下,您可以选择说,如果输入的结转值在这个高偏差范围内,那么您将执行不同的技术。你甚至可以在两种技术之间摇摆-优先使用第二种技术,但如果它产生过大的值,则使用第一种技术,尽管这两种技术本身可能会在结转值接近最大值时对类似的输入随机流产生偏差。这种偏差可以通过使第二种方法生成-1作为超出范围来减少,但每种修复都会增加一个额外的乘法步骤


注意,在算术编码中,当提取每个符号时,这个溢出区被有效地丢弃。在解码过程中可以保证不会出现这些边缘值,这会导致轻微的次优压缩。

当然,您可以通过执行基256长除法或上推乘法来实现这一点。这就像你在小学里学的长除法,但是用字节而不是数字。它涉及到对每个字节依次进行除法和余数的级联。请注意,您还需要意识到您是如何消费这个大数字的,并且当您消费它并且它变小时,对t的偏见会越来越大 他在这个范围内选择了更大的值。例如,如果您只剩下110,而您要求的是rnd100,那么0-9的值可能比10-99的值高10%

但是,你不需要使用bignum技术,你可以使用算术编码压缩的思想,在这里你建立一个数字,而不需要处理整个事情

如果从向无符号uint_32缓冲区读取4个字节开始,则其范围为0..4294967295,非包含性最大值为4294967296。我将把这个综合值称为结转,这个唯一的最大值也很重要

[为简单起见,您可以从读取缓冲区中的3个字节开始,生成最大16M。这避免了必须处理不能保存在32位整数中的4G值。]

有两种方法可以使用此功能,这两种方法都具有准确性含义:

向下流:

做你的模范围。模是你的随机答案。分割结果是新的结转,范围较小。 假设你想要0..99,那么你除以100,你的上半部分有一个最大42949672 4294967296/100的范围,你可以为下一个随机请求结转这个范围 我们还不能再输入一个字节。。。 假设你现在想要0..9,那么你可以模10,现在你的上半部分的范围是0..4294967 42949672/100 由于max小于16M,我们现在可以引入下一个字节。将其乘以当前最大值4294967,并将其添加到结转中。最大值也乘以256->1099511552

此方法对较小的值有轻微的偏差,如在下一个最大时间中为1,值的可用范围将不是完整范围,因为最后一个值被截断,但通过选择在最大值中保持3-4个良好字节,该偏差被最小化。它最多只会在1600万次中出现1次

此算法的计算成本是div除以结转和max的随机范围,然后是每次输入新字节时的乘法。我假设编译器将优化模

向上流动: 假设你想要0..99 将最大值除以范围,得到下一个最大值,并将结转除以下一个最大值。现在,你的随机数出现在除法结果中,余数形成你结转的值,得到下一个随机数。 当nextmax小于16M时,只需将nextmax和您的结转乘以256,然后加上下一个字节。 如果此方法的缺点是,根据用于生成nextmax的除法,最高值结果(即99或9)严重偏向,或者有时您将生成超过100的值-这取决于您是向上还是向下进行第一次除法

这里的计算成本又是2分,假设编译器优化程序混合了div和mod操作。乘以256很快

在这两种情况下,您可以选择说,如果输入的结转值在这个高偏差范围内,那么您将执行不同的技术。你甚至可以在两种技术之间摇摆-优先使用第二种技术,但如果它产生过大的值,则使用第一种技术,尽管这两种技术本身可能会在结转值接近最大值时对类似的输入随机流产生偏差。这种偏差可以通过使第二种方法生成-1作为超出范围来减少,但每种修复都会增加一个额外的乘法步骤


注意,在算术编码中,当提取每个符号时,这个溢出区被有效地丢弃。在解码过程中可以保证不会出现这些边缘值,这会导致轻微的次优压缩。

bignum库仅使用固定大小的整数算法实现,因此这是肯定的。如果您只是为32字节编写一个模除函数呢?编写一个库来处理32字节长整数的除法应该不是那么难。bignum库只使用固定大小的整数算法实现,所以这是肯定的。如果你只写一个32字节的模除法函数呢?写一个库来处理32字节长整数的除法应该不会那么难。乘法运算通常比除法运算快,所以这是一个很好的改进。并有很好的均匀分布保证。唯一的缺点是:它每次都对整个值进行运算,并向其写入数据。乘法运算通常比除法运算快,因此这是一个很好的改进。并有很好的均匀分布保证。唯一的缺点是:它每次都在对整个价值进行操作,并向其写入内容。