痛苦的慢迷宫制作程序

时间:2014-04-05 19:51:21

标签: c++ performance random maze

我正在编写一个可以生成任何大小迷宫的程序。它通过首先创建迷宫中的每个单元格并假设它们完全被围住来实现这一点。它们都被声明为它们自己的集合。然后选择随机单元,然后选择随机方向来分解墙。随机方向功能确保它也是该单元的有效方向。该程序确保它想要加入的两个单元格在某种程度上已经连接,如果它们不是它就会破坏墙壁。如果它们已经直接或间接连接,那么它会选择一个新的随机单元和方向。这一直持续到剩下的数量只有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 ncols\n";
    }

    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 << "true\n";
            walls.push_back(possible[i]);
        }
//      cout << "false\n";
    }

    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 << "inconnections\n";      
        S[cell1].connections.push_back(cell2);
        S[cell2].connections.push_back(cell1);
//      cout << "notinconnections\n"; 

        // 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;

}

2 个答案:

答案 0 :(得分:3)

请学会通过参考传递,而不是价值。

例如:

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

您按值传递矢量。这意味着在调用函数时会生成整个副本。如果您的矢量有100,000个项目,则会生成不必要的副本。改为:

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

现在没有复制完成。在所有其他功能中进行相同的更改。

你也可以按值返回一个向量,但除非证明你的编译器不能或不会优化副本,否则我会留下它们。

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

答案 1 :(得分:0)

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

要测试是否是这种情况,您可以让程序记录每次确定连接两个单元(或连接10次)是否需要花费多长时间,并且确定列表中的时间是否线性增加,那么这就是问题的一部分。

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

对不起,我无法提供针对代码的更好建议,但是我正在研究如何创建迷宫并遇到您的问题,希望我的回答至少是值得深思的。