【问题标题】:random number generator with x,y coordinates as seed以 x,y 坐标为种子的随机数生成器
【发布时间】:2016-05-10 02:44:35
【问题描述】:

我正在寻找一种高效、均匀分布的 PRNG,它可以为平原中的任何整数点生成一个随机整数,并将坐标 x 和 y 作为函数的输入。

int rand(int x, int y)

每次输入相同的坐标时,它必须传递相同的随机数。

您知道可用于此类问题以及更高维度的算法吗?

我已经尝试使用像 LFSR 这样的普通 PRNG,并将 x,y 坐标合并在一起以将其用作种子值。像这样。

int seed = x << 16 | (y & 0xFFFF)

这种方法的明显问题是种子没有迭代多次,而是针对每个 x,y 点再次初始化。如果您将结果可视化,这会导致非常难看的非随机模式。

我已经知道使用某种大小(如 256)的随机排列表的方法,并且您可以像这样从中得到一个随机整数。

int r = P[x + P[y & 255] & 255];

但我不想使用这种方法,因为范围非常有限,周期长度有限,内存消耗很大。

感谢您提供任何有用的建议!

【问题讨论】:

  • 代码是什么语言的?
  • 我使用 C++,但问题与语言无关
  • 你需要多大的整数?
  • xy 的最小值和最大值是多少?
  • @Mark Ransom 整数大小无关紧要。我使用 32 位整数

标签: algorithm random hash dimension


【解决方案1】:

我的方法

一般来说,我认为您需要一些散列函数(大多数这些函数都旨在输出随机性;RNG 的雪崩效应,CryptoPRNG 明确需要随机性)。与this 线程比较。

以下代码使用了这种方法:

  • 1) 根据您的输入构建可散列的内容
  • 2) 散列 -> 随机字节(非加密)
  • 3) 以某种方式将这些随机字节转换为您的整数范围(很难正确/统一!)

最后一步是通过this 方法完成的,这似乎不是那么快,但有很强的理论保证(使用了选定的答案)。

我使用的哈希函数支持种子,将在步骤 3 中使用!

import xxhash
import math
import numpy as np
import matplotlib.pyplot as plt
import time

def rng(a, b, maxExclN=100):
    # preprocessing
    bytes_needed = int(math.ceil(maxExclN / 256.0))
    smallest_power_larger = 2
    while smallest_power_larger < maxExclN:
        smallest_power_larger *= 2

    counter = 0
    while True:
        random_hash = xxhash.xxh32(str((a, b)).encode('utf-8'), seed=counter).digest()
        random_integer = int.from_bytes(random_hash[:bytes_needed], byteorder='little')
        if random_integer < 0:
            counter += 1
            continue # inefficient but safe; could be improved
        random_integer = random_integer % smallest_power_larger
        if random_integer < maxExclN:
            return random_integer
        else:
            counter += 1

test_a = rng(3, 6)
test_b = rng(3, 9)
test_c = rng(3, 6)
print(test_a, test_b, test_c) # OUTPUT: 90 22 90

random_as = np.random.randint(100, size=1000000)
random_bs = np.random.randint(100, size=1000000)

start = time.time()
rands = [rng(*x) for x in zip(random_as, random_bs)]
end = time.time()

plt.hist(rands, bins=100)
plt.show()
print('needed secs: ', end-start)
# OUTPUT: needed secs:  15.056888341903687 -> 0,015056 per sample
# -> possibly heavy-dependence on range of output

可能的改进

  • 从某个来源添加额外的熵(urandom;可以放入 str)
  • 创建一个类并初始化以记忆预处理(如果每次采样都进行,成本会很高)
  • 处理负整数;也许只使用 abs(x)

假设:

  • 输出范围为 [0, N) -> 为其他人移动!
  • 输出范围小于(位)哈希输出(可能使用 xxh64)

评价:

检查随机性/均匀性

检查输入是否具有确定性

