痛苦缓慢的迷宫制作程序

Painfully slow maze making program

本文关键字:程序 迷宫 缓慢 痛苦      更新时间:2023-10-16

我正在编写一个程序,可以生成您想要的任何大小的迷宫。它首先在迷宫中创建每个单元格并假设它们完全被围起来。它们各自声明为自己的集合。然后选择一个随机单元格,然后选择一个随机方向来分解墙壁。随机方向函数确保它也是该单元格的有效方向。该程序确保它希望加入的两个单元尚未以某种方式连接,如果它们没有连接,它就会打破墙壁。如果它们已经直接或间接连接,则它会选择一个新的随机单元和方向。这种情况一直持续到剩余的集合数仅为 1,确保您可以从迷宫中的任何点到达任何其他点。该程序可以工作,但速度非常慢。我认为它不应该像现在这样慢,我不确定为什么。

我可以想象一个场景,所有细胞都连接在一起,只有一个。因此,随机选择一个单元格需要一点时间,这可能会减慢速度,但我想当你处理少于 100,000 个单元格时,它仍然不应该花费那么长时间。兰德应该在吐出数字方面做得很快。

我在下面附上了我的代码。它相当简单,但我很抱歉缺少笔记。

#include <iostream>
#include <cstdlib>
#include <ctime>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
using namespace std;
class dset {
    struct element {
        element() { rank=0, parent=-1; }
        int rank;
        int parent;
        vector<int> connections;
    };
    public:
        dset(int nr=0,int nc=0);
        int size() {return Nsets; }
        int merge (int, int);
        int find(int);
        // Functions
        bool isin(int i, vector<int> test);
        int randdir(int i);
        int randcell();
        int dir(int, int);
        void print();
        vector<int> possibledir(int cell);
        vector<int> walls(int cell, vector<int> possible);
    private:
        int Nsets;
        int nrows, ncols;
        vector<element> S;
};
int main(int argc, char* argv[]){
    int nrows, ncols, cell, direction;
    if (argc != 3){
        cout << "Usage: nrows ncolsn";
    }
    stringstream convert;

    convert << argv[1];
    convert << " ";
    convert << argv[2];
    convert >> ncols;
    convert >> nrows;

    dset maze(nrows,ncols);
    srand(time(NULL));

    while(maze.size() != 1){
        cell = maze.randcell();
//      cell = 11; 
        direction = maze.randdir(cell);
//      direction = 0;
//      cout << "cell: " << cell << "  direction: " << direction << "  new cell: " << maze.dir(cell, direction) <<endl << endl;
//      cout << maze.size() << endl<<endl;;
        maze.merge(cell, maze.dir(cell, direction));
    }
    maze.print();
}
dset::dset(int nr,int nc) {
    nrows = nr;
    ncols = nc;
    int N = (nrows * ncols);
    if (0<N) S.insert(S.end(), N, element());
    Nsets = N;
}
void dset::print(){
    vector<int> wall;
    cout << "MAZE " << nrows << " " << ncols << endl;
    for ( int i = 0; i < (nrows*ncols); i++ ){
        wall = walls(i,possibledir(i));
        for( int j = 0; j < wall.size(); j++){
            if (i < wall[j])
                cout << i << " " << wall[j] << endl;
    }
}
}

int dset::randcell(){
    return (rand()%(nrows*ncols));
}
int dset::dir(int cell, int direction){
    if(direction == 0)
        return (cell - 1);
    if(direction == 1)
        return (cell - (ncols));
    if(direction == 2)
        return (cell+1);
    if(direction == 3)
        return (cell + ncols);
}

