A *算法返回启发式值(JavaScript)

时间:2018-07-30 10:33:01

标签: javascript algorithm path-finding a-star

我为JavaScript创建了A *算法。 重要的是,它必须搜索最长的路径而不是最短的路径。当路径搜索完成时,我只想获取路径的深度/长度。该算法仅返回最大长度而不是路径。

调试结果时,我认为该算法仅返回启发式的值。

我有带有id的舞台对象和活动对象,它们通过持有 fromId toId 连接两个舞台。

示例:

我有状态对象的ID

  • 1,
  • 2,
  • 3,
  • 4,
  • 5

和活动

  • (1,2),
  • (1,3),
  • (2,3),
  • (3,5),
  • (5,1)

我希望得到这样的结果:

  • 距离阶段1:0
  • 距离阶段2:1
  • 距离阶段3:2
  • 距离阶段4:0(因为没有连接)
  • 距离阶段5:3

但是我得到的结果是

result

  

为什么我可以使用A *作为最长路径?

     

那么A *比较距离并取下一个。它应该是   通过更改compare语句可以以更高的成本获得更长的距离。

我在这里提供一个工作代码示例

// #########################################
// My initializer script with some fake data
// #########################################

$(document).ready(() => {
  const path = new Path(activities, 1);

  for (let i = 0; i < stages.length; i++) {
    const currentStage = stages[i];
    const currentStageId = currentStage.id;
    const currentDistance = path.getMaximumDistance(currentStage);
    console.log(`id: ${currentStageId} distance: ${currentDistance}`);
  }
});

const stages = [
  new Stage(1),
  new Stage(2),
  new Stage(3),
  new Stage(4),
  new Stage(5)
];

function Stage(id) {
  this.id = id;
}

const activities = [
  new Activity(1, 2),
  new Activity(1, 3),
  new Activity(2, 3),
  new Activity(3, 5),
  new Activity(5, 1)
];

function Activity(fromId, toId) {
  this.fromId = fromId;
  this.toId = toId;
}

// #########################################
// The A* class
// #########################################

function Path(activities, initialId) {

  this.getMaximumDistance = (targetStage) => {
    var me = this;
    var targetId = targetStage.id;
    var openList = [];
    var closedList = [];

    this.addNodeToList(openList, this.createNode(initialId, this.activities));

    let maxDistance = 0;

    while (openList.length > 0) {
      var currentNode = openList[this.getHighestCostIndex(openList)];

      if (currentNode.id === targetId) {
        maxDistance = this.getPathLength(currentNode);
        break;
      }

      this.removeNodeFromList(openList, currentNode);
      this.addNodeToList(closedList, currentNode);

      currentNode.neighbourIds.forEach(id => {
        var neighbour = me.createNode(id, me.activities);

        let lookUpTableElement;
        if (!me.listContainsNode(closedList, neighbour)) {
          var tempIncrementalCost = currentNode.incrementalCost + 1;

          if (me.listContainsNode(openList, neighbour)) {
            if (tempIncrementalCost < neighbour.incrementalCost) {
              neighbour.changeIncrementalCost(tempIncrementalCost);
            }
          } else {
            neighbour.changeIncrementalCost(tempIncrementalCost);
            me.addNodeToList(openList, neighbour);
          }

          for (var i = 0; i < me.lookUpTable.length; i++) {
            var currentLookUpTableElement = me.lookUpTable[i];
            if (currentLookUpTableElement.id === neighbour.id) {
              lookUpTableElement = currentLookUpTableElement;
            }
          }

          var heuristic = lookUpTableElement ? lookUpTableElement.cost : 0;
          neighbour.changeHeuristic(heuristic);
          neighbour.setParent(currentNode);
        }
      });
    }

    return maxDistance;
  };

  this.createNode = (id, activities) => {
    return new PathNode(id, activities);
  };

  this.getHighestCostIndex = (openList) => {
    let highestCostIndex = 0;

    for (let i = 0; i < openList.length; i++) {
      if (openList[i].totalCost > openList[highestCostIndex].totalCost) {
        highestCostIndex = i;
      }
    }

    return highestCostIndex;
  };

  this.addNodeToList = (list, node) => {
    list.push(node);
  };

  this.listContainsNode = (list, node) => {
    return list.some(x => x.id === node.id);
  };

  this.removeNodeFromList = (list, node) => {
    var index = list.indexOf(node);
    list.splice(index, 1);
  };

  this.getPathLength = (node) => {
    let currentNode = node;
    let counter = 0;

    while (currentNode.parent) {
      currentNode = currentNode.parent;
      counter++;
    }

    return counter;
  };

  this.activities = activities;
  this.lookUpTable = new LookUpTable(this.activities, initialId).table;
}

// #########################################
// Node class for the A*
// #########################################

function PathNode(id, activities) {

  this.getNeighbourIds = (activities) => {
    return activities
      .filter(x => x.fromId === id)
      .map(x => x.toId);
  };

  this.setParent = (parentNode) => {
    this.parent = parentNode;
  };

  this.changeHeuristic = (heuristic) => {
    this.heuristic = heuristic;
    this.updateTotalCost();
  };

  this.changeIncrementalCost = (incrementalCost) => {
    this.incrementalCost = incrementalCost;
    this.updateTotalCost();
  };

  this.updateTotalCost = () => {
    this.totalCost = this.incrementalCost + this.heuristic;
  };

  this.id = id;
  this.heuristic = 0;
  this.incrementalCost = 0;
  this.totalCost = 0;
  this.parent = null;
  this.neighbourIds = this.getNeighbourIds(activities);
}

// #########################################
// The heuristic code
// #########################################

function LookUpTable(activities, originId) {
  this.getLookUpTable = (originId) => {
    const me = this;
    const lookUpTable = [];
    let currentLevel = 0;
    let openList = [];

    this.addStage(openList, originId, currentLevel);

    while (openList.length > 0) {
      var lookUpTableElements = openList.filter(openNode => openNode.cost <= currentLevel);

      lookUpTableElements.forEach(lookUpTableElement => {
        this.addStage(lookUpTable, lookUpTableElement.id, currentLevel);

        var neighbours = activities.filter(targetActivity => targetActivity.fromId === lookUpTableElement.id);

        neighbours.forEach(neighbour => {
          var neighbourId = neighbour.toId === lookUpTableElement.id ? neighbour.fromId : neighbour.toId;

          if (!lookUpTable.some(x => x.id === neighbourId) && !openList.some(x => x.id === neighbourId)) {
            me.addStage(openList, neighbourId, currentLevel + 1);
          }
        });
      });

      openList = openList.filter(x => x.cost !== currentLevel);
      currentLevel++;
    }

    return lookUpTable;
  };

  this.addStage = (array, stageId, level) => {
    array.push({
      id: stageId,
      cost: level
    });
  };

  this.activities = activities;
  this.table = this.getLookUpTable(originId);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

我的A *算法有什么问题,它返回正确的启发式值而不是最大距离?

  

重要提示::如果有人对找到这些连接之间的最大距离有更好的主意,请告诉我!

1 个答案:

答案 0 :(得分:4)

通常,无法调整最短路径算法以返回给定图中的(简单)最长路径。

另外,找到一个简单的(非循环的)Dynamic Feature Manifest

在您的情况下,每条边的成本为1,不幸的是,这并没有改善。即使您知道可以到达所有顶点,也将最终搜索longest path is NP-hard,它是NP完全的。

我建议看看Hamilton Path中最长路径的问题。