打乱一个浮点数?
Posted
技术标签:
【中文标题】打乱一个浮点数?【英文标题】:Scramble a floating point number? 【发布时间】:2011-09-05 20:56:55 【问题描述】:我需要一个可重复的伪随机函数,从 [0,1] 中的浮点数到 [0,1] 中的浮点数。 IE。给定一个 32 位 IEEE 浮点数,返回一个“不同”的浮点数(尽可能随机,给定 24 位尾数)。它必须是可重复的,所以保持大量的内部状态是不可能的。不幸的是,它只能使用 32 位 int 和单浮点数学(没有双精度,甚至没有 32x32=64 位乘法,尽管如果需要我可以模拟——基本上它需要在较旧的 CUDA 硬件上工作)。当然,在这些相当严格的限制范围内,随机性越好。有人有什么想法吗?
(我经历过 Park-Miller,它需要 64 位 int 数学,以及 Park-Miller 的 CUDA 版本,它需要双精度数,Mersenne Twisters 有很多内部状态,以及其他一些不需要的东西'没用。)
【问题讨论】:
大量内部状态并不意味着不可重现。您是否严格需要一个在给定float
的情况下返回float
的函数,并且很难发现两者之间的相关性?或者你真的在寻找一个计算能力有限的好的伪随机数生成器?
函数是否应该可逆?它是否需要具有强大的加密能力?
可逆?不,加密强大?不,我这样做是出于图形目的,所以我需要分布合理的结果。基本上给定一个 (x,y) 作为浮点数,我希望我的函数返回一个新的伪随机 (x,y) 点作为仅原始 x,y 的函数。或一维相同:给定 x,返回 x',其中 x'“看起来”是随机的。可重复我的意思是结果必须纯粹是输入的函数。
【参考方案1】:
我最好理解这些要求,哈希可以完成所需的功能。将浮点输入重新解释为整数,应用散列函数产生一个大致均匀分布在 [0,2^32) 中的整数,然后将该整数乘以 2^-32 以将所得整数转换回大致均匀的浮点数分布在 [0,1] 中。一种不需要乘法的合适哈希函数是 Bob Jenkin 的 mix(),可以在这里找到:http://www.burtleburtle.net/bob/hash/doobs.html。
要将浮点数的位重新解释为整数,反之亦然,CUDA 中有两种选择。使用内在函数,或使用 C++ 风格的重新解释强制转换:
float f;
int i;
i = __float_as_int(f);
f = __int_as_float(i);
i = reinterpret_cast<int&>(f);
f = reinterpret_cast<float&>(i);
所以作为一个自包含的函数,整个过程可能看起来像这样:
/* transform float in [0,1] into a different float in [0,1] */
float scramble_float (float f)
unsigned int magic1 = 0x96f563ae; /* number of your choice */
unsigned int magic2 = 0xb93c7563; /* number of your choice */
unsigned int j;
j = reinterpret_cast<unsigned int &>(f);
mix (magic1, magic2, j);
return 2.3283064365386963e-10f * j;
【讨论】:
酷!绝对不是我以前的想法,但目标正确。 事实证明这是一个很好的方向。我最终使用了两轮不同但出色的混合功能(1-input 32-bit no-collision)我在@找到的 Jenkins 987654322@ 太大,无法粘贴到这里,但效果很好。【参考方案2】:NVIDIA CUDA 工具包包含一个名为 CURAND 的库,我认为它符合您的要求:它产生可重复的结果(假设您从相同的种子开始),在 GPU 上工作,支持 32 位浮点数和整数,并且应该在较旧的 GPU 上工作。它还支持多种伪随机和准随机生成算法和分布。
[注意:使用 C 库 rand() 函数的一个问题(除了它不在设备上的 CUDA 中运行)是在 Windows 上,rand() 只返回一个 16 位值,因此任何由 RAND_MAX 除法创建的浮点数只有 16 个随机位精度。更重要的是,在 linux/mac 上它返回一个 32 位的值,所以使用它的代码在数字上是不可移植的。]
【讨论】:
您的回答是正确的,因为我提出了这个问题——谢谢。不幸的是,我遗漏了一项要求;在 CPU 上运行时,我需要与 GPU 上相同的值,因此仅 CUDA 的库对我不起作用。这就是为什么我一直在尝试自己做,所以我可以获得可移植的(GPU/CPU)代码。 curand 设备 API 的实现在 curand_kernel.h 中非常清晰——您可以非常简单地将其移植到 CPU 上。作为开始,您可能只是尝试更改该文件中的#define QUALIFIERS,以便使用#define QUALIFIERS static inline __device__ __host__
而不是#define QUALIFIERS static inline __device__
。这应该使您从设备内核调用的所有函数都可以从主机代码调用。 (可能还有其他问题,这个我没试过)。
或者,您可以使用 curand 主机 API 在 GPU 上生成一个随机值数组,然后将该数组复制回主机以用于 CPU 代码。【参考方案3】:
为什么不使用标准 C 库 rand()
函数并将结果除以 RAND_MAX
?
#include <stdlib.h>
float randf (void)
return rand() / (float) RAND_MAX;
【讨论】:
在调用rand
之前不要忘记使用srand
播种。
不幸的是,它不适用于 CUDA。但至少在 CPU 上你是对的,你可以将浮点位转换为 int i=*(int *)&f;然后用它来播种 srand。以上是关于打乱一个浮点数?的主要内容,如果未能解决你的问题,请参考以下文章