首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >解决8块滑动拼图

解决8块滑动拼图
EN

Code Review用户
提问于 2018-08-09 17:09:56
回答 1查看 8.9K关注 0票数 9

为了解决8难题,我创建了一个BFS算法,虽然它可以工作,但与我的DFS实现相比,它非常慢。我主要关注的是puzzleExists()函数,它决定创建的谜题是否已经存在于列表中,因此应该删除。目前,该算法的速度明显减慢,因为它必须检查所有以前的谜题复制。

关于如何推广这一点的建议将不胜感激。

为了清楚起见,省去了外勤部的代码和打印:

代码语言:javascript
运行
复制
#include <iostream>
#include <vector>
#include <chrono>

using namespace std;
using namespace std::chrono;

const int PUZZLE_LENGTH = 9;

const int EMPTY = 0;

struct Puzzle
{
    int state[PUZZLE_LENGTH];
    int gapLocation;
};

struct breadthPuzzle
{
    Puzzle puzzle;
    int parent;
};

const Puzzle SOLVED_PUZZLE = { { 1,2,3,4,5,6,7,8,EMPTY } };

// Check if the provided puzzle is actually solvable or not
// Credit for formula to: http://www.cs.bham.ac.uk/~mdr/teaching/modules04/java2/TilesSolvability.html
bool isSolvable(const Puzzle& puz)
{
    int inversions = 0;

    for ( int i = 0; i < PUZZLE_LENGTH; i++ )
        for ( int j = i + 1; j < PUZZLE_LENGTH; j++ )
            if ( puz.state[j] > puz.state[i] && puz.state[i] != EMPTY && puz.state[j] != EMPTY )
                inversions++;

    // If the amount of inversions is even the puzzle can be solved
    return inversions % 2 == 0;

}

// Checks if the 2 provided puzzles are the same
bool puzzleTheSame(const Puzzle& puz1, const Puzzle& puz2)
{
    for ( int length = 0; length < PUZZLE_LENGTH; length++ )
        if ( puz1.state[length] != puz2.state[length] ) return false;
    return true;
}

bool puzzleExists(const Puzzle& currentPuzzle, vector<breadthPuzzle>& currentRoute)
{
    for ( int i = 0; i < currentRoute.size(); i++ )
        if ( puzzleTheSame(currentRoute[i].puzzle, currentPuzzle) )
            return true;
    return false;
}

// Checks if the provided puzzle is solved
bool isSolved(const Puzzle& solution)
{
    return puzzleTheSame(SOLVED_PUZZLE, solution);
}

bool canNorth(int gapLocation)
{
    return gapLocation > 2;
}

bool canEast(int gapLocation)
{
    return (gapLocation != 2 && gapLocation != 5 && gapLocation != 8);
}

bool canSouth(int gapLocation)
{
    return gapLocation < 6;
}

bool canWest(int gapLocation)
{
    return (gapLocation != 0 && gapLocation != 3 && gapLocation != 6);
}

int north(int gap)
{
    return gap - 3;
}

int east(int gap)
{
    return gap + 1;
}

int south(int gap)
{
    return gap + 3;
}

int west(int gap)
{
    return gap - 1;
}

Puzzle createNextPuzzle(Puzzle currentPuzzle, int pos)
{
    int temp = currentPuzzle.state[pos];
    currentPuzzle.state[currentPuzzle.gapLocation] = temp;
    currentPuzzle.state[pos] = EMPTY;
    currentPuzzle.gapLocation = pos;
    return currentPuzzle;
}

void solvePuzzle(vector<breadthPuzzle>& currentRoute, int i, int futurePos)
{
    Puzzle currentPuzzle = createNextPuzzle(currentRoute[i].puzzle, futurePos);

    if ( !puzzleExists(currentPuzzle, currentRoute) )
    {
        breadthPuzzle candidate{ currentPuzzle, i };
        currentRoute.push_back(candidate);
    }
}

void breadthFirst(Puzzle initalPuzzle)
{
    // Origin has no parent, thus -1 as start.
    vector<breadthPuzzle> breadthVector = { { initalPuzzle, -1 } };
    int i = 0;

    while ( i < breadthVector.size() && !isSolved(breadthVector[i].puzzle) )
    {
        Puzzle currentPuzzle = breadthVector[i].puzzle;
        int gapLocation = currentPuzzle.gapLocation;

        if ( canNorth(gapLocation) ) solvePuzzle(breadthVector, i, north(gapLocation));
        if ( canEast(gapLocation) ) solvePuzzle(breadthVector, i, east(gapLocation));
        if ( canSouth(gapLocation) ) solvePuzzle(breadthVector, i, south(gapLocation));
        if ( canWest(gapLocation) ) solvePuzzle(breadthVector, i, west(gapLocation));
        i++;
    }
}

