8 BFS拼图通过BFS

时间:2018-04-23 03:19:17

标签: c++ artificial-intelligence 8-puzzle

我搜索了互联网的深度,我还没有找到解决问题的方法。我已经实现了(我认为)滑动平铺游戏的BFS。但是,它不能解决问题,除非状态只有几步之遥,否则它只会导致内存不足错误。 所以我问你,我哪里错了? AFAIK我的代码遵循BFS伪代码。

编辑/注意:我已经使用了一个调试器并且还没有找到任何与众不同的东西,但我只是一个比较新手的程序员。

#include <ctime>
#include <string>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <deque>
#include <vector>

using namespace std;

///////////////////////////////////////////////////////////////////////////////////////////
//
// Search Algorithm:  Breadth-First Search 
//
// Move Generator:  
//
////////////////////////////////////////////////////////////////////////////////////////////
class State{
public:
    int state[9];

    State(){
        for (int i = 0; i < 9; i++){
            state[i] = i;
        }
    }

    State(string st){
        for (int i = 0; i < st.length(); i++){
            state[i] = st.at(i) - '0';
        }
    }

    State(const State &st){
        for (int i = 0; i < 9; i++){
            state[i] = st.state[i];
        }
    }

    bool operator==(const State& other) {

        for (int i = 0; i < 9; i++){
            if (this->state[i] != other.state[i]){return false;}
        }
        return true;
    }

    bool operator!=(const State& other) {
        return !(*this == other);
    }

    void swap(int x, int y){
        // State b; // blank state
        // for (int i = 0; i < 9; i++) // fill blank state with current state
        //  b.state[i] = state[i];

        int t = this->state[x]; // saves value of the value in position x of the state

        this->state[x] = this->state[y]; // swaps value of position x with position y in the state
        this->state[y] = t; // swaps value of position y with saved position x in the state
    }

    int findBlank(){ // finds position in 3x3 of blank tile
        for (int i=0; i<9; i++){
            if (state[i]==0) return i;
        }
    }

    vector<State> blankExpand(){
        int pos = this->findBlank();
        vector<State> vecStates;


        if (pos != 0 && pos != 1 && pos != 2){ // swaps the tile above it
            State newState = State(*this);
            newState.swap(pos,pos - 3);
            vecStates.push_back(newState);
        }

        if (pos != 6 && pos != 7 && pos != 8){ // swaps the tile above it
            State newState = State(*this);
            newState.swap(pos,pos + 3);
            vecStates.push_back(newState);
        }

        if (pos != 0 && pos != 3 && pos != 6){ // swaps the tile above it
            State newState = State(*this);
            newState.swap(pos,pos - 1);
            vecStates.push_back(newState);
        }

        if (pos != 2 && pos != 5 && pos != 8){ // swaps the tile above it
            State newState = State(*this);
            newState.swap(pos,pos + 1);
            vecStates.push_back(newState);
        }

        return vecStates;
    }
};

string breadthFirstSearch_with_VisitedList(string const initialState, string const goalState){
    string path;

  clock_t startTime;
  startTime = clock();

  deque<State> nodesToVisit;
  vector<State> visitedList;
  int maxQLength = 0;


  //Init
    State init(initialState);
    State goal(goalState);
    nodesToVisit.push_back(init);

    int count = 0;
    int numOfStateExpansions = 0 ;
//
    while (!nodesToVisit.empty()){
        if(maxQLength < nodesToVisit.size()){maxQLength = nodesToVisit.size();}

        State cur = nodesToVisit.front();
        nodesToVisit.pop_front();
         //remove front

        if (cur == goal){
            //solution found
            cout << "solved!";
            break;
        }

        //Get children
        vector<State> children = cur.blankExpand();

        numOfStateExpansions += children.size();        

        //For each child
        for (State& child : children) {
            for (int i = 0 ; i < 9;i++){
                cout << child.state[i];
            }
            cout << " child" << endl;

          //If already visited ignore
          if (std::find(visitedList.begin(), visitedList.end(), child) != visitedList.end()) {
            // cout << "duplicate" << endl;
            continue;
          }

          //If not in nodes to Visit
          else if (std::find(nodesToVisit.begin(), nodesToVisit.end(), child) == nodesToVisit.end()) {
            //Add child
            nodesToVisit.push_back(child);
          }
        }
        visitedList.push_back(cur);

    }



//***********************************************************************************************************
    clock_t actualRunningTime = ((float)(clock() - startTime)/CLOCKS_PER_SEC);  
    return path;    

}

int main(){
    breadthFirstSearch_with_VisitedList("042158367", "123804765");
    //042158367
}

// 0 4 2
// 1 5 8
// 3 6 7

1 个答案:

答案 0 :(得分:0)

您的代码中有许多不足之处会降低它的速度。我真的很惊讶你有耐心等待它达到记忆不足的状态。

主要罪魁祸首是搜索: std::find(visitedList.begin(), visitedList.end(), child) != visitedList.end() //and std::find(nodesToVisit.begin(), nodesToVisit.end(), child) == nodesToVisit.end()

这两个都在O(N)中执行,听起来不错,但是因为你在每个节点上执行它们,就会产生O(N 2 )。

您可以使用std::unordered_set<> visitedList来解决此问题。此外,您可以在排队时将节点添加到visited_list(而不是在将它们排队时)。这样,您只需要进行一次查找。

N.B。您必须专门设定std::hash<State>才能使用std::unordered_set

还有一个提示:主循环中的这些cout << ...会让你失望,因为它们会强制刷新并默认与操作系统同步,注释掉这些会使你的程序运行得更快。

实际上,您的代码中可以进行更多改进,但这是另一天的主题。修复算法的复杂性将把它带入不破坏的领域。