我想知道如何在固定位置获取一个随机数,所以当我回到那个位置时,我会看到相同的随机数。
首先我尝试过这样的事情:
int getRand( int x , int y )
{
srand( x * y );
return rand();
}
但这里-1 * -1与1 * 1相同,因此我会得到很多对称性。
我接着尝试过:
int getRand( int x , int y )
{
srand( x * ( 1 << 12 ) + y );
return rand();
}
和
int getRand( int x , int y ) // this one gave the best result
{
srand( x );
int rand1 = rand();
srand( y );
int rand2 = rand();
srand( rand1 * rand2 );
return rand();
}
和
int getRand( int x , int y )
{
srand( x );
int rand1 = rand();
srand( y );
int rand2 = rand();
srand( rand1 * ( 1 << 12 ) + rand2 );
return rand();
}
那么我应该怎么做呢,因为以上都没有给出好的结果? (很好,我的意思是随机,它看起来都很重复)
答案 0 :(得分:1)
通常您要避免srand
和rand
。它们修改全局状态,因此(作为一个可能的问题)您的代码不是线程安全的。此外,它们不能保证生成非常高质量的随机数据,而且在许多常见的实现中它们都没有。
根据你对rand
所做的事情,它看起来像你真正想要的是一个“随意的”哈希函数,也就是说getRand(x,y)
之间没有明显的关系和getRand(x+1,y)
,getRand(x,y+1)
,getRand(x+1, y-1)
或输出与输入之间的任何其他明显关联。
无论如何,与哈希表一起使用的整数上的哈希函数都不合适。通常这些只是识别功能。用于多次散列的附加散列通常使用一些素数步长或其他东西,因此仍然不是随机的。
如果您没有性能问题,那么最佳可用哈希函数是“加密安全”哈希。 MD5对于这个目的来说已经足够好了 - 它对于一般的现代加密使用来说太弱了,但它看起来非常随机。因此,例如,您可以将getRand(x,y)
定义为8字节序列的MD5哈希的最后4个字节,其中包含x
的4个字节,后跟y
的4个字节。 / p>
不幸的是,安全哈希很慢。如果您需要的是一个非常快速且有点随机查找的函数,那么像FNV这样的东西可能会这样做,并且可能折叠上部和下部位和/或乘以结果(在无符号类型)由一个大的素数,以便稍微混淆这些位。但请仔细查看结果,因为这不是 的预期用途。
如果安全散列太慢并且您仍然需要某种质量控制,那么您可以根据典型的访问模式使用PRNG改进。如果您对数据的典型访问不可预测,这对您没有任何帮助。
但是,例如,如果您主要按顺序访问垂直线(常量x
值,递增y
值),那么您可能会执行以下操作:
uint32_t getRand(uint32_t x, uint32_t y) {
std::mt19937 rng;
rng.seed(good_hash(x, y / 4096));
y = y % 4096;
std::uniform_int_distribution<uint32_t> dst;
while (y-- > 0) {
dst(rng);
}
return dst(rng);
}
good_hash
需要是一个模糊的散列函数,因为虽然Mersenne Twister输出的序列非常随机,但种子和第一个输出之间的关系却不是。
我使用过mt19937
(Mersenne Twister),因为它通常足够模拟,并且比加密安全的流生成器更快。
播种rng
相对于从中生成一个输出是昂贵的,因此您现在可以提供另一个更有效的顺序访问功能,因为它可以在重新种子之间返回4096个值。
如果您的典型访问权限不是垂直线,则相应地调整技术。例如,如果您一次只能访问地图的一个本地区域,那么:
x
和y
这是(非常粗略地说)文件系统级加密通常如何优化以允许在文件内有效搜索:文件系统可以一次解密一个块而无需每个字节进行昂贵的计算。就像任何文件系统一样,它通常会缓存它所看到的任何块,以便附近的其他访问速度很快。当然加密不使用Mersenne Twister,它不够安全。我相信它也必须使用好的哈希来选择种子 - 例如,它可能使用安全哈希函数将随机文件密钥(x
)与块的偏移量组合在一起(y / 4069
)
答案 1 :(得分:0)
这不是伪随机数生成器,它是一个散列函数。
哈希函数是具有此(理想)属性的函数:
因此,哈希函数可以被视为一个用输入识别结果的函数。
互联网上有很多关于创建好哈希函数主题的参考资料。但是,由于C ++ 11和std::unordered_map
容器(这是一个哈希映射)的添加,<functional>
头包含一个函数std::hash()
,它对大多数常见类型都是重载的。
答案 2 :(得分:0)
我可能在这里错了,但我会继续提问者的评论。
如果您尝试获取地图的随机高度值,则应将生成的数字存储在(2d)向量中。在这种情况下,您实际上并不需要哈希函数,因为您的代码似乎表明了这一点。
例如:
#include <vector>
#include <cstdlib>
#include <ctime>
int main()
{
std::srand(std::time(NULL));
int size_x = 100;
int size_y = 100;
std::vector<std::vector<int>> heights(size_x, std::vector<int>(size_y));
for(int i=0; i<size_x; ++i)
for(int j=0; j<size_y; ++j)
heights[i][j] = std::rand();
}
但请注意,我选择的种子和随机数生成器都不好。如果可以的话,你应该使用c ++ 11中引入的随机模块:http://www.cplusplus.com/reference/random/