[英]Optimizing C++ Tree Generation
我正在生成一个Tic-Tac-Toe游戏树(第一次移动后9秒钟),并且被告知只需要几毫秒的时间。 因此,我正在尝试对其进行优化,并通过CodeAnalyst进行了测试,这是进行的前5个调用(我使用位集表示Tic-Tac-Toe板):
std::_Iterator_base::_Orphan_me
std::bitset<9>::test
std::_Iterator_base::_Adopt
std::bitset<9>::reference::operator bool
std::_Iterator_base::~_Iterator_base
void BuildTreeToDepth(Node &nNode, const int& nextPlayer, int depth)
{
if (depth > 0)
{
//Calculate gameboard states
int evalBoard = nNode.m_board.CalculateBoardState();
bool isFinished = nNode.m_board.isFinished();
if (isFinished || (nNode.m_board.isWinner() > 0))
{
nNode.m_winCount = evalBoard;
}
else
{
Ticboard tBoard = nNode.m_board;
do
{
int validMove = tBoard.FirstValidMove();
if (validMove != -1)
{
Node f;
Ticboard tempBoard = nNode.m_board;
tempBoard.Move(validMove, nextPlayer);
tBoard.Move(validMove, nextPlayer);
f.m_board = tempBoard;
f.m_winCount = 0;
f.m_Move = validMove;
int currPlay = (nextPlayer == 1 ? 2 : 1);
BuildTreeToDepth(f,currPlay, depth - 1);
nNode.m_winCount += f.m_board.CalculateBoardState();
nNode.m_branches.push_back(f);
}
else
{
break;
}
}while(true);
}
}
}
我应该在哪里进行优化? 我应该如何优化这5个调用(我无法识别它们=。
井字游戏树非常多余。 消除旋转的和镜像的板将使游戏树的最终层数减少3或4个数量级。 没有任何数量的优化可以使Bubblesort像introsort一样快。
struct Game_board;
struct Node
{
Game_board game_board;
Node* parent;
std::vector<Node*> children;
enum { X_Win, Y_Win, Draw, Playing } outcome;
};
// returns the same hash value for all "identical" boards.
// ie boards that can be rotated or mirrored to look the
// same will have the same hash value
int hash( const Game_board& game_board );
// uses hash() function to generate hashes from Node*
struct Hash_functor;
// nodes yet to be explored.
std::hash_set<Node*,Hash_functor> open;
//nodes already explored.
std::hash_set<Node*,Hash_functor> closed;
while( ! open.empty() )
{
Node* node_to_expore = get_a_node( open );
assert( node_to_expore not in close or open sets )
if( node_to_expore is win lose or draw )
{
Mark node as win lose or draw
add node to closed set
}
loop through all children of node_to_expore
{
if( child in close )
{
add node from closed set to children list of node_to_expore
}
else if( child in open )
{
add node from open set to children list of node_to_explore
}
else
{
add child to open set
add child to children list of node_to_expore
}
}
}
这些功能通常是微不足道的。 这意味着优化(“发行”)版本通常会将它们内联。 但是,在调试版本中不是。 结果是调试构建的速度较慢,但允许您在这些函数上设置断点。 因此,“毫秒注释”应该应用于发布版本,在该版本中您将不再拥有这些功能。
您将全部包裹在数据结构中。 不建树,只走它。 仅拥有一份董事会副本。 在搜索树的每个节点上,只需修改电路板,然后在退回时取消对其进行修改。
而且,如果您想知道它在做什么,只需随机点击暂停按钮即可。 它会向您显示为什么在所有您不认识的例程中总是花时间。
坦白地说,这并不是要对您构成抨击,您是在要求我们检查文档记录不良的一段代码,而该代码段是较大代码库的一小部分。 我们没有提供太多信息的上下文。 当其他人似乎还没有完成他们自己可以做的所有事情来检查自己的代码时,我个人也被关闭(我并不是说我对您或其他任何事情感到恼火,只是我的眼睛在注视着您的代码)。
我建议您通过探查器运行代码,然后确定花费了很多时间的确切时间。 像调试一样对待此配置文件。 当您发现一个需要很长时间的模块时,请分小节检查该模块(就像您正在寻找错误一样)以了解原因。
如果您仍然需要问一些问题,这将使您提出一个更加明智的问题。
让我补充一下,如果您的系统花费9秒钟来完成工作,那么这意味着被称为数十亿倍的东西。 如果您没有发布级别的分析功能,则在代码中放置一些全局计数器,并在每次调用它们所在的代码时对其进行递增。 这将为您提供一个穷人的档案,该档案可用于发行版本。 如果您在意想不到的地方看到数十亿电话,那么您现在可以放心看看。
实际上,如果需要速度,井字游戏的整个移动树应该很容易存储为散列。 9! 动作不再是一个大领域(不再)。 36.2万的举动不应该破坏资金,这是蛮力分析。 当您考虑所有各种数据形式时,可以将该域削减。
ah,这是我编写代码的方式,因为人们已经将我锁定在信封数学的背面:
我什至不会走树路线,只需遵守一些规则即可。
转1.进入中心。
转2。如果中心没空,去那儿,否则去角落
转3。如果对手在一个角上填充,则在对面的角走,否则在角上-您赢了。
转动4.如果需要,阻止。 如果X占据对角,则填充边缘。 如果X占据中心和对角,则填充角。 如果X占据相反的边缘,则填充角并获胜。 如果X填充相邻的边,请在它们之间填充角。
如果可能,转5赢。 如果需要,阻止。 否则,在相邻边缘的对角移动并获胜。
如果可能,转6-9赢。 如果需要,阻止。 否则,随机抽奖。
您发布的代码太少了。
您正在询问如何优化代码,但是您也应该询问如何优化算法。
我立即看到两件事。
正如“迈克尔·多根(Michael Dorgan)”所说的那样,产生一次动作树。
您在树中生成了多少个广泛对象? 362880? 您的代码似乎正在生成冗余条目。 例如,对于一个空板,实际上有3个动作而不是9个动作。 所有其他组合都是旋转的板(相等)。 您可以减少所需生成的板数,并加快生成树的速度。
这是前三个动作(旋转最后两个板以生成其他板)
| | |X| | | |X| | | | | X| | | | | |
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.