Forum: Mikrocontroller und Digitale Elektronik Datenstruktur gerichtete Graphen


von Walter T. (nicolas)


Angehängte Dateien:

Lesenswert?

Guten Tag,

ich will eine Zustandsmaschine implementieren, die sich von einem 
beliebigen IST-Zustand über eine Reihe von erlaubten Zustandswecheln auf 
einen Soll-Zustand zubewegt. Bislang ist das eine riesige geschachtelte 
switch-case-Orgie. Ich habe zwar insgesamt nur 12 Zustände, aber die 
kombinatorische Explosion macht das extrem unübersichtlich.

Andererseits gibt es ohnehin nur eine überschaubare Anzahl an erlaubten 
Zustandsübergängen. Der Informatiker wird das wohl als eine Ansammlung 
gerichteter Graphen verstehen.

Mir fehlt nur gerade die Vorstellungkraft, wie da die passende 
Datenstruktur aussehen mag, um den passenden Pfad vom aktuellen Zustand 
zum Zielzustand effizient zu finden.

von Dussel (Gast)


Lesenswert?

Ich bin nicht sicher, ob ich dir Frage verstehe. Du bist in beliebigen 
Zustand X und möchtest wissen, über welche Übergänge man in Zustand Y 
kommt, oder? Dafür brauchst du erstmal keine Datenstruktur, sondern 
einen Algorithmus. Das Ergebnis kannst du dann speichern und auslesen. 
Dafür würde ich wahrscheinlich eine einfache Liste für jeden Knoten mit 
möglichen Zielknoten nehmen.

Als Algorithmen bieten sich Graphsuchalgorithmen an, zum Beispiel zu 
finden unter 
https://de.wikipedia.org/wiki/Kategorie:Graphsuchalgorithmus
Leider kann ich auf die Schnelle nicht sagen, welcher davon auf 
gerichteten Graphen funktioniert.

von VD (Gast)


Lesenswert?

1. Versuch
Es ist tatsächlich schwer vorstellen, was du willst. Bestimmt lege ich 
falsch. Trotzdem ich versuche es
 Was du hier gezeigt hast, ist nur der Lifecycle für  eventuell einen 
Knoten. Eventuel existieren viele solche Knoten, die zu einander 
bestimmte Relationen haben und Regeln. Zb. von einem kann man zu dem 
anderen nur dann übergehen, wenn ein Knoten in einem Zustand und der 
andere in dem anderen. Daher es ist tatsächlich ein graph, jedoch die 
Kanten sind entweder dynamisch, ob es sie gibt oder eventuell mit 
Gewichten rumspielen.

2. Versuch
Ein Knoten soll eine Klassentität sein. Eine relation auch. Danach gibt 
es konfiguration der Welt, und Logik, die alle Kanten auf die 
einheitliche Art und Weise durchläuft, wenn ein neuer Zustand 
übermittelt wird, in welchem sich die Maschine gerade befindett oder 
befinden wird. Mit Graphen ist es einfach zu realisieren.

Ich hoffe, ein wenig zu helfen.

von Klaus H. (klummel69)


Lesenswert?

Walter T. schrieb:
> Bislang ist das eine riesige geschachtelte
> switch-case-Orgie. Ich habe zwar insgesamt nur 12 Zustände, aber die
> kombinatorische Explosion macht das extrem unübersichtlich.
Variante 1: Split das ganze und nutze je Zustand jeweils eine Funktion 
die den Übergang zu den 11 anderen Zuständen prüft/steuert.  Dadurch 
wird es übersichtlicher.

Varinante2: Mach eine Zustandsübergangstabelle 12x12. Jeder erlaubte 
Übergang enthält einen Zeiger auf eine Funktion die eine Übergangsaktion 
ausführen kann. Ein Funktionspointer NULL signalisiert, dass der 
Übergang nicht erlaubt ist.

Variante3: Nutz ein Tool, das aus Zustandsdiagrammen Sourcecode erzeugt 
oder die Tabelle aus Variante2 erzeugt.

Was unklar ist: Sind die Zustandsübergänge fix? Oder können sie sich 
unter Bedingungen verändern?

von Walter T. (nicolas)


Lesenswert?

Dussel schrieb:
> Du bist in beliebigen
> Zustand X und möchtest wissen, über welche Übergänge man in Zustand Y
> kommt, oder?

