C# 如何最好地按需生成随机数的静态数组?
我正在开发的应用程序需要一个随机数矩阵。矩阵可以在任何时间向任何方向增长,并且并不总是满的。(我可能会用四叉树或其他东西来重新实现它,而不是用一个包含大量空对象的矩阵。) 我需要一种方法来生成相同的矩阵,给定相同的种子,无论我以何种顺序计算矩阵C# 如何最好地按需生成随机数的静态数组?,c#,algorithm,random,C#,Algorithm,Random,我正在开发的应用程序需要一个随机数矩阵。矩阵可以在任何时间向任何方向增长,并且并不总是满的。(我可能会用四叉树或其他东西来重新实现它,而不是用一个包含大量空对象的矩阵。) 我需要一种方法来生成相同的矩阵,给定相同的种子,无论我以何种顺序计算矩阵 LazyRandomMatrix rndMtx1 = new LazyRandomMatrix(1234) // Seed new object float X = rndMtx1[0,0] // Lazily generate random numbe
LazyRandomMatrix rndMtx1 = new LazyRandomMatrix(1234) // Seed new object
float X = rndMtx1[0,0] // Lazily generate random numbers on demand
float Y = rndMtx1[3,16]
float Z = rndMtx1[23,-5]
Debug.Assert(X == rndMtx1[0,0])
Debug.Assert(Y == rndMtx1[3,16])
Debug.Assert(Z == rndMtx1[23,-5])
LazyRandomMatrix rndMtx2 = new LazyRandomMatrix(1234) // Seed second object
Debug.Assert(Y == rndMtx2[3,16]) // Lazily generate the same random numbers
Debug.Assert(Z == rndMtx2[23,-5]) // on demand in a different order
Debug.Assert(X == rndMtx2[0,0])
是的,如果我知道数组的维度,最好的方法是生成整个数组,并且只返回值,但是它们需要独立地根据需要生成
我的第一个想法是为每次调用一个新的坐标初始化一个新的随机数生成器,用整个矩阵的种子和调用中使用的坐标的散列来播种,但这似乎是一个可怕的黑客行为,因为它需要创建大量新的
随机对象。标准随机生成器通常是序列生成器,其中每个下一个元素都是从上一个元素构建的。因此,要生成rndMtx1[3,16]
必须在一个序列中生成所有先前的元素。
实际上,您需要一些不同于随机生成器的东西,因为您只需要一个值,而不需要序列。您必须构建自己的“生成器”,它使用种子和索引作为公式的输入来生成单个随机值。你可以发明许多方法来做到这一点。最简单的方法之一是获取随机值asm散列(seed+index)
(我想密码和签名中使用散列的想法是从输入数据中产生一些稳定的“随机”值)
另外,您可以通过生成惰性矩阵块来改进独立生成器(Random(seed+index)
)的方法。伪随机数生成器本质上是一个函数,可以确定地计算给定值的后继值
你可以发明一个简单的算法,从它的邻居那里计算一个值。如果一个邻居还没有值,首先从其各自的邻居计算其值
大概是这样的:
- 值(0,0)=种子
- 值(x+1,0)=后继值(值(x,0))
- 值(x,y+1)=后继值(值(x,y))
后继(n)=n+1以计算值(2,4)的示例:
\x12
y+-------------------
0 | 627 628 629
1 | 630
2 | 631
3 | 632
4 | 633
这个示例算法显然不是很好,但你明白了。我认为你的第一个想法是实例化一个新的随机对象,该对象由一些确定性散列(x坐标、y坐标、LazyRandomMatrix种子)播种,这在大多数情况下可能是合理的。一般来说,在托管堆上创建大量小对象是CLR非常擅长高效处理的事情。我不认为Random.ctor()非常昂贵。如果是一个问题,您可以轻松衡量性能
一个非常类似的解决方案可能比创建一个好的确定性哈希更容易,就是每次使用两个随机对象。比如:
public int this[int x, int y]
{
get
{
Random r1 = new Random(_seed * x);
Random r2 = new Random(y);
return (r1.Next() ^ r2.Next());
}
}
据我所知,这里有两种基本算法:
- 根据每个坐标的
func(种子,坐标)
生成一个新的随机数
- 从seed生成一个随机数,然后将其转换为坐标(类似于
旋转(x)+平移(y)
或其他)
在第一种情况下,您总是会遇到生成新随机数的问题,尽管这可能不像您担心的那样昂贵
在第二种情况下,问题是在转换操作期间可能会丢失随机性。然而,无论哪种情况,结果都是可重复的
这就是MS Research在并行随机数生成或GPU上所做的。
(事实上,PRNG可以被认为是来自(seed,state)=>nextnumber的散列函数。)
在GPU上生成大量随机数也会带来类似的问题。
(例如,为了使其并行,不应存在单个共享状态。)
虽然在加密世界中使用加密散列更为常见,但为了速度和简单起见,我还是冒昧地使用了加密散列。它作为散列函数具有非常好的统计特性。一项相关但不完全相同的测试表明,它作为PRNG给出。(除非我在C代码中有fsc#kd,即:)请随意使用任何其他合适的哈希函数;加密的(MD5,TEA,SHA)也一样——尽管加密散列往往要慢得多
public class LazyRandomMatrix
{
private uint seed;
public LazyRandomMatrix(int seed)
{
this.seed = (uint)seed;
}
public int this[int x, int y]
{
get
{
return MurmurHash2((uint)x, (uint)y, seed);
}
}
static int MurmurHash2(uint key1, uint key2, uint seed)
{
// 'm' and 'r' are mixing constants generated offline.
// They're not really 'magic', they just happen to work well.
const uint m = 0x5bd1e995;
const int r = 24;
// Initialize the hash to a 'random' value
uint h = seed ^ 8;
// Mix 4 bytes at a time into the hash
key1 *= m;
key1 ^= key1 >> r;
key1 *= m;
h *= m;
h ^= key1;
key2 *= m;
key2 ^= key2 >> r;
key2 *= m;
h *= m;
h ^= key2;
// Do a few final mixes of the hash to ensure the last few
// bytes are well-incorporated.
h ^= h >> 13;
h *= m;
h ^= h >> 15;
return (int)h;
}
}
您所谈论的通常被称为“柏林噪音”,这里有一个链接供您参考:
这篇文章中最重要的是2D中的噪波函数:
function Noise1(integer x, integer y)
n = x + y * 57
n = (n<<13) ^ n;
return ( 1.0 - ( (n * (n * n * 15731 + 789221) + 1376312589) & 7fffffff) / 1073741824.0);
end function
函数Noise1(整数x,整数y)
n=x+y*57
n=(n这是一个基于SHA1散列的解决方案。基本上,它将X、Y和种子值的字节打包到一个字节数组中。然后是字节数组的散列和用于生成int
的散列的前4个字节。这应该是非常随机的
public class LazyRandomMatrix
{
private int _seed;
private SHA1 _hashProvider = new SHA1CryptoServiceProvider();
public LazyRandomMatrix(int seed)
{
_seed = seed;
}
public int this[int x, int y]
{
get
{
byte[] data = new byte[12];
Buffer.BlockCopy(BitConverter.GetBytes(x), 0, data, 0, 4);
Buffer.BlockCopy(BitConverter.GetBytes(y), 0, data, 4, 4);
Buffer.BlockCopy(BitConverter.GetBytes(_seed), 0, data, 8, 4);
byte[] hash = _hashProvider.ComputeHash(data);
return BitConverter.ToInt32(hash, 0);
}
}
}
您需要随机访问元素的随机数生成器,而不是顺序访问。(注意,您可以将两个坐标减少为一个索引,即通过计算i=x+(y)随机性的质量对您来说重要吗?因此,数字具有良好的“随机性”重要吗分布?我希望随机数具有与静态生成数组类似的分布(构造函数中具有已知维度的
循环的一个大的)。基本上是最好的(尽管显然不可能)解决方案是在创建对象时生成一个无限的随机数矩阵,然后简单地返回所需的任何值。这将很困难。我不知道有哪个随机数生成器允许您随机访问随机序列(尽管可能有一个),伪随机数生成器生成num
public class LazyRandomMatrix
{
private int _seed;
private SHA1 _hashProvider = new SHA1CryptoServiceProvider();
public LazyRandomMatrix(int seed)
{
_seed = seed;
}
public int this[int x, int y]
{
get
{
byte[] data = new byte[12];
Buffer.BlockCopy(BitConverter.GetBytes(x), 0, data, 0, 4);
Buffer.BlockCopy(BitConverter.GetBytes(y), 0, data, 4, 4);
Buffer.BlockCopy(BitConverter.GetBytes(_seed), 0, data, 8, 4);
byte[] hash = _hashProvider.ComputeHash(data);
return BitConverter.ToInt32(hash, 0);
}
}
}
X(0) = S
X(n) = B + X(n-1)*A (mod M)
//Expand a few times to see the pattern:
X(n) = B + X(n-1)*A (mod M)
X(n) = B + (B + X(n-2)*A)*A (mod M)
X(n) = B + (B + (B + X(n-3)*A)*A)*A (mod M)
//Aha! I see it now, and I can reduce it to a closed form:
X(n) = B + B*A + B*A*A + ... + B*A^(N-1) + S*A^N (mod M)
X(n) = S*A^N + B*SUM[i:0..n-1](A^i) (mod M)
X(n) = S*A^N + B*(A^N-1)/(A-1) (mod M)