【问题标题】:Why is my mt19937 Random generator giving me ridiculous results? C++为什么我的 mt19937 随机生成器给了我荒谬的结果? C++
【发布时间】:2013-02-26 02:55:53
【问题描述】:

正在处理另一个项目,我们需要使用mt19937 来随机生成数字。我们应该让它根据网格的截面随机选择一个 x 和 y 坐标。例如,我的函数将minXmaxXminYmaxY 传递给函数。我的 x 坐标工作正常。在测试运行时,我不断地随机出错。有时它会运行 10 次没有问题,然后出现错误。我添加了一些自调试行来显示 mt 生成器实际生成的内容。就像我说的,x 工作正常,y 有时也可以。它会随机给我一个 -3437892 或 9743903。

这是我的代码:

void DungeonLevel::generateRoom(int minX,int maxX,int minY, int maxY){
    mt19937 mt;
    mt.seed( time(NULL) );


    // Calculate random width and height; these both range
    // from 4-13
    int iRandomWidth = 4 + (mt() % 10);
    int iRandomHeight = 4 + (mt() % 10);

    // Calculate the start points in both X and Y directions

    int iStartX;
    iStartX = mt() % (maxX - iRandomWidth);
    cout << "xStart: " << iStartX<<endl; //cout flag
    while ((iStartX > maxX) && (iStartX >= 0)){
            cout << "xStart: " << iStartX<<endl;//cout flag
            iStartX = mt() % (maxX - iRandomWidth);
    }
    int iStartY = 0;
    iStartY = mt() % (maxY - iRandomHeight);
    cout<<"yStart: " <<iStartY<<endl; //cout flag
    while ((iStartY > maxY)){
            cout<<"yStart: " <<iStartY<<endl;//cout flag
            iStartY = (mt() % (maxY - iRandomHeight));
    }

    // Iterate through both x and y coordinates, and
    // set the tiles to room tiles
    // SINGLE ROOM
    for( int x = iStartX; x <= iStartX + iRandomWidth; x++ ){
            for( int y = iStartY; y <= iStartY + iRandomHeight; y++ ){
                    if (y == iStartY){
                            dungeonGrid[y][x] = '-';
                    }
                    else if (iStartX == x){
                            dungeonGrid[y][x] = '|';
                    }
                    else if (y == (iStartY+iRandomHeight)){
                            dungeonGrid[y][x] = '-';
                    }
                    else if (x == (iStartX+iRandomWidth)){
                            dungeonGrid[y][x] = '|';
                    }
                    else {
                            dungeonGrid[y][x] = '.';
                    }

            }
    }

}

【问题讨论】:

  • 我很困惑为什么它只发生在 iStartY 上。 X 工作正常。
  • mt19937 是打算成为 C++11 random number engine 还是某些用户定义的类型?
  • 在这种情况下,您的参数(minX、maxX 等)有哪些值?
  • @ModdedLife 我怀疑minY 有时小于 13。这可以解释。

标签: c++ random c++11


【解决方案1】:

我认为您应该对 mt19937 使用随机分布。所以用它来

mt19937 mt;
mt.seed( time(nullptr) );
std::uniform_int_distribution<int> dist(4, 13);

int iRandomWidth = dist(mt);
int iRandomHeight = dist(mt);

这样你就可以保证得到 4 到 13 之间的随机数。

更新 虽然我的回答解决了原始问题并且在我看来是代码可读性的改进,但它实际上并没有解决原始代码中的问题。另请参阅 jogojapan 的回答。

【讨论】:

  • 正是这个。我看不出 OP 代码中的错误在哪里,但是%n 存在很多问题,用于生成高达n 的随机数,此外,使用分布使代码变得更干净很多
  • 我同意这个建议,但为什么要使用uint32_t 分配给(signed) int 变量赋值呢?
  • @jogojapan:我选择了uint32_t,因为分发创建的所有值都是&gt;=0。但是int32_tint 应该一样好。您甚至可以将隐式强制转换保存为带符号的 int,但我认为这并不重要,不是吗?
  • @Haatschii 这个特定计算的结果并不重要,但是一旦intDist(mt) 成为更复杂计算的一部分,它就会引入一个你不会想到的无符号数据类型;反之亦然,接收端的 int 类型会在您意想不到的地方引入签名(和潜在的溢出)。这可能会导致 OP 似乎遇到的那种问题。
  • @ModdedLife 从您发布的代码中,我猜您有除以零,例如在iStartX = mt() % (maxX - iRandomWidth); 中(与 Y 相同的行)。 maxX 是什么?因为如果maxX - iRandomWidth 可能为零,则会导致此错误。