Genau. Ich bin im Zustand A, will in Zustand B. Zu welchem Zustand muss 
ich als nächstes?
(Beispiel: Ich bin in "pre-init", will nach "manual" -> "init" ist der 
nächste Schritt.)

Bevor ich einen Algorithmus suchen kann, brauche ich doch erst einmal 
eine Datenstruktur für die Daten. Aber wahrscheinlich reicht ein 
einfaches zweifach indiziertes Array mit den Kantenlisten. Ungerichtete 
Kanten haben dann einfach zwei Einträge.

Wobei ich mittlerweile gemerkt habe, dass der Algorithmus auch gar nicht 
so effizient sein muss. 12 Zustände gehen in 4 Bit, d.h. ein komplettes 
Lookup-Table passt in 216 Byte. Damit kann man leben.

Klaus H. schrieb:
> Was unklar ist: Sind die Zustandsübergänge fix? Oder können sie sich
> unter Bedingungen verändern?

Sie können sich ändern, es können Verbindungen verboten werden. Aber 
alle erlaubten Bedingungen sind beim Programmstart bekannt.

: Bearbeitet durch User
von Εrnst B. (ernst)


Lesenswert?

Walter T. schrieb:
> Mir fehlt nur gerade die Vorstellungkraft, wie da die passende
> Datenstruktur aussehen mag, um den passenden Pfad vom aktuellen Zustand
> zum Zielzustand effizient zu finden.

Datenstruktur egal, und der Algorithmus eher auch. Weil's nur 12 
Zustände sind.

https://de.wikipedia.org/wiki/Breitensuche

drüber, fertig sobald du am Zielknoten angekommen bist.

Etwas komplizierter wirds, wenn du den Zustandsübergängen/Kanten noch 
Kosten zuweisen willst, also mehrere Wege zum Ziel führen können, aber 
der Kürzeste nicht unbedingt der Beste ist.

Aber auch da: Breitensuche mit etwas modifizierter Abbruchbedingung wird 
wohl reichen.

von VD (Gast)


Lesenswert?

Walter T. schrieb:
> Zu welchem Zustand muss ich als nächstes?

Hier ist noch zu klären  was bedeutet 'muss'? Denn, wenn du sagen 
würdest 'kann', dann ist die Logik der Kanten die Grundlage, aber mit 
dem muss ist gemeint, dass ein 'höheres' Ziel vorhanden ist und fie 
Anzahl der Schritte klein wie möglich sein soll...

von VD (Gast)


Lesenswert?

Und die Ergänzung dazu.

Walter T. schrieb:
Zu welchem Zustand muss ich als nächstes?

Im Schach gibt es sehr viele Züge, die möglich sind, aber zum Gewinn 
bringen nur bestimmte.

von Mikro 7. (mikro77)


Lesenswert?

Walter T. schrieb:
> ich will eine Zustandsmaschine implementieren, die sich von einem
> beliebigen IST-Zustand über eine Reihe von erlaubten Zustandswecheln auf
> einen Soll-Zustand zubewegt.

Hört sich eher nicht nach einem Zustandsautomaten an, wenn du dich auf 
einen "Sollzustand zubewegst".

https://en.wikipedia.org/wiki/Finite-state_machine

> Andererseits gibt es ohnehin nur eine überschaubare Anzahl an erlaubten
> Zustandsübergängen. Der Informatiker wird das wohl als eine Ansammlung
> gerichteter Graphen verstehen.

> Mir fehlt nur gerade die Vorstellungkraft, wie da die passende
> Datenstruktur aussehen mag, um den passenden Pfad vom aktuellen Zustand
> zum Zielzustand effizient zu finden.

Für Datenstrukturen siehe:
https://en.wikipedia.org/wiki/Sparse_matrix

Für eine Suche wurde ja oben schon die Breitensuche erwähnt:
https://en.wikipedia.org/wiki/Breadth-first_search

von Dussel (Gast)


Lesenswert?

