6

在另一个项目上工作,我们需要使用mt19937随机生成数字。我们应该让它根据网格的截面随机选择一个 x 和 y 坐标。例如,我的函数将minX, maxX, minY,传递maxY给一个函数。我的 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] = '.';
                    }

            }
    }

}
4

3 回答 3

16

我认为您应该对 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 的回答。

于 2013-02-26T03:10:42.060 回答
5

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

具体来说,如果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上面代码中的值。

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

2
2147484647
-2147482649

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

尽管我同意 Haatschii 的观点,即您应该使用std::uniform_int_distribution它来使您的生活更轻松,但即使在那时,适当地使用已签名和未签名也很重要。

于 2013-02-26T03:34:38.353 回答
0

在@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] = '.';
                    }

            }
    }

}

谢谢提醒伙计!

于 2013-02-27T03:06:54.130 回答