【解决方案2】:

问题的最终原因是您在代码中混合了有符号和无符号整数而没有采取必要的预防措施(也没有必要)。

具体来说,如果minY 有时小于 13,那么iRandomHeight 会时不时地出现负数。然后得到的效果类似于下面演示的效果:

#include <limits>
#include <iostream>

using namespace std;

int main()
{
  /* Unsigned integer larger than would fit into a signed one.
     This is the kind of thing mt199737 returns sometimes. */
  unsigned int i = ((unsigned int)std::numeric_limits<int>::max()) + 1000;
  cout << (i % 3) << endl;
  cout << (i % -3) << endl;
  cout << (signed)(i % -3) << endl;
  return 0;
}

这首先生成一个比有符号整数略大的无符号整数。 mt19937 返回无符号,有时会在上面的代码中为您提供类似 i 的值。

上面代码的输出(见on liveworkspace)如下:

2
2147484647
-2147482649

第二行显示了对负数进行模运算的结果(例如iRandomHeight 有时会),应用于大于适合相应有符号整数的无符号整数。第三行显示了当您将其转换回有符号整数时会发生什么(当您将其分配给您的一个有符号整数变量时,您会隐式执行此操作)。

虽然我同意 Haatschii 的观点,即您应该使用 std::uniform_int_distribution 让您的生活更轻松,但即使在那时,正确使用已签名和未签名也很重要。

【讨论】:

  • 这是国际海事组织的真正答案。 std::uniform_int_distribution 并不总是正确的选择,它有一些不受欢迎的属性。
【解决方案3】:

在@haatschii 的帮助下找出了我犯的业余错误。

现在很有意义。 iStartY 和 iStartX 没有被设置为低于或等于零的数字的限制。我因为没有抓住那个哈哈而感到很愚蠢。我添加了另一个循环以确保该值大于 0。我还将 iStartX 和 iStartY 值以 maxX+1 和 maxY+1 开头,以便它们自动进入循环以生成大于 0 的解决方案。

解决方法代码如下:

void DungeonLevel::generateRoom(int minX,int maxX,int minY, int maxY){
    mt19937 mt;
    mt.seed( time(NULL) );

    // Calculate random width and height; these both range
    // from 4-13
    int iRandomWidth = 4 + (mt() % 10);
    int iRandomHeight = 4 + (mt() % 10);

    int iStartX = maxX+1; //automatically has to enter the second while loop        
    while ((iStartX > maxX) && (iStartX >= 0)){
            while ((maxX - iRandomWidth) <= 0){
                    iRandomHeight = 4 + (mt() % 10); //makes value > 0
            }
            iStartX = mt() % (maxX - iRandomWidth);
    }

    int iStartY = maxY+1; //automatically has to enter the second loop
    while ((iStartY > maxY)){
            while ((maxY - iRandomHeight) <= 0){
                    iRandomHeight = 4 + (mt() % 10); //sets to valid value
            }
            iStartY = mt() % (maxY - iRandomHeight);
    }
    // Iterate through both x and y coordinates, and
    // set the tiles to room tiles
    // SINGLE ROOM
    for( int x = iStartX; x <= iStartX + iRandomWidth; x++ ){
            for( int y = iStartY; y <= iStartY + iRandomHeight; y++ ){
                    if (y == iStartY){
                            dungeonGrid[y][x] = '-';
                    }
                    else if (iStartX == x){
                            dungeonGrid[y][x] = '|';
                    }
                    else if (y == (iStartY+iRandomHeight)){
                            dungeonGrid[y][x] = '-';
                    }
                    else if (x == (iStartX+iRandomWidth)){
                            dungeonGrid[y][x] = '|';
                    }
                    else {
                            dungeonGrid[y][x] = '.';
                    }

            }
    }

}

谢谢大家的建议!

【讨论】:

    猜你喜欢
    • 2021-02-22
    • 2022-01-13
    • 1970-01-01
    • 1970-01-01
    • 2016-08-31
    • 1970-01-01
    • 2023-03-10
    • 1970-01-01
    • 2020-08-05
    相关资源
    最近更新 更多