Walter T. schrieb:
> Bevor ich einen Algorithmus suchen kann, brauche ich doch erst einmal
> eine Datenstruktur für die Daten. Aber wahrscheinlich reicht ein
> einfaches zweifach indiziertes Array mit den Kantenlisten. Ungerichtete
> Kanten haben dann einfach zwei Einträge.
Da es anscheinend auf einem Mikrocontroller laufen soll, muss man sehen, 
wie viel Platz vorhanden ist.
Ist viel Platz übrig, würde ich mir um die Struktur nicht so viele 
Gedanken machen. Eine Liste aller (Start-)Knoten, wovon jeder dieser 
Knoten eine Liste der potentiellen Zielknoten enthält und davon jeder 
eine Liste des besten Wegs zu diesem Knoten.
Ansonsten könnte man das sicher auch deutlich sparsamer beschreiben, 
wenn man den Weg über Verweise auf andere Wege speichert. Man speichert 
zum Beispiel den Pfad von B nach E über C und D (B->C->D->E). Wenn man 
jetzt von A nach D möchte und A und B benachbart sind, muss man nur den 
Weg von A nach B (A->B) speichern und ab da auf den schon gespeicherten 
Pfad von B nach E verweisen.

Mikro 7. schrieb:
> Walter T. schrieb:
>> ich will eine Zustandsmaschine implementieren, die sich von einem
>> beliebigen IST-Zustand über eine Reihe von erlaubten Zustandswecheln auf
>> einen Soll-Zustand zubewegt.
>
> Hört sich eher nicht nach einem Zustandsautomaten an, wenn du dich auf
> einen "Sollzustand zubewegst".
Warum nicht? Das kann schon sein. Als einfaches Beispiel eine 
Ampelschaltung. Nord-Süd-Richtung hat grün und jetzt soll auf 
Ost-West-Richtung grün geschaltet werden. Das geht nicht direkt, sondern 
man über über N-S gelb, alle rot, O-W gelb-rot zu O-W grün gehen.
Das ist jetzt natürlich ein sehr einfaches Beispiel, aber auch in einem 
Zustandsautomaten kann der kürzeste Weg zu einem Zielzustand gesucht 
sein.

von Walter T. (nicolas)


Lesenswert?

Klaus H. schrieb:
> Variante 1: Split das ganze und nutze je Zustand jeweils eine Funktion
> die den Übergang zu den 11 anderen Zuständen prüft/steuert.  Dadurch
> wird es übersichtlicher.

Das war mein erster Versuch. Ergebnis war eine Schar einander sehr 
ähnlicher Funktionen. Deswegen will ich jetzt den datenbasierten Ansatz 
versuchen.

Mikro 7. schrieb:
> Für Datenstrukturen siehe:
> https://en.wikipedia.org/wiki/Sparse_matrix

Hmja, an eine Art Adjazenzmatrix anstelle einer Kantenliste habe ich 
auch schon gedacht.

Mikro 7. schrieb:
> Hört sich eher nicht nach einem Zustandsautomaten an, wenn du dich auf
> einen "Sollzustand zubewegst".

Bitte nicht den Anwendungsfall (Zustandsautomat) mit dem Mittel dazu 
(Pfadsuche) verwechseln. Mir geht es um letzteres.

Mikro 7. schrieb:
> Für eine Suche wurde ja oben schon die Breitensuche erwähnt:

Nach allem, was ich heute gelesen und probiert habe, ist Breitensuche 
wohl nicht so der Renner auf einem Mikrocontroller (pun intended), weil 
doch recht dynamisch mit dem Speicher umgegangen wird (Warteschlange), 
und weil ich sie auch mit viel Mühe nicht von rekursiv auf iterativ 
umgestellt bekomme.

Aber kommt Zeit kommt Rat. Der freie Samstag ist jetzt vorbei, und 
vielleicht fällt im Laufe der Woche noch der Groschen.

von Walter T. (nicolas)


Lesenswert?

Nachtrag:

Über die Adjazenzmatrix geht es doch ganz angenehm, direkt die 
Breitensuche aus dem Wikipedia-Artikel zu implementieren. Ein wenig 
knifflig war es noch, den Suchpfad mitzuloggen.

Ein wenig unsicher bin ich mir noch, ob die Länge der Warteschlange 
beschränkt ist (und wie lang).

Ich werde das also sicherheitshalber nur dazu nutzen, auf dem PC 
lookup-Tables zu erzeugen.

von Harry L. (mysth)


Lesenswert?


von Εrnst B. (ernst)


Lesenswert?

