I've seen this pseudo-random number generator for use in shaders referred to here and there around the web:
我已经见过这个伪随机数生成器在这里和网络中使用的着色器中使用
float rand(vec2 co){
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
It's variously called "canonical", or "a one-liner I found on the web somewhere".
它被称为“canonical”,或者“我在网络上找到的一套单行代码”。
What's the origin of this function? Are the constant values as arbitrary as they seem or is there some art to their selection? Is there any discussion of the merits of this function?
这个函数的原点是什么?这些不变的价值是任意的,还是有些艺术的选择?对这个函数的优点有什么讨论吗?
EDIT: The oldest reference to this function that I've come across is this archive from Feb '08, the original page now being gone from the web. But there's no more discussion of it there than anywhere else.
编辑:我所看到的这个函数的最古老的引用是这个从2008年2月8日开始的档案,原来的页面现在已经从网上消失了。但是没有比其他地方更多的讨论了。
4 个解决方案
#1
35
Very interesting question!
很有趣的问题!
I am trying to figure this out while typing the answer :) First an easy way to play with it: http://www.wolframalpha.com/input/?i=plot%28+mod%28+sin%28x*12.9898+%2B+y*78.233%29+*+43758.5453%2C1%29x%3D0..2%2C+y%3D0..2%29
我在输入答案的时候试图找出答案:首先是一种简单的方法来玩:http://www.wolframalpha.com/input/? i% 28+mod%28+sin%28x*12.9898+%2B+y*78.233%29+*+43758.5453%2C1%29x% 2%2 %2 +y%3D0..2%29。
Then let's think about what we are trying to do here: For two input coordinates x,y we return a "random number". Now this is not a random number though. It's the same every time we input the same x,y. It's a hash function!
然后我们考虑一下我们要做的是:对于两个输入坐标x,我们返回一个“随机数”。这不是一个随机数。每次输入相同的x y都是一样的。这是一个哈希函数!
The first thing the function does is to go from 2d to 1d. That is not interesting in itself, but the numbers are chosen so they do not repeat typically. Also we have a floating point addition there. There will be a few more bits from y or x, but the numbers might just be chosen right so it does a mix.
函数做的第一件事是从2d到1d。这本身并不有趣,但数字是被选择的,所以它们不会重复。还有一个浮点加法。会有更多的y或x,但是数字可能会被选对,所以它会混合。
Then we sample a black box sin() function. This will depend a lot on the implementation!
然后我们对一个黑盒sin()函数进行采样。这将很大程度上取决于实现!
Lastly it amplifies the error in the sin() implementation by multiplying and taking the fraction.
最后,它通过乘和取分数来放大sin()实现中的错误。
I don't think this is a good hash function in the general case. The sin() is a black box, on the GPU, numerically. It should be possible to construct a much better one by taking almost any hash function and converting it. The hard part is to turn the typical integer operation used in cpu hashing into float (half or 32bit) or fixed point operations, but it should be possible.
在一般情况下,我不认为这是一个好的哈希函数。sin()是一个黑盒子,在GPU上,数值上。可以通过使用几乎所有的哈希函数并转换它来构造一个更好的函数。最困难的部分是将cpu哈希中使用的典型整数操作转换为浮点(半或32位)或定点操作,但应该是可能的。
Again, the real problem with this as a hash function is that sin() is a black box.
同样,这个作为哈希函数的真正问题是sin()是一个黑盒子。
#2
14
The origin is probably the paper: "On generating random numbers, with help of y= [(a+x)sin(bx)] mod 1", W.J.J. Rey, 22nd European Meeting of Statisticians and the 7th Vilnius Conference on Probability Theory and Mathematical Statistics, August 1998
该论文的来源可能是:“在生成随机数时,借助y= [(a+x)sin(bx)] mod 1”,wj . j . Rey,第22届欧洲统计学家会议和第七届Vilnius概率论与数理统计会议,1998年8月。
EDIT: Since I can't find a copy of this paper and the "TestU01" reference may not be clear, here's the scheme as described in TestU01 in pseudo-C:
编辑:由于我找不到这篇论文的副本,而“TestU01”的引用可能不清楚,以下是伪c的TestU01中描述的方案:
#define A1 ???
#define A2 ???
#define B1 pi*(sqrt(5.0)-1)/2
#define B2 ???
uint32_t n; // position in the stream
double next() {
double t = fract(A1 * sin(B1*n));
double u = fract((A2+t) * sin(B2*t));
n++;
return u;
}
where the only recommended constant value is the B1.
唯一推荐的常数是B1。
Notice that this is for a stream. Converting to a 1D hash 'n' becomes the integer grid. So my guess is that someone saw this and converted 't' into a simple function f(x,y). Using the original constants above that would yield:
注意这是一条流。转换为1D哈希'n'变成整数网格。我猜是有人看到了这个,把t变成了一个简单的函数f(x,y)使用上面的原始常数可以得到:
float hash(vec2 co){
float t = 12.9898*co.x + 78.233*co.y;
return fract((A2+t) * sin(t)); // any B2 is folded into 't' computation
}
#3
6
the constant values are arbitrary, especially that they are very large, and a couple of decimals away from prime numbers.
常数的值是任意的,特别是它们很大,并且有几个小数远离质数。
a modulus over 1 of a hi amplitude sinus multiplied by 4000 is a periodic function. it's like a window blind or a corrugated metal made very small because it's multiplied by 4000, and turned at an angle by the dot product.
一个hi振幅的模量乘以4000是一个周期函数。它就像一个百叶窗或瓦楞金属做的很小因为它乘以4000,然后以点积的角度旋转。
as the function is 2-D, the dot product has the effect of turning the periodic function at an oblique relative to X and Y axis. By 13/79 ratio approximately. It is inefficient, you can actually achieve the same by doing sinus of (13x + 79y) this will also achieve the same thing I think with less maths..
当函数为2-D时,点积的作用是将周期函数与X轴和Y轴倾斜。13/79比例约。它是低效的,你可以通过做sinus (13x + 79y)来达到同样的效果。
If you find the period of the function in both X and Y, you can sample it so that it will look like a simple sine wave again.
如果你在X和Y中找到函数的周期,你可以对它进行采样,这样它看起来就像一个简单的正弦波。
Here is a picture of it zoomed in graph
这是一张放大的图。
I don't know the origin but it is similar to many others, if you used it in graphics at regular intervals it would tend to produce moire patterns and you could see it's eventually goes around again.
我不知道它的起源,但它和其他很多东西相似,如果你用它在图形上定期的间隔它会产生波纹图案,你可以看到它最终会再次出现。
#4
1
Maybe it's some non-recurrent chaotic mapping, then it could explain many things, but also can be just some arbitrary manipulation with large numbers.
也许它是一些非经常的混沌映射,它可以解释很多东西,但也可以是一些任意的大数字的操作。
EDIT: Basically, the function fract(sin(x) * 43758.5453) is a simple hash-like function, the sin(x) provides smooth sin interpolation between -1 to 1, so sin(x) * 43758.5453 will be interpolation from -43758.5453 to 43758.5453. This is a quite huge range, so even small step in x will provide large step in result and really large variation in fractional part. The "fract" is needed to get values in range -0.99... to 0.999... . Now, when we have something like hash function we should create function for production hash from the vector. The simplest way is call "hash" separetly for x any y component of the input vector. But then, we will have some symmetrical values. So, we should get some value from the vector, the approach is find some random vector and find "dot" product to that vector, here we go: fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); Also, according to the selected vector, its lenght should be long engough to have several peroids of the "sin" function after "dot" product will be computed.
基本上,函数fract(sin(x) * 43758.5453)是一个简单的hashlike函数,sin(x)提供了在-1到1之间的平滑的sin插值,因此sin(x) * 43758.5453将由-43758.5453到43758.5453插值。这是一个相当大的范围,所以即使是x的小步骤也会提供很大的结果,在小数部分也会有很大的变化。需要“fract”来获取0.99范围内的值……0.999……现在,当我们有类似哈希函数的东西时,我们应该从向量中创建生产哈希函数。最简单的方法是,将输入向量的任何y分量称为“散列”。然后,我们会得到一些对称的值。因此,我们应该从向量中得到一些值,方法是找到一些随机向量,然后找到这个向量的“点”积,我们来看看:fract(sin(co)xy,vec2(12.9898,78.233)))* 43758.5453);此外,根据选定的矢量,它的长度应该是长engough,在“点”产品之后,将会计算出一些“sin”函数。
#1
35
Very interesting question!
很有趣的问题!
I am trying to figure this out while typing the answer :) First an easy way to play with it: http://www.wolframalpha.com/input/?i=plot%28+mod%28+sin%28x*12.9898+%2B+y*78.233%29+*+43758.5453%2C1%29x%3D0..2%2C+y%3D0..2%29
我在输入答案的时候试图找出答案:首先是一种简单的方法来玩:http://www.wolframalpha.com/input/? i% 28+mod%28+sin%28x*12.9898+%2B+y*78.233%29+*+43758.5453%2C1%29x% 2%2 %2 +y%3D0..2%29。
Then let's think about what we are trying to do here: For two input coordinates x,y we return a "random number". Now this is not a random number though. It's the same every time we input the same x,y. It's a hash function!
然后我们考虑一下我们要做的是:对于两个输入坐标x,我们返回一个“随机数”。这不是一个随机数。每次输入相同的x y都是一样的。这是一个哈希函数!
The first thing the function does is to go from 2d to 1d. That is not interesting in itself, but the numbers are chosen so they do not repeat typically. Also we have a floating point addition there. There will be a few more bits from y or x, but the numbers might just be chosen right so it does a mix.
函数做的第一件事是从2d到1d。这本身并不有趣,但数字是被选择的,所以它们不会重复。还有一个浮点加法。会有更多的y或x,但是数字可能会被选对,所以它会混合。
Then we sample a black box sin() function. This will depend a lot on the implementation!
然后我们对一个黑盒sin()函数进行采样。这将很大程度上取决于实现!
Lastly it amplifies the error in the sin() implementation by multiplying and taking the fraction.
最后,它通过乘和取分数来放大sin()实现中的错误。
I don't think this is a good hash function in the general case. The sin() is a black box, on the GPU, numerically. It should be possible to construct a much better one by taking almost any hash function and converting it. The hard part is to turn the typical integer operation used in cpu hashing into float (half or 32bit) or fixed point operations, but it should be possible.
在一般情况下,我不认为这是一个好的哈希函数。sin()是一个黑盒子,在GPU上,数值上。可以通过使用几乎所有的哈希函数并转换它来构造一个更好的函数。最困难的部分是将cpu哈希中使用的典型整数操作转换为浮点(半或32位)或定点操作,但应该是可能的。
Again, the real problem with this as a hash function is that sin() is a black box.
同样,这个作为哈希函数的真正问题是sin()是一个黑盒子。
#2
14
The origin is probably the paper: "On generating random numbers, with help of y= [(a+x)sin(bx)] mod 1", W.J.J. Rey, 22nd European Meeting of Statisticians and the 7th Vilnius Conference on Probability Theory and Mathematical Statistics, August 1998
该论文的来源可能是:“在生成随机数时,借助y= [(a+x)sin(bx)] mod 1”,wj . j . Rey,第22届欧洲统计学家会议和第七届Vilnius概率论与数理统计会议,1998年8月。
EDIT: Since I can't find a copy of this paper and the "TestU01" reference may not be clear, here's the scheme as described in TestU01 in pseudo-C:
编辑:由于我找不到这篇论文的副本,而“TestU01”的引用可能不清楚,以下是伪c的TestU01中描述的方案:
#define A1 ???
#define A2 ???
#define B1 pi*(sqrt(5.0)-1)/2
#define B2 ???
uint32_t n; // position in the stream
double next() {
double t = fract(A1 * sin(B1*n));
double u = fract((A2+t) * sin(B2*t));
n++;
return u;
}
where the only recommended constant value is the B1.
唯一推荐的常数是B1。
Notice that this is for a stream. Converting to a 1D hash 'n' becomes the integer grid. So my guess is that someone saw this and converted 't' into a simple function f(x,y). Using the original constants above that would yield:
注意这是一条流。转换为1D哈希'n'变成整数网格。我猜是有人看到了这个,把t变成了一个简单的函数f(x,y)使用上面的原始常数可以得到:
float hash(vec2 co){
float t = 12.9898*co.x + 78.233*co.y;
return fract((A2+t) * sin(t)); // any B2 is folded into 't' computation
}
#3
6
the constant values are arbitrary, especially that they are very large, and a couple of decimals away from prime numbers.
常数的值是任意的,特别是它们很大,并且有几个小数远离质数。
a modulus over 1 of a hi amplitude sinus multiplied by 4000 is a periodic function. it's like a window blind or a corrugated metal made very small because it's multiplied by 4000, and turned at an angle by the dot product.
一个hi振幅的模量乘以4000是一个周期函数。它就像一个百叶窗或瓦楞金属做的很小因为它乘以4000,然后以点积的角度旋转。
as the function is 2-D, the dot product has the effect of turning the periodic function at an oblique relative to X and Y axis. By 13/79 ratio approximately. It is inefficient, you can actually achieve the same by doing sinus of (13x + 79y) this will also achieve the same thing I think with less maths..
当函数为2-D时,点积的作用是将周期函数与X轴和Y轴倾斜。13/79比例约。它是低效的,你可以通过做sinus (13x + 79y)来达到同样的效果。
If you find the period of the function in both X and Y, you can sample it so that it will look like a simple sine wave again.
如果你在X和Y中找到函数的周期,你可以对它进行采样,这样它看起来就像一个简单的正弦波。
Here is a picture of it zoomed in graph
这是一张放大的图。
I don't know the origin but it is similar to many others, if you used it in graphics at regular intervals it would tend to produce moire patterns and you could see it's eventually goes around again.
我不知道它的起源,但它和其他很多东西相似,如果你用它在图形上定期的间隔它会产生波纹图案,你可以看到它最终会再次出现。
#4
1
Maybe it's some non-recurrent chaotic mapping, then it could explain many things, but also can be just some arbitrary manipulation with large numbers.
也许它是一些非经常的混沌映射,它可以解释很多东西,但也可以是一些任意的大数字的操作。
EDIT: Basically, the function fract(sin(x) * 43758.5453) is a simple hash-like function, the sin(x) provides smooth sin interpolation between -1 to 1, so sin(x) * 43758.5453 will be interpolation from -43758.5453 to 43758.5453. This is a quite huge range, so even small step in x will provide large step in result and really large variation in fractional part. The "fract" is needed to get values in range -0.99... to 0.999... . Now, when we have something like hash function we should create function for production hash from the vector. The simplest way is call "hash" separetly for x any y component of the input vector. But then, we will have some symmetrical values. So, we should get some value from the vector, the approach is find some random vector and find "dot" product to that vector, here we go: fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); Also, according to the selected vector, its lenght should be long engough to have several peroids of the "sin" function after "dot" product will be computed.
基本上,函数fract(sin(x) * 43758.5453)是一个简单的hashlike函数,sin(x)提供了在-1到1之间的平滑的sin插值,因此sin(x) * 43758.5453将由-43758.5453到43758.5453插值。这是一个相当大的范围,所以即使是x的小步骤也会提供很大的结果,在小数部分也会有很大的变化。需要“fract”来获取0.99范围内的值……0.999……现在,当我们有类似哈希函数的东西时,我们应该从向量中创建生产哈希函数。最简单的方法是,将输入向量的任何y分量称为“散列”。然后,我们会得到一些对称的值。因此,我们应该从向量中得到一些值,方法是找到一些随机向量,然后找到这个向量的“点”积,我们来看看:fract(sin(co)xy,vec2(12.9898,78.233)))* 43758.5453);此外,根据选定的矢量,它的长度应该是长engough,在“点”产品之后,将会计算出一些“sin”函数。