【问题标题】:Hamiltonian path哈密​​顿路径
【发布时间】:2019-10-01 14:27:10
【问题描述】:

我要实现的是使用backtracking algorithmHamiltonian path finder。

注意: 虽然我设法找到的大多数数学论文和 SO 主题要么与回答“是否哈密顿 路径/循环'存在或致力于寻找哈密顿循环,我的问题 略有不同 - 我需要找出顶点的顺序, 哈密​​顿量路径经过

对于示例图(未使用的边为黑色,顶点索引为从 0 开始):

我已经建立了以下表示的邻接矩阵(顶点索引从 1 开始):

const vertexes = [ { vertex: 1, peers: [ 3, 8, 15 ] },
  { vertex: 2, peers: [ 7, 14, 23 ] },
  { vertex: 3, peers: [ 1, 6, 13, 22 ] },
  { vertex: 4, peers: [ 5, 12, 21 ] },
  { vertex: 5, peers: [ 4, 11, 20 ] },
  { vertex: 6, peers: [ 3, 10, 19 ] },
  { vertex: 7, peers: [ 2, 9, 18 ] },
  { vertex: 8, peers: [ 1, 17 ] },
  { vertex: 9, peers: [ 7, 16 ] },
  { vertex: 10, peers: [ 6, 15 ] },
  { vertex: 11, peers: [ 5, 14 ] },
  { vertex: 12, peers: [ 4, 13 ] },
  { vertex: 13, peers: [ 3, 12, 23 ] },
  { vertex: 14, peers: [ 2, 11, 22 ] },
  { vertex: 15, peers: [ 1, 10, 21 ] },
  { vertex: 16, peers: [ 9, 20 ] },
  { vertex: 17, peers: [ 8, 19 ] },
  { vertex: 18, peers: [ 7 ] },
  { vertex: 19, peers: [ 6, 17 ] },
  { vertex: 20, peers: [ 5, 16 ] },
  { vertex: 21, peers: [ 4, 15 ] },
  { vertex: 22, peers: [ 3, 14 ] },
  { vertex: 23, peers: [ 2, 13 ] } ]

接下来,从作为根的顶点 18 和包含单个路径 [[18]]paths 数组开始,我尝试改变该数组,将其替换为包含原始路由(如果不是死路)的临时副本向前走,直到没有找到长度为n(顶点总数)的潜在路线或路径:

while(paths.length>0){
    let tempPath = [];
    for(path of paths){
     const nextSteps = vertexes.find(({vertex}) => vertex == path[path.length-1]).peers.filter(v => !path.includes(v));
     if(!nextSteps.length) continue;
     else if(path.length == n-1) return [...path, nextSteps[0]];
     else nextSteps.forEach(step => tempPath.push([...path,step]));
    }
    paths = tempPath;
}

所以,问题是上面的代码永远不会退出循环,也没有返回所需的输出 ([18, 7, 9, 16, 20, 5, 11, 14, 22, 3, 1, 8, 17, 19, 6, 10, 15, 21, 4, 12, 13, 23, 2])。

您对上述代码为何失败以及如何修复它以返回预期输出的想法将不胜感激。

编辑:感谢@DavidSampson 和@trincot 指出我的拼写错误,我的代码现在可以工作了:

while(paths.length>0){
    let tempPath = [];
    for(let path of paths){
     const nextSteps = vertexes.find(({vertex}) => vertex == path[path.length-1]).peers.filter(v => !path.includes(v));
     if(!nextSteps.length) continue;
     else if(path.length == n-1) return [...path, nextSteps[0]];
     else tempPath.push(...nextSteps.map(v => [...path,v]));
    }
    paths = tempPath;
}

现在性能是我最关心的问题,有什么建议可以提高吗?

【问题讨论】:

  • while(paths.length>0){ 在 while 循环中,您实际上没有更改 paths 的值。由于它以其中的一个元素 ([18]) 开始,并且您永远不会更改它,因此 paths.length > 0 将始终为真,因此循环将永远运行。
  • 为了提高性能,你真的应该考虑一种不同的算法。我建议使用动态编程。它适用于哈密顿路径和哈密顿循环。参见例如codeforces.com/blog/entry/337

标签: javascript arrays algorithm graph


【解决方案1】:

主要问题是拼写错误,如果您使用严格模式,则可以避免:

变化:

path = tempPath;

到:

paths = tempPath;

同时声明你的变量。喜欢这里:

for (let path of paths) {
//   ^^^^

然后它对我有用:

"use strict";

function hamiltonian(vertexes, start) {
    let n = vertexes.length;
    let paths = [[start]];  
    while(paths.length>0) {
        let tempPath = [];
        for(let path of paths){
            const nextSteps = vertexes.find(({vertex}) => vertex == path[path.length-1]).peers.filter(v => !path.includes(v));
            if(!nextSteps.length) continue;
            else if(path.length == n-1) return [...path, nextSteps[0]];
            else nextSteps.forEach(step => tempPath.push([...path,step]));
        }
        paths = tempPath;
    }
}

const vertexes = [ { vertex: 1, peers: [ 3, 8, 15 ] },{ vertex: 2, peers: [ 7, 14, 23 ] },{ vertex: 3, peers: [ 1, 6, 13, 22 ] },{ vertex: 4, peers: [ 5, 12, 21 ] },{ vertex: 5, peers: [ 4, 11, 20 ] },{ vertex: 6, peers: [ 3, 10, 19 ] },{ vertex: 7, peers: [ 2, 9, 18 ] },{ vertex: 8, peers: [ 1, 17 ] },{ vertex: 9, peers: [ 7, 16 ] },{ vertex: 10, peers: [ 6, 15 ] },{ vertex: 11, peers: [ 5, 14 ] },{ vertex: 12, peers: [ 4, 13 ] },{ vertex: 13, peers: [ 3, 12, 23 ] }, { vertex: 14, peers: [ 2, 11, 22 ] },{ vertex: 15, peers: [ 1, 10, 21 ] },{ vertex: 16, peers: [ 9, 20 ] },{ vertex: 17, peers: [ 8, 19 ] },{ vertex: 18, peers: [ 7 ] },{ vertex: 19, peers: [ 6, 17 ] },{ vertex: 20, peers: [ 5, 16 ] },{ vertex: 21, peers: [ 4, 15 ] },{ vertex: 22, peers: [ 3, 14 ] },{ vertex: 23, peers: [ 2, 13 ] } ];

let result = hamiltonian(vertexes, 18);
console.log('result', result);

请注意,使用动态编程可以提高运行时间。查看Hamiltonian path problem 上的不同方法。

【讨论】:

  • 修正了错字path->paths,我设法让我的代码也能正常工作(有一些小的更正)。而现在,'running time' 正是我的问题。能否请您详细说明一下with dynamic programming you could improve the running time?由于回溯算法是我目前唯一能够实现(理解)的算法。
  • 动态编程(你可以查一下)意味着你可以记住之前的结果。你仍然像现在一样搜索,但是当你能够使用记忆的信息时,你会更早地回溯。维基百科链接准确地解释了需要记忆的内容,以及在搜索过程中何时使用它。我想说:试一试,尝试实施它,如果您遇到一个关于此的特定问题,请将其作为新问题提交。确保它是具体的,并提供您尝试实现动态编程的代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-20
  • 2011-03-17
  • 2011-11-12
  • 1970-01-01
  • 2023-03-08
  • 2023-04-10
相关资源
最近更新 更多