Walter T. schrieb:
> Nach allem, was ich heute gelesen und probiert habe, ist Breitensuche
> wohl nicht so der Renner auf einem Mikrocontroller (pun intended), weil
> doch recht dynamisch mit dem Speicher umgegangen wird (Warteschlange),

Musst du nicht dynamisch machen. Du hast 12 Knoten, wie groß kann also 
eine "Warteschlange" von noch-nicht-besuchten knoten maximal werden?

Wenn du Speicher sparen willst: sowohl die Kanten von jedem Knoten weg 
als auch die Queue, und sogar die Wegbeschreibung passen in je einen 
uint16_t, als einzelne Bits kodiert. Ist sogar noch Platz übrig, um auf 
16 Knoten zu erweitern.

Viele Aktionen auf dem Graph werden dann zu einfachen Logik-Operationen.

von quotendepp (Gast)


Lesenswert?

https://de.m.wikipedia.org/wiki/Dijkstra-Algorithmus
1
Der Algorithmus von Dijkstra (nach seinem Erfinder Edsger W. Dijkstra) ist ein Algorithmus aus der Klasse der Greedy-Algorithmen[1] und löst das Problem der kürzesten Pfade für einen gegebenen Startknoten.

von Walter T. (nicolas)


Lesenswert?

Εrnst B. schrieb:
> Du hast 12 Knoten, wie groß kann also
> eine "Warteschlange" von noch-nicht-besuchten knoten maximal werden?

Das ist die Frage. Dafür bin ich zu wenig Informatiker. 8191 Knoten?

von Εrnst B. (ernst)


Lesenswert?

Walter T. schrieb:
> Das ist die Frage. Dafür bin ich zu wenig Informatiker. 8191 Knoten?

Es macht keinen Sinn in die Queue Knoten einzufügen, für die bereits ein 
kürzerer Weg gefunden wurde.

Neuer Versuch? Vielleicht über die Mengenlehre? Du bildest die Teilmenge 
der Knoten die vom aktuellen Knoten erreichbar und noch unbesucht sind. 
Kann diese Teilmenge größer sein als die Menge "aller Knoten" oder 
größer als die Menge "unbesuchter Knoten"?

Warum "Mengenlehre"? Wenn alle Kanten (ungewichtet) gleichlang sind, 
passt das schön zur µC-tauglichen Implementierung über Bitfelder. 
Schnittmenge ist Bitweises AND usw.

Einzig aus dem Bitmuster wieder eine Knotennummer zu generieren ist 
etwas ungünstig.
Der GCC bringt ein ffs / __builtin_ffs mit, was zwar "schnell" ist, aber 
trotzdem O(n) …—… für n ≤ 12.

von Walter T. (Gast)


Lesenswert?

Εrnst B. schrieb:
> Kann diese Teilmenge größer sein als die Menge "aller Knoten" oder
> größer als die Menge "unbesuchter Knoten"?

Wenn ich das richtig sehe ja. Jeder Knoten wird ja pro Verzweigung und 
einmal mehr besucht.

Und die Reihenfolge ist wichtig, weil daraus später der Pfad generiert 
wird. Also braucht die Warteschlange mindestens 4 Bit pro Element.

von Εrnst B. (ernst)


Lesenswert?

Walter T. schrieb:
> Jeder Knoten wird ja pro Verzweigung und
> einmal mehr besucht.

Muss er nicht, ist ja Breitensuche.
Falls du einen Knoten zur Queue hinzufügen willst, der schon drinnen 
ist: Einfach nicht machen.
Der vorhandene Eintrag wurde über einen kürzeren Pfad erreicht, und bei 
ungewichteten Kanten (insb. keine negativen Gewichte) macht es keinen 
Sinn, längere Pfade und Schleifen zu verfolgen.

Walter T. schrieb:
> Und die Reihenfolge ist wichtig, weil daraus später der Pfad generiert
> wird.

Der Pfad braucht die Reihenfolge nicht (wieder unter der Annahme: 
"Ungewichtete Kanten"), die ergibt sich beim "Abarbeiten" des Pfades 
wieder automatisch, der ganze Pfad passt also auch in ein uint16_t (wenn 
der Startknoten bekannt ist)

Aktueller Knoten X, Pfad P: Nächster Knoten X' ist dann 
ffs(kanten[X]&P)-1, nächster Pfad P' ist P&~(1<<X)
Ziel erreicht bei !P...

: Bearbeitet durch User
von Walter T. (nicolas)


Lesenswert?

Εrnst B. schrieb:
> der ganze Pfad passt also auch in ein uint16_t (wenn
> der Startknoten bekannt ist

Das verstehe ich nicht. Wie passt der ganze Pfad dort hinein, dass ich 
die Reihenfolge rekonstruieren kann, ohne die ganze Suche wieder zu 
durchlaufen?

von Εrnst B. (ernst)


Lesenswert?

Walter T. schrieb:
> Das verstehe ich nicht. Wie passt der ganze Pfad dort hinein, dass ich
> die Reihenfolge rekonstruieren kann, ohne die ganze Suche wieder zu
> durchlaufen?

Weil du nicht neu suchen musst, sondern die Schnittmenge bildest 
zwischen denen von der aktuellen Position direkt erreichbaren Knoten und 
den Knoten, die in deinem Pfad vorkommen.
Schnittmenge: "Bitweises AND", "kanten[X] & P" im Pseudocode oben.

Diese Schnittmenge ist dann einelementig (bei einem eindeutigen Pfad), 
kann aber, wenn mehrere gleichwertige Pfade existieren, auch 
mehrelementig sein.

Du wählst einfach einen Knoten aus der Schnittmenge aus (ich nahm den 
mit der niedrigsten Nummer), und machst von dem aus weiter, im 
Pseudocode oben "ffs(...)-1"

Also keine Suche, einfaches Ablaufen der Knotenmenge....

Das Aufbauen des "Pfad"-Bitsets, dafür ist die Suche nötig.


Wenn's grad nicht so schön sonnig wäre, würd ich das mal in C 
runtertippen. Vielleicht am Nachmittag...

: Bearbeitet durch User
von Walter T. (nicolas)


Lesenswert?

Hier ist es diesig.

Es funktioniert wirklich. Die Warteschlange darf wirklich eine Bitmaske 
sein, weil die Reihenfolge der Abarbeitung der Warteschlange egal ist.

Damit wird die Sache erstaunlich effizient, zumal der µC auch CLZ() 
eingebaut hat. Nicht effizient genug, um in der Hauptschleife immer 
mitzulaufen, aber effizient genug, um die Lookup-Tables auf der 
Zielplattform zu berechnen.

Momentan ist mein Quelltext definitiv noch zu häßlich zum zeigen, und 
auch noch nicht alle Grenzfälle abgetestet.

Danke für die hilfreichen Beiträge und Tipps!

Beitrag #7223336 wurde vom Autor gelöscht.
Beitrag #7223340 wurde vom Autor gelöscht.
von Walter T. (nicolas)


Angehängte Dateien:

Lesenswert?

Es scheint alles zu funktionieren und ich bin zufrieden.

Im Anhang noch mein Quelltext, falls jemand in eine ähnliche
Problemstellung läuft und ein abschreckendes Beispiel sucht. Das
Testbeispiel entspricht dem Diagramm im Eröffnungspost.

Danke für die Hilfe und die Anregungen!

von Klaus H. (klummel69)


Lesenswert?

Funktioniert der Code?
Hast Du da später was verändert?
* alut?
* lut als funktion und als parametername?
seltsam...
1
    void lut(void *lut, uint32_t Adj[], uint8_t nNode)
2
    {
3
        uint8_t (*alut)[nNode][nNode] = (uint8_t (*)[nNode][nNode]) lut;
4
        //...

von Walter T. (nicolas)


Lesenswert?

Klaus H. schrieb:
> Hast Du da später was verändert?
Ja, damit das Beispiel zum Eröffnungsbeitrag passt.

Klaus H. schrieb:
> Funktioniert der Code?

Leider nein, in  TEST(statemachine, findPath) ist ein Tippfehler (forwrd 
anstelle forward), aber ich wollte meinen Beitrag nicht ein drittes Mal 
löschen, weil sich der Anhang nicht bearbeiten lässt.

Klaus H. schrieb:
> lut als funktion und als parametername?

Gibt es eine Regel, die das verbietet?

von Klaus H. (klummel69)


Lesenswert?

Ah, jetzt hab ich’s kapiert. Zu schnell überflogen. alut  passt.

>>lut als funktion und als parametername?
>Gibt es eine Regel, die das verbietet?

Nein, der Parameter lut wird als lokale Variable betrachtet und 
überdeckt damit den globalen Namen der Funktion. Ich würde es trotzdem 
nicht machen. Änderst Du den Parameternamen könnte das Programm 
weiterhin compilieren, da du dann den Funktionspointer (über den cast) 
speicherst.

Na ja, das wird natürlich beim Test aufgedeckt. Aber ich versuche solche 
Konstruktionen zu vermeiden.

Seltsam dass der Compiler nicht meckert? Welchen Compiler nutzt Du? Was 
passiert wenn du '-Wshadow' o.ä. aktivierst?

von Walter T. (nicolas)


Lesenswert?

Klaus H. schrieb:
> Welchen Compiler nutzt Du?

Einen gut abgelagerten GCC 5.1.0 (MinGW). Mit -Wshadow ändert sich 
nichts. Aber die mir bekannte Beschreibung lautet "Warn whenever a local 
variable shadows another local variable", was ja auch nicht zutrifft.

Klaus H. schrieb:
> Aber ich versuche solche Konstruktionen zu vermeiden.

Es wird wohl auch nicht so bleiben. Das war jetzt Zufall - geschuldet 
einem ausprobieren zwischen Tür und Angel (Respektive zwischen 
Mittagessen und Kuchen).

Meinen "Prototypen" für die Breitensuche hatte ich gestern in Matlab 
geschrieben, und heute wollte ich in C ausprobieren, ob es wirklich so 
einfach ist und die Reihenfolge der Warteschlange wirklich keine Rolle 
spielt. Und plötzlich hatte ich eine funktionierende Pfadsuche und habe 
den LUT auch noch schnell nachgeschoben.

(Den Wechsel zwischen Matlab und C sieht man leider auch recht deutlich 
daran, dass ich an etlichen Stellen vergessen habe, dass ich Umlaute 
nutzen darf.)

: Bearbeitet durch User
von Jens R. (tmaniac)


Lesenswert?

Ich habe die Kommentare bisher nur überflogen, falls das schon mal 
angesprochen wurde habe ich es übersehen.
Aber solche Zustandsautomaten wie die in der Aufgabenstellung oben 
beschrieben sind, lassen sich sehr kompakt in C++ mit hilfe von enum 
class und den passenden Operator beschreiben. Bis auf den Übergang in 
den Abzweig ist das komplett im ++operator machbar 🙄

von Klaus H. (klummel69)


Lesenswert?

Walter T. schrieb:
> Einen gut abgelagerten GCC 5.1.0 (MinGW). Mit -Wshadow ändert sich
> nichts. Aber die mir bekannte Beschreibung lautet "Warn whenever a local
> variable shadows another local variable", was ja auch nicht zutrifft

Stimmt. Hab's mit aktuellem gcc und clang probiert, meckert nicht.

Ich orientiere mich meist an den
CppCoreGuidelines: "ES.12: Do not reuse names in nested scopes".

von VD (Gast)


Lesenswert?

Generell:
Ich muss eingestehen, aus deiner Aufgabe und dem Code nicht viel 
verstanden zu haben. Denn. Es sind nur Zustände und die erlaubte 
Übergänge angegeben, jedoch nicht - was bedeutet ein IST- und dazu ein 
SOLL-Zustand. Dazu noch.
Es sind z.B. Zyklen vorgeben wie z.B. manual<->sync. Daher, nehme ich an 
wenn der Automat sich in einem IST-Zustand wie pre-init befindet, was 
würde bedeuten - würde er in den Zustand manual jemals reingehen können, 
wenn der SOLL-Zustand z.B. disabled ist? Und wenn ja, dann - wie viele 
Male? Überhaupt - wie kann man sich vorstellen den SOLL-Zustand? Ist der 
SOLL-Zustand immer "disabled"?

Wenn man das nicht beachtet, dann ist die Bedeutung von Richtungen wie 
sync->manual, sync->forward, sync->reverse gar nicht nötig, denn solche 
bei der Berechnung des kürzeren Weges niemals "betreten" werden sollen. 
Weil sie als Zyklus erkannt und als unnötig "ignoriert" werden müssen.

Wie gesagt, sind solche Relationen dafür da, bestimmen zu können, welche 
nächster Schritt überhaupt erlaubt ist. Jedoch nicht, wie der richtige 
Weg durch die Zustände aussehen kann. Dafür sind weitere Logiken nötig, 
die über den Übergang mehr Informationen liefern, ob ein Übergang einen 
Sinn ergibt.

Min. mögliche Lösung:
Ohne deine Aufgabe-Stellung so richtig verstanden zu haben, lässt sich 
auch nicht dein Code nachzuvollziehen. Erlaube mir aber doch zu sagen, 
dass es auf den ersten Blick zu komplex aussieht. Manchmal kann man auch 
genau das lösen, was gefordert ist und nicht eine 
"allgemein-galaktische-Lösung" erfinden.

Für genau diese Anzahl (8) der erlaubten Kombinationen würde ich z.B. 
mit 8 Bytes lösen können, wo jedem Knoten ein Bit zugewiesen werden 
kann:
1
[disabled] [de-init] [sync] [reverse] [forward] [manual] [init] [pre-init]
2
state[0] = b00000010; // pre_init
3
state[1] = b00100000; // init
4
state[2] = b00100000; // manual
5
state[3] = b00100000; // forward
6
state[4] = b00100000; // reverse
7
state[5] = b01011100; // sync
8
state[6] = b10000000; // de_init
9
state[7] = b00000000; // disabled
wie man sieht, sind die Werte für state[1] state[2], state[3] und 
state[4] gleich, weil sie alle nur auf sync verweisen.

Und wenn ein weiterer Zustand ermittelt werden will, dann aus den 
Bit-Stellen, die für diesen Zustand auf Eins stehen - ableiten, ob es 
möglich ist. Z.B. aus state[0] = b00000010; // pre_init ist "sichtbar" 
dass disabled, also 7. Stelle nicht erreichbar ist, weil sie auf Null 
steht. Aber das ist nur als Beispiel zum Nachdenken - nicht als Lösung. 
Da ich nach wie vor nicht verstanden habe, was SOLL-Zustand bedeutet?

von Walter T. (nicolas)


Lesenswert?

VD schrieb:
> Ich muss eingestehen, aus deiner Aufgabe und dem Code nicht viel
> verstanden zu haben.
> [Viel weiterer Text]

Das ist in Ordnung. Nicht jeder muß zu allem eine Meinung haben. Also 
warum der Beitrag?

von Εrnst B. (ernst)


Lesenswert?

Walter T. schrieb:
> Es funktioniert wirklich. Die Warteschlange darf wirklich eine Bitmaske
> sein, weil die Reihenfolge der Abarbeitung der Warteschlange egal ist.

Freut mich, dass mein inkohärentes Gebrabbel dich irgendwie zu 
funktionierenden Code leiten konnte :)

> Damit wird die Sache erstaunlich effizient, zumal der µC auch CLZ()
> eingebaut hat.

Rein interessehalber:
ist auf deiner Zielplattform ffs / __builtin_ffs entsprechend optimiert, 
dass es auch CLZ verwendet?

von VD (Gast)


Lesenswert?

Walter T. schrieb:
> Nicht jeder muß zu allem eine Meinung haben. Also warum der Beitrag?

Eine Aufgabestellung hat mit unterschiedlichen Meinungen nichts zu tun. 
Die Beiträge schreibt man, um eventuell Menschen zu unterstützen. Da du 
aber frech bist, stellte ich fest, dass es tatsächlich Mühe umsonst war.

von Walter T. (nicolas)


Lesenswert?

Εrnst B. schrieb:
> Rein interessehalber:
> ist auf deiner Zielplattform ffs / __builtin_ffs entsprechend optimiert,
> dass es auch CLZ verwendet?

Danke für den Hinweis. Es hätte natürlich __builtin_clz() und nicht 
__builtin_clzl() heißen müssen. Praktischerweise hat der Optimizer aber 
gemerkt, dass nur 32 Bit geprüft werden müssen.

Falls das nicht als wink mit dem Zaunpfahl gemeint war: Das weiß ich 
nicht. ffs() habe ich noch nie verwendet. __builtin_clz() und 
__builtin_clzl() führen im Assembler-Listing zu einem (bzw. zwei) CLZx.

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.