【问题标题】:Optimizing Duplicate node search(Closed list,Open List) on N-Puzzle using A-star使用 A-star 优化 N-Puzzle 上的重复节点搜索(关闭列表、打开列表)
【发布时间】:2016-02-14 20:00:22
【问题描述】:

我编写了一个使用 A* 算法解决 N-Puzzle 的程序。该算法运行良好,但与使用相同算法解决相同问题的所有程序相比,它似乎慢得多。 我认为减慢我的代码的部分是检查新节点是否存在于打开和关闭的列表中。 本质上,我正在做的是检查特定节点的整个值数组,每个节点都存储在 Closed 和 open 列表中。 这是我认为导致速度变慢的代码 sn-p。

for(auto temp_Node:Neighbours(process))
        {
            auto pred = [temp_Node](Node* item)                         //lambda Expression for 'pred', custom comparator
            {
                bool value=true;
                for(int i=0;i<N;i++)
                    for(int j=0;j<N;j++)
                    {
                        if(item->Grid[i][j]!=temp_Node->Grid[i][j])
                        {
                            value=false;
                            break;
                        }
                    }
                if(item->g!=temp_Node->g)
                    value=false;
                return value;
            };
            if(find_if(begin(closed_list),end(closed_list), pred)==end(closed_list))
            {
                auto open_list_cpy=find_if(begin(open_list),end(open_list), pred);
                if(open_list_cpy==open_list.end())
                {
                    open_list.insert(temp_Node);
                }

如您所见,我使用带有 find_if 的 lambda 表达式来检查每个节点中的所有值。

我想知道我是否遗漏了什么,或者是否有任何其他更有效的方法来解决这个问题,或者这是应该如何完成的,而我的代码的其他部分有问题?

【问题讨论】:

  • 将封闭列表维护为一个集合,其中查找是 O(1) 而不是您的代码实现的 O(n)。
  • 封闭列表查找已使用下面的代码减少到 O(log(n)),如何减少到 O(1)?我不认为这是可能的,是吗!?
  • 任何基于哈希表的集合实现在合理假设下都有一个摊销的 O(1) 成员资格测试。参见例如cplusplus.com/reference/unordered_set/unordered_set
  • 所以在这种情况下,我必须找到一个重复的 NxN grid ,你是说把整个 Grid 作为 Key 吗?因为查找网格将是 O(1)。
  • @Gene 我尝试将我的closed_list 实现为unordered_set,但我无法为二维整数数组(Grid) 提供哈希函数。您能否建议一个哈希函数或将我引导至包含类似信息的页面?

标签: algorithm performance c++11 optimization a-star


【解决方案1】:

您可能希望使用网格保留mapset 的节点进行比较,而不是按顺序搜索所有节点:

struct GridLess {
    bool operator()(const Node *a,const Node *b) const
    {
        assert(a);
        assert(b);
        for(int i=0;i<N;i++)
        {
           for(int j=0;j<N;j++)
           {
               if(a->Grid[i][j]!=b->Grid[i][j])
               {
                   return a->Grid[i][j] < b->Grid[i][j];
               }
           }
        }
        return false;
    }
};


std::set<Node*,GridLess> closed_list;

现在你可以使用

if (closed_list.count(temp_Node)==0) {
    // No node in closed_list has the same grid as temp_node
}

这将时间复杂度从 O(n) 降低到 O(log(n))。

【讨论】:

  • 对不起,我没听懂,如果我错了,请纠正我,这里检查重复的标准是Grid的地址对吗?如果这是真的,它将不适用于temp_Node,因为它是由neighbor 函数生成的全新节点,因此即使Grid 值相同,它们的地址也将与Nodes 中相同的网格不同open_listclosed_list
  • @phoenixdd 我认为 Node::grid 是某种整数矩阵,而不是指针。 GridLess 可以随心所欲地定义,所以如果你需要间接指向一个指针,你可以这样做。
  • Node::Grid 是一个二维整数数组,正如您所想的那样,但我无法说明您的代码将如何检查 Neighbors 中的整数矩阵,无论它在关闭列表和打开列表中是否相同。如果有帮助,这是 Node 声明struct Node { int x0,y0,g,h,f; int Grid[N][N]; Node* parent=NULL; Node(int x=0,int y=0,int G=0,Node* node=NULL) { x0=x; y0=y; g=G; parent=node; } }
  • @PhoenixDD:我已经在我的答案中添加了如何与原始数组进行比较。请注意,如果您改用 std::array,则可以使用内置的比较运算符。
  • 这会比我的代码更快吗?由于使用closed_list.count(temp_Node)==0 将使用相同的for 循环来检查计数。代码的复杂度不是保持不变吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-09
  • 1970-01-01
  • 2015-05-17
  • 1970-01-01
  • 2012-08-15
  • 1970-01-01
相关资源
最近更新 更多