int dset::randdir(int i){
    srand(time(NULL));
    int direction;
    vector<int> used;
//cout << "i : " << i << endl;  
    while (true){
        direction = rand() % 4;
        while (true){
            if(isin(direction,used))
                direction = rand()%4;
            else
                break;
        }
        //      cout << "rand: " << direction << endl;
        if(direction ==0){
            if( i != 0){
//              cout << 0 << " i%(ncols -1) :" << (i%(ncols -1)) << endl; 
                if(i%(ncols) != 0){
                    break;
                }
            }
        }
        if(direction == 1){
//              cout << 1 << " i - ncols :" << (i-ncols) << endl; 
            if(i-ncols > 0){
                break;
            }

        }
        if (direction == 2){
//              cout << 2 << " i%(ncols) :" << (i%ncols) << endl; 
            if ( i == 0 )
                break;
            if (i%ncols != ncols-1){
                break;
            }
        }
        if (direction == 3){
            if (i+ncols < ((nrows*ncols))){
//              cout << 3 << " i+ncols :" << (i+ncols) << endl; 
                break;
            }
        }
        used.push_back(direction);
    }
    return direction;
}
vector<int> dset::possibledir(int cell){
    vector<int> possible;
//  cout << "cell  " << cell << " possible connections:n";
    for (int i = 0; i < 4; i++){
        if (i == 0){
            if( cell != 0 ){
                if(cell%(ncols) !=0){
//                  cout << dir(cell,i) <<endl;
                    possible.push_back(dir(cell,i));
                }
            }
        }
        if(i==1){
            if (cell-ncols > 0){
//              cout<<dir(cell,i) <<endl;
                possible.push_back(dir(cell,i));
            }
        }
        if(i==2){
            if(cell == 0){
//              cout<<dir(cell,i) <<endl;
                possible.push_back(1);
            }else if(cell%ncols != ncols-1){
//              cout<<dir(cell,i) <<endl;
                possible.push_back(dir(cell,i));
            }
        }
        if(i==3){
            if ( cell+ncols < ((nrows*ncols))){
//              cout<<dir(cell,i) <<endl;
                possible.push_back(dir(cell,i));
        }
        }

    }
//  cout << endl;
    return possible;
}

vector<int> dset::walls(int cell, vector<int> possible){
    vector<int> walls;
//  cout << cell <<  " connection 0: " << S[cell].connections[0] << endl;
    for(int i = 0; i < possible.size(); i++){
        if (!isin(possible[i], S[cell].connections)){
//          cout << "truen";
            walls.push_back(possible[i]);
        }
//      cout << "falsen";
    }
    return walls;
}

int dset::merge(int i, int j) {
    int cell1 = i;
    int cell2 = j;
    i = find(i);
    j = find(j);
    if (i != j) {
        element &Si = S[i];
        element &Sj = S[j];
        // Adjust Adjacency List
//      cout << "inconnectionsn";      
        S[cell1].connections.push_back(cell2);
        S[cell2].connections.push_back(cell1);
//      cout << "notinconnectionsn"; 
        // merge (union) by rank
        if (Si.rank > Sj.rank)  Sj.parent = i;
        else if (Si.rank < Sj.rank) Si.parent = j;
        else { Sj.parent = i; Si.rank +=1; }
        Nsets -=1;
    }
    return find(i);
}
int dset::find(int i) {
    if (S[i].parent == -1){
        return i;
    }
    // recursive path compression
    S[i].parent = find(S[i].parent);
    return S[i].parent;
}
bool dset::isin(int i, vector<int> test){
    bool out = false;
    for(int j = 0; j < test.size(); j++){
        if(test[j] == i)
            out = true;
    }
    return out;
}

请学会通过引用传递,而不是值。

例如:

bool dset::isin(int i, vector<int> test)

您正在按值传递向量。 这意味着在调用函数时会创建整个副本。 如果您的向量有 100,000 个项目,则会创建不必要的副本。 更改为此内容:

bool dset::isin(int i, vector<int>& test)

现在没有完成任何复制。 在所有其他函数中进行相同的更改。

您还按值返回向量,但除非证明您的编译器不能或不会优化副本,否则我会不理会这些。

此外,请确保您正在安排发布、优化程序,而不是"调试"或未优化的程序。 由于您没有提及正在使用的编译器,因此请在生成程序时使用生成优化代码的设置。

虽然我对 c++ 了解不多,但从您一开始的程序描述来看,当您的程序确定两个可预期连接的单元是否已经连接时,您的速度可能会变慢。由于大多数(如果不是全部)使用此方法的情况必须确定单元未连接,因此只有一个适当的解决方案,因此每次完成此操作时,您的程序都必须检查/解决整个迷宫到该点,以确保没有办法已经连接。这意味着随着迷宫的现有部分变大,完成此任务所需的时间将变得越来越长。

要测试是否是这种情况,您可以让程序记录每次(或 10 次)连接两个单元格所需的时间,如果列表中的时间线性变长,那么这个或类似的东西是问题的一部分。

您可以通过允许已连接的单元通过另一条路径连接,或者通过简化程序检查连接哪些单元的方式来解决此问题。

抱歉,我无法提供更好的特定于代码的建议,但我正在研究如何创建迷宫并遇到了您的问题,希望我的答案至少值得深思。