int main()
{
    Puzzle toSolve = { {1,2,3,4,6,5,8,7,EMPTY}, 8 };
    //Breadth-First: Duration in seconds = 72;
    //Depth-First: Duration in seconds = 15;

    high_resolution_clock::time_point t1 = high_resolution_clock::now();

    if ( isSolvable(toSolve) )
    {
        cout << "Puzzle Solvable\n";
        breadthFirst(toSolve);
    }
    else
        cout << "Puzzle insolvable, stopping\n";

    high_resolution_clock::time_point t2 = high_resolution_clock::now();
    auto durationSec = duration_cast<seconds>(t2 - t1).count();

    cout << "Duration in seconds of function: " << durationSec << endl;
}
EN

回答 1

Code Review用户

回答已采纳

发布于 2018-08-09 18:24:45

下面是您想要加速的代码:

代码语言:javascript
运行
复制
// Checks if the 2 provided puzzles are the same
bool puzzleTheSame(const Puzzle& puz1, const Puzzle& puz2)
{
    for ( int length = 0; length < PUZZLE_LENGTH; length++ )
        if ( puz1.state[length] != puz2.state[length] ) return false;
    return true;
}

bool puzzleExists(const Puzzle& currentPuzzle, vector<breadthPuzzle>& currentRoute)
{
    for ( int i = 0; i < currentRoute.size(); i++ )
        if ( puzzleTheSame(currentRoute[i].puzzle, currentPuzzle) )
            return true;
    return false;
}

// Checks if the provided puzzle is solved
bool isSolved(const Puzzle& solution)
{
    return puzzleTheSame(SOLVED_PUZZLE, solution);
}

这是对数组currentRouteO(n)比较的线性搜索,其中n是当前路由的长度。

出于文体上的原因,让我们使用C++语法来表示“同一性”,而不是使用英语单词TheSame。因为每个人都知道==在C++中的含义,所以我们不再需要解释puzzleTheSame的代码注释。

代码语言:javascript
运行
复制
bool operator==(const Puzzle& puz1, const Puzzle& puz2)
{
    return std::equal(std::begin(puz1.state), std::end(puz1.state),
                      std::begin(puz2.state), std::end(puz2.state));
}

bool puzzleExists(const Puzzle& currentPuzzle,
                  const std::vector<breadthPuzzle>& currentRoute)
{
    for (auto&& elt : currentRoute) {
        if (elt.puzzle == currentPuzzle) {
            return true;
        }
    }
    return false;
}

我们也可以在它使用的任何地方内联这个下一个助手函数;为它命名isSolved不再有帮助了。顺便说一下,参数的名称是误导的:参数是puzzle,但它不一定是solution。函数的全部目的是确定它是否是一个solution

代码语言:javascript
运行
复制
bool isSolved(const Puzzle& puzzle)
{
    return puzzle == SOLVED_PUZZLE;
}

好吧,那我们该怎么加速呢?我们摆脱了线性搜索,而倾向于二进制搜索!

代码语言:javascript
运行
复制
bool operator<(const Puzzle& puz1, const Puzzle& puz2)
{
    return std::lexicographical_compare(
        std::begin(puz1.state), std::end(puz1.state),
        std::begin(puz2.state), std::end(puz2.state));
}

bool puzzleExistsInSet(const Puzzle& puzzle,
                       const std::set<Puzzle>& seenPuzzles)
{
    return seenPuzzles.find(puzzle) != seenPuzzles.end();
}

注意,这意味着我们需要存储两次所见的谜题--一次是按照我们遍历它们的顺序,在currentRoute中,另一次是按排序顺序,在seenPuzzles中。因此,这改善了我们的大-O渐近运行时间,但它可能会增加我们的“常数因素”,以消除这一增益。

说到命名,我注意到您的solvePuzzle函数完全被错误地命名了--它在任何意义上都不能“解决”一个难题。适当的名称可能类似于slideBlockmakeMove,或者只是将其内联到其调用方中。

但它的一个来电者叫它四次!我们能修好吗?

代码语言:javascript
运行
复制
    if ( canNorth(gapLocation) ) solvePuzzle(breadthVector, i, north(gapLocation));
    if ( canEast(gapLocation) ) solvePuzzle(breadthVector, i, east(gapLocation));
    if ( canSouth(gapLocation) ) solvePuzzle(breadthVector, i, south(gapLocation));
    if ( canWest(gapLocation) ) solvePuzzle(breadthVector, i, west(gapLocation));

是。这将触发新的重构,从而减少其他地方的重复代码:

代码语言:javascript
运行
复制
for (auto&& m : { north(gapLoc), south(gapLoc), east(gapLoc), west(gapLoc) }) {
    if (isLegalMove(m)) {
        makeMove(breadthVector, i, m);
    }
}

继续以这种迭代的方式进行:寻找异常;考虑如何让代码看起来像;然后重构,直到它看起来是那样的。冲洗并重复。

票数 9
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/201308

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档