具有曼哈顿距离启发式的 A* 算法

2024-06-25

我一直在用 C 语言开发一个 15 个谜题求解器。我的代码使用的大量内存给我带来了一些问题。

我不会发布我的代码,因为它太长了......我已经实现了我正在使用的大部分库,它可能会给您带来困惑。让我们从基础开始。

我现在正在使用的东西是:(全部用C实现)

- 斐波那契堆:

/* Struct for the Fibonacci Heap */
typedef struct _fiboheap {
    int  size;           // Number of nodes in the heap
    node min;            // Pointer to the minimun element in the Heap
    funCompare compare;  // Function to compare within nodes in the heap
    freeFunction freeFun;// Function for freeing nodes in the heap
} *fiboheap;


/* Struct of a Fibonacci Heap Node */
typedef struct _node {
    node parent;    // Pointer to the node parent
    node child;     // Pointer to the child node
    node left;      // Pointer to the right sibling
    node right;     // Pointer to the left sibling
    int  degree;    // Degree of the node
    int  mark;      // Mark
    void *key;      // Value of the node (Here is the element)
} *node;

- 哈希表

我自己唯一没有实现的是,我正在使用uthash.

- 15 个谜题状态表示

这是一个有趣的话题。在解释这里的情况之前,让我们先思考一下 15-Puzzle 问题...众所周知,15-Puzzle 有 16 个图块(我们将空白图块视为数字 0 的图块)。那么,15 题问题有多少种可能的状态呢?嗯,这是阶乘(16)状态。所以,有很多州。这意味着我们希望我们的状态尽可能小......如果我们的初始状态离解决方案太远,我们可能会探索太多的状态,以至于我们的程序内存将会爆炸。

所以...我的 15 个谜题表示由使用位和逻辑运算符组成:

/* Struct for the 15-puzzle States Representation */
typedef struct _state {
    unsigned int quad_1; /* This int represent the first 8 numbers */
    unsigned int quad_2; /* This int represent the last 8 numbers */
    unsigned short zero; /* This is the position of the zero */
} *state;

所以我正在做的是使用逻辑运算符来移动和更改数字,使用最小的空间。

Note该结构的大小是12 bytes(因为它有三个整数)

- 曼哈顿距离

这就是众所周知的曼哈顿距离启发式。您基本上可以在其中计算每个数字当前位置到目标状态中的数字位置的距离总和。

- A* 实现和与 A* 一起使用的节点结构

让我们从节点开始。

typedef struct nodo_struct {
    nodo parent;        // Pointer to the parent of the node
    state estado;       // State associated with the node
    unsigned int g : 8; // Cost of the node
    action a;           // Action by which we arrived to this node
                        // If null, it means that this is the initial node
} *nodo;

Note这些节点与斐波那契堆中的节点无关。

现在这个主题的主要原因...我当前正在使用的 A* 的伪代码。

a_star(state initial_state) {
    q = new_fibo_heap; // Sorted by (Cost g) + (Manhattan Distance)
                       // It will have nodes which contain a pointer to the state
    q.push(make_root_node(initial_state));
    closed = new_hash_table();
    while (!q.empty()) {
        n = q.pop();
        if ((n->state ∉ closed) || (n->g < dist(n->state))) { 
        /* The dist used above is stored in the hash table as well */
            closed.insert(n->state);
            dist(n->state) = n->g;   // Update the hash table
            if (is_goal(n->state)) {
                return extract_solution(n); // Go through parents, return the path
            }
            for <a,s> ∈ successors(n->state) {
            // 'a' is the action, It can be 'l', 'r', 'u', 'd'
            // 's' is the state that is a successor of n
                if (manhattan(s) < Infinity) {
                    q.push(make_node(n,a,s));
                    // Assuming that manhattan is safe
                }
            }
        }
    }
    return NULL;
}

所以我还无法回答的问题是......

如何有效地管理内存?可以重用状态或节点吗?这会带来什么后果?

我的意思是,如果你看一下伪代码。它不考虑重用状态或节点。它只是不断地分配节点和状态,即使它们之前已经被计算过。