【讨论】:

  • 感谢您的回答。你认为我可以使用遗传算法找到合适的哈希函数吗?我想要一个非常快的哈希函数,它比 gpu 中的查找表更快,所以我只能使用简单的按位运算。随机数不需要是加密安全的,它们只需要在眼睛上看起来是随机的。所以我想我可以测量在 DNA 中编码的不同算法的分布特性和周期长度,并杂交和/或变异好的算法,以便在几代后找到一个好的解决方案。
  • 绝对不是!我使用的 xxhash 是一个非加密哈希函数,它与你的记忆一样快。如果没有大量研究,您将无法击败它。使用 GA 生成代码也非常困难,即使对于结构化输出也是如此,对于想要的混乱输出来说更难。只需坚持使用快速哈希函数即可。剩下的 2 个问题:GPU 是否有更快的问题(看起来你的目标是这些)?更重要的是:如何在 GPU 上实现第 3 步。我的方法并不快,甚至在 GPU 上会更慢。你必须决定你需要多少质量。像模数这样更简单的方案就足够了。
  • 我在 gpu 上实现了 xxhash 的简化版本。只需一个整数输入而不是字节数组,大部分复杂性就消失了。我只是用这段代码将我的整数输入 x 和 y 转换为一个随机性确实很好,但它比使用查找表稍慢。所以我会尝试找到一个使用较少乘法的更简单的哈希函数。如果我找到一个,我会在这里发布。感谢您提供有用的答案。
  • 上一条评论的编辑:所以我只是对我的输入做了这个 '(x
  • 我找到了一个哈希函数。你怎么看待这件事?结果对我有好处。
【解决方案2】:

我发现了一个基于xxhash算法的非常简单、快速、足够的hash函数。

// cash stands for chaos hash :D
int cash(int x, int y){   
    int h = seed + x*374761393 + y*668265263; //all constants are prime
    h = (h^(h >> 13))*1274126177;
    return h^(h >> 16);
}

它现在比我上面描述的查找表方法快得多,而且看起来同样随机。我不知道随机属性与 xxhash 相比是否更好,但只要它看起来是随机的,就我的目的而言这是一个公平的解决方案。

这是以像素坐标为输入的样子:

【讨论】:

  • good 哈希函数的构建是一个艰难的过程。你的看起来非常非常糟糕。首先,我怀疑 h 中存在溢出,其次:这是具有不同种子的相同输入的结果:12980285595313403384 / 12980285592294613320。这是类似但不同输入的结果:12709661718304170483 / 13558132204872960782 不是哈希。 - 完全有功能(如果想要雪崩效应的话)。如果它对你有用,那很好。但这比线性同余生成器还要糟糕(而且可能不会更快)。
  • @sascha 我添加了我的函数的绘图图像。看起来很随意。我的算法与 xxhash 类似,只是为了获得性能而扭曲较少。 xxhash 在内部也适用于溢出。 github.com/Cyan4973/xxHash/blob/master/xxhash.c 例如从第 370 行到第 375 行。这不是算法的缺陷。我不能使用像 LCG 这样的生成器,因为它们被设计为在一个序列中一个接一个地计算一个随机数。在 GPU 中,我必须并行计算它们。
  • 我很欣赏这一点,它对我正在做的抗锯齿 hack 添加一点噪音非常有帮助。环顾四周,寻找在 gpu 上运行速度很快的非加密哈希函数,即使测试失败,这也是相当不错的。
  • @lahwran 谢谢。我很感激,它很有帮助。
【解决方案3】:

您可以使用各种randomness extractors 来实现您的目标。您至少可以从两个来源寻找解决方案。

总而言之,你最好使用:

  1. AES-CBC-MAC 使用随机密钥(可以固定并重复使用)
  2. HMAC,最好使用 SHA2-512
  3. SHA 系列哈希函数(SHA1、SHA256 等);使用随机的最终块(例如,在末尾使用大的随机盐)

因此,您可以连接坐标、获取它们的字节、添加随机密钥(用于 AES 和 HMAC)或为 SHA 添加盐,并且您的输出具有足够的熵。 根据 NIST,输出熵依赖于输入熵:

假设您使用 SHA1;因此 n = 160 位。假设 m = input_entropy(你的坐标的熵)

  • 如果 m >= 2n 那么 output_entropy=n=160 位
  • 如果 2n 则 最大 output_entropy=m(但不能保证完全熵)。
  • 如果 m 则 最大 output_entropy=m(这是你的情况)

NIST sp800-90c(第11页)

【讨论】:

  • “根据 NIST,输出熵依赖于输入熵”——这句话很有说服力!这意味着,如果输入具有低熵(并且可能具有高输出范围;至少比关于差距更可观察),我的方法(以及通常所有没有外部熵源的方法)可能会遇到困难。根据设置,值得考虑添加额外的熵。
猜你喜欢
  • 2013-11-09
  • 2017-06-14
  • 2021-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-08
  • 1970-01-01
  • 2013-08-23
相关资源
最近更新 更多