当前位置: 首页 > 知识库问答 >
问题:

一个范围内的64位随机数

邓正真
2023-03-14

因此,我一直在寻找一个函数,该函数需要2个参数,一个低值和一个高值(均为64位整数),而不是在这些范围之间生成一个随机数。我一直遇到的问题是,这个数字不是64位int,或者边缘的数字比中间的数字更常见。

这是一些代码:它只是一直返回-1或0…

#include<stdio.h>
#include<stdlib.h>
#include<inttypes.h>

int64_t range1=0,range2=18446744073709551614;

int64_t getRandomInRange(int64_t low, int64_t high )
{
    int64_t base_random = rand(); 
    if (RAND_MAX==base_random) return getRandomInRange(low, high);
    int range       = high-low,
        remainder   = RAND_MAX%range,
        bucket      = RAND_MAX/range;
    if (base_random < RAND_MAX-remainder) {
        return low+base_random/bucket;
    } else {
        return getRandomInRange(low, high);
    }
}

int main () {
    int i;
    for (i=0;i<100;i++) {
        printf("random number: %lld\n",getRandomInRange(range1, range2));
    }
}

共有2个答案

曹昊焱
2023-03-14

您的代码返回 0 或 -1,因为18446744073709551614太大而无法放入int64_t。(事实上,它稍微太大而无法放入uint64_t,因为它正好是264,并且可以容纳k位无符号整数的最大数字是2k-1。因此,您最终会得到有符号整数溢出。(gcc和叮当声(至少)警告过你,即使没有-Wall

无论如何,如果您有某种机制来生成随机的64位无符号整数,那么生成您正在寻找的库函数并不困难。一个不错的选择是梅森图斯特图书馆。然而,为了进行演示,我们只能使用标准的C库函数,在本例中是< code>lrand48,它产生一个在< code>(0,231-1)范围内均匀分布的整数。因为这个范围只产生31位的随机性,所以我们需要多次调用它才能产生64位。

#define _XOPEN_SOURCE
#include <stdlib.h>
#include <stdint.h>

uint64_t urand64() {
  uint64_t hi = lrand48();
  uint64_t md = lrand48();
  uint64_t lo = lrand48();
  return (hi << 42) + (md << 21) + lo;
}

要从范围[low, high)中获得无偏样本,我们需要将随机数生成限制为high-low的某个倍数。urand64的范围是大小264,因此我们需要排除modHigh-low264值。不幸的是,除非我们有一个长于64位的无符号int,否则我们实际上不能直接计算模。但是,我们可以使用标识:

modk(modkm modkn)=modk(m n)

在这种情况下,我们将选择m作为264-1n作为1,以避免计算 。此外,很容易证明,除非 k是2的精确幂,否则 modk264-1 modk11不可能是2的确切幂,而如果 是2之精确幂,则所需的 modk264是0,其解释可在其他地方找到:

bool is_power_of_2(uint64_t x) {
  return x == x & -x;
}

因此,我们可以定义:

uint64_t unsigned_uniform_random(uint64_t low, uint64_t high) {
  static const uint64_t M = ~(uint64_t)0; 
  uint64_t range = high - low;
  uint64_t to_exclude = is_power_of_2(range) ? 0
                                             : M % range + 1;
  uint64_t res;
  // Eliminate `to_exclude` possible values from consideration.
  while ((res = urand64()) < to_exclude) {}
  return low + res % range;
}

请注意,在最坏的情况下,要排除的值的数量为263-1,略小于可能值范围的一半。因此,在最坏的情况下,我们平均需要两次调用urand64才能找到满意的值。

最后,我们需要处理这样一个事实,即我们被要求生成有符号整数,而不是无符号整数。然而,这并不是一个问题,因为必要的转换是定义良好的。

int64_t uniform_random(int64_t low, int64_t high) {
  static const uint64_t OFFSET = ((uint64_t)1) << 63;
  uint64_t ulow =  (uint64_t)low + OFFSET;
  uint64_t uhigh = (uint64_t)high + OFFSET;
  uint64_t r = unsigned_uniform_random(ulow, uhigh);
  // Conform to the standard; a good compiler should optimize.
  if (r >= OFFSET) return r - OFFSET;
  else             return (int64_t)r - (int64_t)(OFFSET - 1) - 1;
}
云炜
2023-03-14

取模N不会导致均匀分布,除非N精确地划分范围R:

 rnd = 0..15,  range = 9.

 0 1 2 3 4 5 6 7 8  <-- 0..8 % 9 
 0 1 2 3 4 5 6      <-- 9-15 % 9
----------------------------------
 2 2 2 2 2 2 2 1 1    <-- sum = 16

同样,如果一个人试图通过乘以9/16来避免这个事实

 rnd = 0..15,   range = 9,   reducing function = rnd * 9 >> 4, one has
 0 1 2 3 4 5 6 7 8    for rnd = 0, 2, 4, 6, 8, 9, 13, 15    and
 0 1 2 3   5 6 7      for rnd = 1, 3, 5, 7, 10, 12, 14
------------------------
 2 2 2 2 1 2 2 2 1     <-- sum = 16

这就是所谓的“鸽子洞原理”。

创建随机数均匀分布的一种正确方法是生成随机数的ceil(log2(N))位,直到这些位表示的数字小于范围:

 int rand_orig(); // the "original" random function returning values from 0..2^n-1
                  // We assume that n = ceil(log2(N));
 int rand(int N)
 {
     int y;
     do {
          y = rand_orig();
     } while (y >= N);
     return y;
 }

这当然可以改进,如果rand_orig();将返回更大的值n

另一种方法是创建一个平衡值(N

 #define CO_PRIME 1 // Better to have some large prime 2^(n-1) < CO_PRIME < 2^n-1
 int rand_orig();   // some function returning random numbers in range 0..2^n-1
 int rand(int N)    // N is the range
 {
     static int x;
     int y = rand_orig();
     int new_rand = (x + y) % N;
     x = (x + CO_PRIME) % N;
     return new_rand;
 }

现在这个平衡项x的周期是N,导致至少均匀分布。

 类似资料:
  • 问题内容: 我知道如何在Python范围内生成随机数。 我知道我可以将其循环生成n个数量的这些数字 但是,我需要确保该列表中的每个数字都是唯一的。除了大量的条件语句之外,还有一种直接的方法可以生成n个唯一的随机数吗? 重要的是列表中的每个数字都不同。 所以 [12,5,6,1] =好 但 [12,5,5,1] =不好,因为数字5出现两次。 问题答案: 如果您只需要采样而无需更换: random.s

  • 问题内容: 我需要生成一个范围内的随机唯一数字吗?怎么做 ? 我可以通过生成随机数 我知道这段代码不好,所以我需要一个更好的优化版本代码!帮帮我 ! 例如:如果我需要在1到15之间生成3个数字,它们应该像5、9、1而不是3,1,2 [具有1-3(我要生成的数字)] 问题答案: 以随机顺序排列数字范围的数组: 包装功能: 例: 结果:

  • 我还没有找到一个函数来生成给定长度的随机浮点数数组。 我已经看过随机抽样,但似乎没有函数可以满足我的需求。 random.uniform很接近,但它只返回一个元素,而不是一个特定的数字。 这就是我所追求的: 这将返回一个由50个随机非唯一浮点数(即:允许重复)组成的数组,这些浮点数均匀分布在范围内。 有这样的功能吗

  • 返回指定范围内的一个随机数。 使用 Math.random() 生成一个随机值,使用乘法将其映射到所需的范围。 const randomNumberInRange = (min, max) => Math.random() * (max - min) + min; randomNumberInRange(2, 10); // 6.0211363285087005

  • 比方说,如果我想在和之间生成一个无偏随机数,我会这样做: 但是,如果我想生成一个介于和之间的随机数,但更偏向于一个介于和之间的值的程度呢?最好用概率曲线来说明:

  • 编者注:此代码示例来自Rust 1.0之前的版本,在语法上不是有效的Rust 1.0代码。此代码的更新版本会产生不同的错误,但答案仍然包含有价值的信息。 我遇到了下面的例子,说明了如何使用Rust生成一个随机数,但它似乎不起作用。这个例子没有显示它适用于哪个版本的Rust,所以可能它已经过时了,或者可能我搞错了什么。 当我试图编译此文件时,会出现以下错误: 在同一页(上面)上还有另一个例子(如下所