我一直在思考这个问题......每次运行求解器时,它都可以快速扩展数百万个节点。正如我们所知,当您找到另一条路径的成本低于前一条路径时,A* 可能会重新探索节点。这意味着...如果我们探索 100 万个节点,这将是 2400 万个字节(哇)...考虑到每个节点都有一个状态,每个节点将有 14 个字节,即 1400 万个字节。 。

最后,我需要的是一种重用/释放空间的方法,这样我的计算机在执行这个解算器时就不会崩溃。

(PD:很抱歉这篇文章很长)


你这样做是为了任务还是为了好玩?

如果是为了好玩,那就不要用A*,用IDA*。 IDA* 速度更快,而且几乎不使用内存。此外,您可以使用额外的内存来构建更好的启发式方法,并获得更好的性能。 (如果你搜索“模式数据库”,你应该找到足够的信息。这些是由 Jonathan Schaeffer 和 Joe Culberson 发明的,但由 Rich Korf 和 A​​riel Felner 进行了非常详细的研究。)

IDA* 有一些缺点,并且并不适用于每个领域,但它对于滑动瓷砖拼图来说几乎是完美的。

另一种可以提供帮助的算法是广度优先启发式搜索 http://icaps04.icaps-conference.org/icapspapers/ICAPS04ZhouR.pdf。这篇文章和其他文章讨论了如何避免完全存储关闭列表。

基本上,很多聪明人之前已经解决过这个问题,并且他们已经发布了他们的方法/结果,以便您可以向他们学习。

