简体   繁体   English

查找图中节点之间的最短路径

[英]Finding the shortest path between nodes in graph

I have a state machine that looks like this:我有一台 state 机器,看起来像这样:

           G--H
          /
A--B--C--D--E--F

I want to have a function, goToState(target) that has as an input argument the target state, and then the function will execute all the transitions starting from the current state until it reaches the target state. I want to have a function, goToState(target) that has as an input argument the target state, and then the function will execute all the transitions starting from the current state until it reaches the target state.

For example, let's say that the current state is B and we call goToState(F).例如,假设当前 state 为 B,我们调用 goToState(F)。 Then the function will do the following state transitions B->C, C->D, D->E, E->F.然后 function 将执行以下 state 转换 B->C、C->D、D->E、E->F。

The transitions work both ways so if current state is F and we call goToState(G), then the function will do F->E, E->D, D->G.转换是双向的,所以如果当前 state 为 F,我们调用 goToState(G),那么 function 将执行 F->E、E->D、D->G。

If we had a linear state machine (eg, no branch G--H), then I would just do an array of functions for each transition, in the legal order, and then find the index for current state and the index for the target state and call all the transition function in between those two indexes in a for loop.如果我们有一个线性 state 机器(例如,没有分支 G--H),那么我只需按照合法顺序为每个转换执行一组函数,然后找到当前 state 的索引和目标的索引state 并在 for 循环中调用这两个索引之间的所有转换 function。

However now that I have a branch, this method would not work.但是现在我有一个分支,这种方法行不通。 What would be the most efficient way to encode the legal transitions and to implement a function that executes them in the right order based on the target state in C?什么是编码合法转换和实现 function 的最有效方法,该方法根据 C 中的目标 state 以正确的顺序执行它们?

EDIT: As some other users very well pointed out, what I'm looking for is some sort of path finding algorithm that finds the shortest path between two states.编辑:正如其他一些用户很好地指出的那样,我正在寻找的是某种寻路算法,它可以找到两个状态之间的最短路径。 I just couldn't find the right words to formulate it properly in the original post.我只是在原始帖子中找不到合适的词来正确表述它。 I need the simplest path finding algorithm that would work for a state diagram as shown above.我需要最简单的寻路算法,该算法适用于如上所示的 state 图。 The state diagram will never get more complex than this so the algorithm doesn't need to cover any other scenarios either. state 图永远不会比这更复杂,因此该算法也不需要涵盖任何其他场景。

EDIT2: Updated the title to better describe the problem. EDIT2:更新了标题以更好地描述问题。 Your comments helped me find the right terminology so I can search the web for the solution.您的评论帮助我找到了正确的术语,因此我可以在 web 中搜索解决方案。

I found the answer on GeeksforGeeks website: https://www.geeksforgeeks.org/shortest-path-unweighted-graph/我在 GeeksforGeeks 网站上找到了答案: https://www.geeksforgeeks.org/shortest-path-unweighted-graph/

It's a modified version of the Breadth-First Search algorithm and does exactly what I need!它是广度优先搜索算法的修改版本,完全符合我的需要!

Thank you all for your answers and comments.谢谢大家的回答和评论。 They helped me find the right terminology to search for the right solution online.他们帮助我找到了正确的术语来在线搜索正确的解决方案。

You can model the states as an array of structs, each of which contains a function pointer for the transition and an array of possible destination states.您可以将状态 model 作为结构数组,每个结构都包含一个用于转换的 function 指针和一个可能的目标状态数组。

Then, create a function that takes the current and destination state and have it loop through the list of possible states.然后,创建一个 function 以获取当前和目标 state 并让它遍历可能的状态列表。 If one matches the destination, put that at the start of an empty list of states and return the list.如果一个匹配目的地,把它放在一个空的状态列表的开头并返回列表。 If not, recurse through each possible intermediate state until either one returns a nonempty list and add the current state to the front of the list.如果不是,则递归每个可能的中间 state 直到其中一个返回非空列表并将当前 state 添加到列表的前面。

After the recursive function returns, you can iterate through the list running the transitions.在递归 function 返回后,您可以遍历运行转换的列表。

#include <stdio.h>
#include <stdlib.h>

typedef void (*func)(void);    // modify as needed
typedef enum { NONE=-1, A, B, C, D, E, F, G, H, MAX_STATES } states;

struct transitions {
    func transition;
    states slist[MAX_STATES];
};

struct tlist {
    struct transitions *trans;
    struct tlist *next;
};

void trans_a(void) { printf("transition A\n"); }
void trans_b(void) { printf("transition B\n"); }
void trans_c(void) { printf("transition C\n"); }
void trans_d(void) { printf("transition D\n"); }
void trans_e(void) { printf("transition E\n"); }
void trans_f(void) { printf("transition F\n"); }
void trans_g(void) { printf("transition G\n"); }
void trans_h(void) { printf("transition H\n"); }

struct transitions transitions[] = {
    { trans_a, { B, NONE } },
    { trans_b, { A, C, NONE } },
    { trans_c, { B, D, NONE } },
    { trans_d, { C, G, E, NONE } },
    { trans_e, { D, F, NONE } },
    { trans_f, { E, NONE } },
    { trans_g, { D, H, NONE } },
    { trans_h, { G, NONE } }
};

