为了解决8难题,我创建了一个BFS算法,虽然它可以工作,但与我的DFS实现相比,它非常慢。我主要关注的是puzzleExists()函数,它决定创建的谜题是否已经存在于列表中,因此应该删除。目前,该算法的速度明显减慢,因为它必须检查所有以前的谜题复制。
关于如何推广这一点的建议将不胜感激。
为了清楚起见,省去了外勤部的代码和打印:
#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;
}发布于 2018-08-09 18:24:45
下面是您想要加速的代码:
// 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);
}这是对数组currentRoute:O(n)比较的线性搜索,其中n是当前路由的长度。
出于文体上的原因,让我们使用C++语法来表示“同一性”,而不是使用英语单词TheSame。因为每个人都知道==在C++中的含义,所以我们不再需要解释puzzleTheSame的代码注释。
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!
bool isSolved(const Puzzle& puzzle)
{
return puzzle == SOLVED_PUZZLE;
}好吧,那我们该怎么加速呢?我们摆脱了线性搜索,而倾向于二进制搜索!
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函数完全被错误地命名了--它在任何意义上都不能“解决”一个难题。适当的名称可能类似于slideBlock或makeMove,或者只是将其内联到其调用方中。
但它的一个来电者叫它四次!我们能修好吗?
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));是。这将触发新的重构,从而减少其他地方的重复代码:
for (auto&& m : { north(gapLoc), south(gapLoc), east(gapLoc), west(gapLoc) }) {
if (isLegalMove(m)) {
makeMove(breadthVector, i, m);
}
}继续以这种迭代的方式进行:寻找异常;考虑如何让代码看起来像;然后重构,直到它看起来是那样的。冲洗并重复。
https://codereview.stackexchange.com/questions/201308
复制相似问题