以下是一些提高 A* 的技巧:

  • 我发现斐波那契堆并没有太大的速度增益,因此您可能需要使用更简单的数据结构。 (尽管自从我上次尝试以来,可用的实现可能有所改进。)

  • 节点的 f 成本将以 2 为增量跳跃。因此,您可以对 f 成本进行存储,并且只关心在同一 f 成本层中对项目进行排序。 FIFO 队列实际上工作得很好。

  • 您可以使用其中的想法这张纸 http://webhome.cs.uvic.ca/~ruskey/Publications/RankPerm/MyrvoldRuskey.pdf将 15 个拼图表示转换为排列,完整表示大约需要 43 位。但是,扩展状态变得更加昂贵,因为您必须转换为不同的表示形式才能生成移动。

  • 避免完全使用禁止的运算符来存储关闭列表。 (参见之前的广度优先启发式搜索论文或这篇关于最佳优先前沿搜索 http://aaaipress.org/Papers/AAAI/2004/AAAI04-103.pdf更多细节。)

希望这些要点能够解决您的问题。如果您需要的话,我很乐意提供更多链接或说明。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

具有曼哈顿距离启发式的 A* 算法 的相关文章

随机推荐

  • 提交时出现 Apple Watch 图标问题

    我正在尝试向 App Store 提交一款 iOS 应用程序 支持新 Apple Watch 的应用程序 但我在所需的图标文件 名称和大小方面遇到了重大问题 我已阅读文档 https developer apple com library
  • 角度不安全 url

    我使用此指令将 jCrop 与 Angular 结合使用 http plnkr co edit Z2IQX8s9UK6wQ1hS4asz p preview http plnkr co edit Z2IQX8s9UK6wQ1hS4asz p
  • Microsoft Graph API 日历 API 空 SeriesMasterId

    解决方案如下 我的朋友们 这是一个漂亮的 msft 图形 api 调用 可以通过非空 SeriesMasterId 过滤前 50 个日历结果 另请记住 如果未定义此前 50 个过滤器 则返回的 json 仅显示前 10 个结果从您的日历中匹
  • 如何在MySQL查询结果中显示序号

    我有一些简单的查询 SELECT foo bar FROM table 我想你现在的结果是什么样的 我想要做的是根据查询结果中出现的数据数量来显示一些序列号 就像AUTO INCREMENT 这并不意味着我想出示身份证 我想要的结果是这样的
  • 使用迭代器遍历和取消遍历 std::vector 最干净的方法是什么?

    我遇到一种情况 我正在穿过一个向量 做一些事情 std vector
  • Blazor 应用程序随机出现“错误:电路无法初始化”

    我目前正在开发 Blazor 服务器端应用程序 在启动页面后立即遇到随机错误 当我按 F5 按钮或单击索引页面上的任何链接时 页面将重新加载 并且错误不会再次出现 错误消息非常笼统 我真的不知道从哪里开始调试 即使我完全删除 Index r
  • 让 VBScript 检查文件名中包含特定单词的文件,然后查找并删除该文件

    我想知道是否有一种方法可以使我的 vbs 脚本可以检查并删除名称中含有特定单词的任何文件 这是我到目前为止所拥有的 x MsgBox Searching for any infected files 64 Search DIM filesy
  • Laravel 5 多租户应用程序具有单独的数据库 - 用户可以访问多个安装

    在过去的几年里 我开发了一个非常定制的 PHP MySQL 应用程序 用于许多客户 到目前为止 我一直在为每个客户端创建一个新的数据库和新的安装 这里第一个明显的问题是让多个安装保持最新的任何代码更改 第二个问题是每次安装都有大量用户 对于
  • ImageButton 音板 android 应用程序

    我刚刚开始制作我的第一个音板 基本上这就是我到目前为止所拥有的 除了我有 40 个声音 有谁知道更好的方法来做到这一点 我必须去赴约 但我会在今天晚些时候回来回复 谢谢任何能提供帮助的人 音板 包 com soundboard app 导入
  • CSS - 将 div 与文本框右侧对齐

    div div style width 250px padding 20px 6px 6px 6px div div
  • 服务器时间不对

    我来自英国 在美国托管 我联系了我的主机并说我的服务器时间设置为比 GMT 慢 6 小时 他们说我需要在我的 CMS 中更改此设置 我该怎么做呢 每当我输入 now 时 我都会得到错误的时间 以前从未见过这个 有人可以提供任何建议吗 您可以
  • Haskell 中的 Monad 和 Purity

    我的问题是 Haskell 中的 monad 是否真正保持了 Haskell 的纯度 如果是的话 又是如何保持的 我经常读到副作用是如何不纯粹的 但有用的程序 例如 I O 需要副作用 下一句指出 Haskell 对此的解决方案是 mona
  • 存储 setInterval 的值

    如果我有这样的代码 count 0 count2 setInterval count 1000 count2 变量始终设置为 2 而不是 count 的实际值 因为它每秒都在增加 我的问题是 您甚至可以存储 seInterval 方法的值吗
  • DropDelegate Safari 拖动图像

    我正在尝试实施DropDelegate模式以允许将图像拖到我的视图中并加载它们 这对于取景器中的图像效果很好 但是当将图像从 safari 拖到我的视图中时 这不起作用 我注意到typeIdentifier or UTType所提供的信息
  • zip 样式 @repeat 嵌套形式

    repeat非常有用 然而 我遇到了嵌套表单的障碍 我需要制作一个比赛日程表 它有 2 个属性 日程数据 比赛日期 时间 地点 对手 和提交球队备注 例如 由于冬季风暴 1 月 7 日的比赛已移至1 月 9 日在 夏威夷 表单映射基于 ca
  • Eclipse DLTK:将向导添加到 ScriptExplorerPart 的“新建”菜单

    我正在尝试将向导条目添加到ScriptExplorerPartEclipse 的动态语言工具包 这些向导可以从File gt New gt Other 所以至少我知道它们有效 它们是使用扩展点添加的org eclipse ui newWiz
  • ocx_Oracle ORA-12541 tns 无侦听器

    我尝试通过cx Oracle连接到远程oracle服务器 db cx Oracle connect 用户名 密码 dsn tns 但它说数据库错误 ORA 12541 tns没有监听器 我能够通过数据库客户端 例如 datagrip 进行连
  • 如何在nodejs中处理xhr blob post

    客户端代码 var xhr new XMLHttpRequest xhr open POST frame true xhr send blob 服务器代码 app use bodyParser urlencoded extended fal
  • swagger文件默认属性的控制

    在 1 5 16 版本中使用 swagger core swagger annotations 控制我的数据模型的 swagger 文件中的默认属性时遇到问题 有一个定义 HTTP POST 输入 JSON 对象的 POJO import
  • 具有曼哈顿距离启发式的 A* 算法

    我一直在用 C 语言开发一个 15 个谜题求解器 我的代码使用的大量内存给我带来了一些问题 我不会发布我的代码 因为它太长了 我已经实现了我正在使用的大部分库 它可能会给您带来困惑 让我们从基础开始 我现在正在使用的东西是 全部用C实现 斐