struct tlist *getStates(states prev, states start, states end)
{
    int i;
    for (i = 0; transitions[start].slist[i] != NONE; i++) {
        if (transitions[start].slist[i] == prev) continue;
        if (transitions[start].slist[i] == end) {
            struct tlist *entry = malloc(sizeof *entry);
            entry->trans = transitions + start;
            entry->next = NULL;
            return entry;
        }
        struct tlist *list = getStates(start, transitions[start].slist[i], end);
        if (list) {
            struct tlist *entry = malloc(sizeof *entry);
            entry->trans = transitions + start;
            entry->next = list;
            return entry;
        }
    }
    return NULL;
}

void runStates(states start, states end)
{
    printf("from %d to %d\n", start, end);
    struct tlist *list = getStates(NONE,start,end);
    while (list) {
        struct tlist *tmp = list;
        list->trans->transition();
        list = list->next;
        free(tmp);
    }
    printf("\n");
}

int main()
{
    runStates(A,H);
    runStates(A,E);
    runStates(E,A);
    runStates(F,H);
    return 0;
}

Output: Output:

from 0 to 7
transition A
transition B
transition C
transition D
transition G

from 0 to 4
transition A
transition B
transition C
transition D

from 4 to 0
transition E
transition D
transition C
transition B

from 5 to 7
transition F
transition E
transition D
transition G

I think the answers already posted are adequate to answer the question, but I want to add that this is a classical problem that is studied in the abstract mathematical field of theoretical computations.我认为已经发布的答案足以回答这个问题,但我想补充一点,这是一个在理论计算的抽象数学领域研究的经典问题。 The mathematical model of computation that describes what you're asking for is called a Finite State Machine.描述您所要求的计算的数学 model 称为有限 State 机器。 An FSM is an abstract machine that contains (without going to deep into math jargon or notation): FSM 是一种抽象机器,它包含(无需深入研究数学术语或符号):

  1. An input language , which in your case is whatever you pass to the function to find out which state your program should transition to ("target" in my example)一种输入语言,在您的情况下是您传递给 function 以找出您的程序应该转换到哪个state的任何内容(在我的示例中为“目标”)
  2. A set of states , which in your case is A, B, C, D, E, F, G, and H一组状态,在您的情况下是 A、B、C、D、E、F、G 和 H
  3. An initial state , which is whichever of the set of states above that your program starts in一个初始 state ,它是您的程序开始的上述状态集中的任何一个
  4. A transition function that tells each state where to go from any given state given any input in the input language (the function "transtition" in my example) A transition function that tells each state where to go from any given state given any input in the input language (the function "transtition" in my example)
  5. A set of final states which is either empty or a subset of your set of states一组最终状态,要么是空的,要么是你的状态集的子集

The crux of your question is about item 4, the transition function .您问题的症结在于第 4 项,即过渡 function A C implementation of a transition function for an FSM that may fit your needs may look something like this:一个 C 转换 function 的实现可能适合您的 FSM 可能看起来像这样:

//Model states as integers from 0-7 plus an extra invalid state for error handling
enum state{A, B, C, D, E, F, G, H, invalid};

enum state transition(enum state current, enum state target){
  
  if(target < A || target > H || current < A || current > H)
    return invalid;
  else if(target == current)
    return current;

  switch(current){
    case A:
        return B;
    case B: //B and C have combinable transition behavior
    case C:
      if(target > current)
        return current + 1;
      else
        return current - 1;
    case D:
      if(target < D)
        return C;
      else if(target > F)
        return G;
      else
        return E;
    case E:
      if(target < E)
        return D;
      else
        return F;
    case F:
      return E;
    case G:
      if(target < G)
        return D;
      else
        return H;
    case H:
      return G;
    //Edge-cases have been managed so no need for default case
  }
}

Then in you can implement goToState() like so:然后你可以像这样实现goToState()

int goToState(enum state current,enum state target){

  while(current != target && current != invalid){
    current = transtition(current, target);
    handle(current);  //Do whatever must be done for whatever state we are in along the way
  }
  if(current == invalid)
    return -1; //Error code
  else
    return 0;
}

No matter the state machine, you'll want to avoid "stateghetti", which is what happens when each and every state makes multiple, local decisions on what state to execute next.无论 state 机器如何,您都希望避免“stateghetti”,这是当每个 state 对接下来执行的 state 做出多个本地决策时会发生的情况。

Instead, have each state return a result code.相反,让每个 state 返回一个结果代码。 You can still have an array of function pointers no matter.无论如何,您仍然可以拥有一个 function 指针数组。 Then at the top level of the program, write something along the lines of this pseudo:然后在程序的顶层,按照这个伪代码写一些东西:

while(...)
{
  result = state_machine[current_state]();
  current_state = evaluate(current_state, result);
}

The function evaluate() is the only place in your code where state transitions are allowed. function evaluate()是代码中唯一允许 state 转换的地方。 Optionally it can double as your top-level error handler.可选地,它可以兼作您的顶级错误处理程序。

Then simply code down all dependencies you need inside this function:然后在这个 function 中简单地编写你需要的所有依赖项:

...
if(state==C && result==OK)
  return D;

if(state==D)
{
  if(result==OK)
    return E;
  else
    return G;
}
...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM