“确定性”伪随机数生成

时间:2017-12-06 16:02:11

标签: python c++ matrix random

我处于需要生成具有特定随机稀疏模式的非常大(~10 ^ 16个元素)随机矩阵的情况。显然,存储所有这些元素是完全不可行的。在任何给定时间只需要一些元素,因此可以按需绘制它们 - 但是,一旦存储了元素,稍后可能需要它,并且使用相同的值很重要。即,元素不能被抛出并随机重绘 - 一旦绘制了随机元素,就需要保存它。

基于问题本身,可能有一些聪明的方法来解决这个问题,我不会介入。然而,一位同事表示,应该可以通过使用带有矩阵中索引给出的种子的伪随机数生成器来确定性地生成这些随机数,即使用i + N*j的元素(i, j)矩阵,其中矩阵的大小为N*N。这不会调用rand()函数,而是使用具有特定参数的基础伪随机函数来确定性地生成先前绘制的随机值。然后,不需要保存任何数字,并且可以根据需要确定性地重新绘制它们。

我对PRG的理解是,对于一系列数字看起来是随机的,你必须修复种子。上述方法有意义吗?在我看来,就像重复播种PRG并只是采取第一个元素。

2 个答案:

答案 0 :(得分:1)

不是一个确切的答案,而是一些尝试。

哈希函数似乎是实现目标的简单而有效的方法。

Here是关于整数到整数哈希函数的一些好主意。

从这篇文章中我试试:

from numba import uint64, njit 
import pylab as pl

@njit(uint64(uint64,uint64))    
def hash64(i,j) :
    x= i + (j << 32)
    x = (x ^ (x >> 30)) * (0xbf58476d1ce4e5b9);
    x = (x ^ (x >> 27)) * (0x94d049bb133111eb);
    x = x ^ (x >> 31);
    return x;  

n=1000    
im=[[hash64(i,j) for i in range(n)] for j in range(n)]
pl.subplot(121)
pl.imshow(im)
pl.colorbar()
pl.subplot(122)
pl.hist(np.array(im).ravel(),bins=100)
pl.show()  

此numba hash64函数在~200 ns内计算哈希码。

这个情节(即使它没有展示任何内容)表明这个功能可能是一个很好的候选人。

enter image description here

相比之下,python哈希函数(hash((i,j)) on tuple)没有通过测试: enter image description here

这里是一个Knuth Generator:

enter image description here

还有一些基准:

In [61]: %timeit hash64(0,1)
215 ns ± 9.11 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [62]: %timeit np.random.seed(0+1<<30);a=np.random.randint(2**30)
4.18 µs ± 126 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [63]:%timeit  hash((0,1))
102 ns ± 19.5 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each) 

答案 1 :(得分:0)

基本上,你需要10个 16 ~2 53 随机数,它们由矩阵元素线性指数唯一确定。

最简单的随机数生成器是64位Linear Congruential one。基本上任何 合理的长度为63-64位且完整周期将起作用。代码如下 实现Knuth LCG,种子等于矩阵元素线性指数。 LCG的另一个好的特性是以对数复杂度跳跃 - jump~log 2 (N)

代码

import numpy as np

A = np.uint64(6364136223846793005) # multiplier
C = np.uint64(1442695040888963407) # increment
K = np.float64(5.421010862427522170037E-20) # 1/2^64

def K64(seed):
    """
    Knuth 64bit MMIX LCG, return uint64 in the range [0...2^64)
    """
    return A*np.uint64(seed) + C

def KLCG64(seed):
    """
    Knuth 64bit MMIX LCG, return float64 in the range [0.0...1.0)
    """
    #return np.float64(K64(seed))*K # here is proper implementation, but slower than inlined code below
    return np.float64(A*np.uint64(seed) + C)*K

def randomMat(i, j, N):
    """
    return random element of the matrix
    """
    return KLCG64(np.uint64(i) + np.uint64(j)*np.uint64(N))

np.seterr(over='ignore') # important! LCG relies on unsigned overflow for mod operation

print(randomMat(123738, 9276, 735111))