固定位置的随机数

时间:2014-01-12 14:17:13

标签: c++ random

我想知道如何在固定位置获取一个随机数,所以当我回到那个位置时,我会看到相同的随机数。

首先我尝试过这样的事情:

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();
}

那么我应该怎么做呢,因为以上都没有给出好的结果? (很好,我的意思是随机,它看起来都很重复)

3 个答案:

答案 0 :(得分:1)

通常您要避免srandrand。它们修改全局状态,因此(作为一个可能的问题)您的代码不是线程安全的。此外,它们不能保证生成非常高质量的随机数据,而且在许多常见的实现中它们都没有。

根据你对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个值。

如果您的典型访问权限不是垂直线,则相应地调整技术。例如,如果您一次只能访问地图的一个本地区域,那么:

  • 将地图划分为较小的正方形,例如1024 * 1024像素
  • 使用较小正方形的索引对RNG进行种子处理,也就是说xy
  • 的最高位
  • 为1024 * 1024输出运行RNG以生成小方块的所有随机值,并在访问该区域时存储这些值。我想你希望能够一次缓存4个小方块,以处理与小方块的角重叠的区域。那只需要16MB。

这是(非常粗略地说)文件系统级加密通常如何优化以允许在文件内有效搜索:文件系统可以一次解密一个块而无需每个字节进行昂贵的计算。就像任何文件系统一样,它通常会缓存它所看到的任何块,以便附近的其他访问速度很快。当然加密不使用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/