【问题标题】:Custom Random Number Generator自定义随机数生成器
【发布时间】:2014-01-23 00:46:17
【问题描述】:

是否有可能获得一个非常快速但可靠的(相同的输入 = 相同的输出,所以我不能使用时间)伪随机数生成器?我希望最终结果类似于float NumGen( int x, int y, int seed );,以便它根据这三个值创建一个介于 0 和 1 之间的随机数。我找到了几个随机数生成器,但我无法让它们工作,而且 Unity 附带的随机数生成器使用起来很慢。每 1 米地形我必须对生成器进行大约 9 次调用,所以我真的不在乎它是否在统计上不是完全随机的,只是它运行得非常快。有谁知道适合我需要的算法?谢谢:)

【问题讨论】:

  • 是的,有可能。一个线性同余生成器是两行代码。
  • 我会调查System.Random代。它确实需要时间,但如果你将时间换成你的种子,或者甚至将两者结合起来,你就会得到我认为你在这里描述的内容。
  • @EricLippert 我找不到实现线性同余生成器以获取 3 个输入并仍然工作 0f-1f 的方法。
  • @WoutervanderHouven 这就是我正在使用的,但我每秒调用该函数数百次,所以他们使用的复杂算法非常需要资源
  • 知道了。所以仅仅生成一个由 10000 个随机数组成的大序列是行不通的。另一个复杂的问题是,您将生成例如 (100, 200) 的数字,然后是 (100, 201) 和 (99, 200) 等等;坐标没有增加任何熵。我不知道有什么算法可以满足您的要求,但我会考虑一下。

标签: c# random unity3d


【解决方案1】:

我认为你低估了System.Random 类。这是相当快的。我相信您的减速与在每次调用您的 NumGen 方法时创建 Random 类的新实例有关。

在我的快速测试中,我能够在大约 1 毫秒内使用 System.Random 生成 100,000 个随机数。

为避免减速,请考虑 2D 平面中的种子点。分散种子点,使它们覆盖的距离不超过 100,000 米。然后关联(或计算)每米最近的种子点,并将该点用作System.Random 的种子。

是的,您将生成大量您永远不会使用的随机数,但它们实际上是免费的。

伪代码:

double NumGen(x, y, distance, seed) {
  Random random = new Random(seed);
  double result = 0;
  for (int i=0; i<distance; i++) {
    result = random.NextDouble();
  }
}

您可以修改这个简单的轮廓以返回一个随机数序列(可能表示一个网格),并将其与缓存机制结合起来。这样可以节省内存并改善(减少)CPU 消耗。

【讨论】:

  • 确实,如果你只有一个(或几个)Random类的实例,性能会很好,显然取决于你用来测量的变量。
【解决方案2】:

我猜你必须在每次调用 NumGen 时创建一个 Random 实例。要让函数为相同的参数返回相同的数字,您可以使用哈希函数。

我测试了一些东西,这段代码比重新创建 Random 的实例快了大约 3 倍。

//System.Security.Cryptography
static MD5 hasher = MD5.Create();
static byte[] outbuf;
static byte[] inbuf = new byte[12];
static float floatHash(uint x, uint y, uint z) {
    inbuf[0]= (byte)(x >> 24);
    inbuf[1]=(byte)(x >> 16);
    inbuf[2]=(byte)(x >> 8);
    inbuf[3]=(byte)(x);
    inbuf[4]=(byte)(y >> 24);
    inbuf[5]=(byte)(y >> 16);
    inbuf[6]=(byte)(y >> 8);
    inbuf[7]=(byte)(y);
    inbuf[8]=(byte)(z >> 24);
    inbuf[9]=(byte)(z >> 16);
    inbuf[10]=(byte)(z >> 8);
    inbuf[11]=(byte)(z);
    outbuf = hasher.ComputeHash(inbuf);
    return ((float)BitConverter.ToUInt64(outbuf, 0))/ulong.MaxValue;
}

另一种使用一些 RSA 方法的方法比 new System.Random(seed) 快大约 5 倍

static uint prime = 4294967291;
static uint ord = 4294967290;
static uint generator = 4294967279;
static uint sy;
static uint xs;
static uint xy;
static float getFloat(uint x, uint y, uint seed) {
    //will return values 1=> x >0; replace 'ord' with 'prime' to get 1> x >0
    //one call to modPow would be enough if all data fits into an ulong
    sy = modPow(generator, (((ulong)seed) << 32) + (ulong)y, prime);
    xs = modPow(generator, (((ulong)x) << 32) + (ulong)seed, prime);
    xy = modPow(generator, (((ulong)sy) << 32) + (ulong)xy, prime);
    return ((float)xy) / ord;
}
static ulong b;
static ulong ret;
static uint modPow(uint bb, ulong e, uint m) {
    b = bb;
    ret = 1;
    while (e > 0) {
        if (e % 2 == 1) {
            ret = (ret * b) % m;
        }
        e = e >> 1;
        b = (b * b) % m;
    }
    return (uint)ret;
}

我进行了测试以生成 100000 个浮点数。我将索引用作 System.Random 的种子和 floatHash 的 x 参数(y 和 z 为 0)。

System.Random:最小值:2.921559E-06 最大值:0.9999979 重复次数:0

floatHash MD5:最小值:7.011156E-06 最大值:0.9999931 重复次数:210(返回值两次)

getFloat RSA:最小值:1.547858E-06 最大值:0.9999989 重复次数:190

【讨论】:

  • 你确定输出是随机的吗?我只能看到你正在散列一个字节数组并将结果除以 ulong.maxvalue?这怎么可能是随机的?
  • 这不是完全随机的。我在答案中编辑了一份测试报告。
  • 这看起来很完美,但我无法使用它。我正在使用 Unity,由于某种原因,每次尝试运行它时都会崩溃。我认为它不喜欢 uint,因为我之前尝试过处理它们并且花了很长时间。有没有办法将它转换为单独的 int,因为我不确定代码是如何工作的,并且按位运算符似乎取决于位长度。
  • @NicholasPipitone 我编辑了代码。现在都是无符号整数。负整数转ulong导致整数溢出。
猜你喜欢
  • 1970-01-01
  • 2021-06-19
  • 2017-05-10
  • 1970-01-01
  • 1970-01-01
  • 2011-09-18
  • 2023-01-03
  • 2011-06-08
  • 1970-01-01
相关资源
最近更新 更多