【问题标题】:Finding the shortest path between states and executing all the transitions in between找到状态之间的最短路径并执行其间的所有转换
【发布时间】:2022-08-18 16:15:25
【问题描述】:

我有一个看起来像这样的状态机:

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

我想要一个函数,goToState(target),它以目标状态作为输入参数,然后该函数将执行从当前状态开始的所有转换,直到它达到目标状态。

例如,假设当前状态为 B,我们调用 goToState(F)。然后该函数将执行以下状态转换 B->C、C->D、D-​​>E、E->F。

转换是双向的,所以如果当前状态是 F 并且我们调用 goToState(G),那么函数将执行 F->E、E->D、D-​​>G。

如果我们有一个线性状态机(例如,没有分支 G--H),那么我只需按照合法顺序为每个转换执行一组函数,然后找到当前状态的索引和目标的索引状态并在 for 循环中调用这两个索引之间的所有转换函数。

但是,现在我有了一个分支,这种方法就行不通了。对合法转换进行编码并实现基于 C 中的目标状态以正确顺序执行它们的函数的最有效方法是什么?

编辑: 正如其他一些用户很好地指出的那样,我正在寻找的是某种寻路算法,它可以找到两个状态之间的最短路径。我只是在原始帖子中找不到合适的词来正确表达它。我需要最简单的寻路算法,该算法适用于如上所示的状态图。状态图永远不会比这更复杂,因此算法也不需要涵盖任何其他场景。

  • 您是在询问如何将状态转换编程为特定的、可直接到达的状态,还是询问路径查找算法以找出要选择的下一个状态?似乎您在这里没有专注于一个问题。请专注于一个问题,要么通过证明您可以转换到可到达状态,要么具体询问如何做到这一点。
  • 一个图形?可能是一个如果不能有任何循环?
  • @Some 程序员老兄,这些将如何帮助找到正确的分支?!
  • 这可以用图论更好地描述:您正在寻找无向图中两个节点之间的路径。您机器中的每个状态都是graph 中的一个节点,每个转换都是两个节点之间的一条边,并且转换可以是任意一种方式(边是无向的)。如果您只需要处理状态图,那么您希望在两个节点之间使用find a path
  • 呃,我会因为需要详细信息而投票关闭它,但是已经有两个错误的投票 - 一个它需要调试细节,它不需要,因为它是关于算法的问题,而不是关于代码中的错误,还有一个是寻求书籍、工具、软件库等的推荐,除非在“更多”中不恰当地包含算法。投票结束将导致 Stack Overflow 列出早期令人困惑的原因之一。

标签: c transition state-machine


【解决方案1】:

您可以将状态建模为一个结构数组,每个结构都包含一个用于转换的函数指针和一个可能的目标状态数组。

然后,创建一个获取当前状态和目标状态的函数,并让它遍历可能的状态列表。如果一个匹配目的地,把它放在一个空的状态列表的开头并返回列表。如果不是,则递归每个可能的中间状态,直到其中一个返回非空列表并将当前状态添加到列表的前面。

递归函数返回后,您可以遍历运行转换的列表。

#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;
}

输出:

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

【讨论】:

    【解决方案2】:

    无论状态机如何,您都希望避免“stateghetti”,这是当每个状态都针对接下来要执行的状态做出多个本地决策时发生的情况。

    相反,让每个状态返回一个结果代码。无论如何,您仍然可以拥有一组函数指针。然后在程序的顶层,按照这个伪代码写一些东西:

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

    函数evaluate()只要放置在允许状态转换的代码中。可选地,它可以兼作您的顶级错误处理程序。

    然后只需在此函数中编写所需的所有依赖项:

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

    【讨论】:

    • 这对进一步找到从一个给定状态到另一个状态的路径的目标有什么作用?状态机通常用于处理输入,其中输入指导转换。这个问题不涉及那个;它的目标是遍历从一个给定状态到另一个给定状态的路径。
    • @EricPostpischil 这可以通过传递先前的结果或在评估()中记住它们来轻松扩展。
    • @Lundin 也许我没有很好地描述这个问题。每个状态都进行一些处理并根据条件产生下一个状态,这不是典型的状态机。我要解决的是找到一种简单的方法来找到两个状态之间的最短路径。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-18
    相关资源
    最近更新 更多