Benutzer:Rolf b/boolescheAlgebra
Boolesche Algebra bezeichnet im Rahmen der Programmierung die Regeln, nach denen sich die grundlegenden logischen Operationen UND, ODER und NICHT im Zusammenhang mit den logischen Werten TRUE und FALSE verhalten. Der Begriff an sich ist deutlich umfangreicher, bei Interesse sei auf den Wikipedia-Artikel zur Booleschen Algebra [1] verwiesen.
Inhaltsverzeichnis
Einführung
Bei den üblichen Programmierproblemen steht man vor der Aufgabe, fachliche oder technische Situationen zu identifizieren, um dann je nach Situation das Richtige zu tun. Eine einzelne Situation wird erkannt, indem man beliebige Werte miteinander vergleicht und so Wahrheitswerte enthält. Gelegentlich werden ermittelte Wahrheitswerte auch in Variablen gespeichert und später wieder verwendet (solche Variablen nennt man Schalter oder Flag).
Zumeist ist aber nicht nur ein Wahrheitswert, sondern mehrere zu betrachten, und konkrete Situationen erkennt man durch Verknüpfungen dieser Wahrweitswerte im Sinne von "Wenn dies, aber nicht das - es sei denn JENES ist der Fall - DANN haben wir den Fall 17c und müssen dieses und jenes tun". Die Booleschen Algebra hilft dabei, den WENN Teil dieser Regeln besser zu verstehen.
Um komplexe Bedingungsgefüge übersichtlich darstellen zu können, gibt es ein Hilfsmittel, auf das in einem weiteren Artikel eingegangen werden soll: Entscheidungstabellen.
Zunächst aber wollen wir einzelne logische Operationen betrachten.
Wahrheitstabellen
Eine gängige Methode, eine logische Operation zu beschreiben, ist die Wahrheitstabelle. In einer solchen Tabelle notiert man die an der Operation beteiligten Wahrheitswerte und schreibt dazu auf, für welche Kombination dieser Wahrheitswerte das Ergebnis der logischen Operation wahr sein soll. Die beteiligten Wahrheitswerte kann man auf unterschiedliche Weise notieren, beispielsweise durch false
und true
, durch FALSCH und WAHR, oder auch durch Buchtaben wie F und W oder N und J (für Nein und Ja).
Die möglichen Kombinationen der Eingangswerte schreibt man dabei systematisch auf, um keine zu übersehen. Je mehr Eingangswerte es gibt, desto mehr Kombinationen sind möglich: für N Eingangswerte gibt es 2N Kombinationen. Zumeist schreibt man die Kombinationen zeilenweise auf. Wahrheitstabellen können komplexe Zusammenhänge darstellen:
A B C >= 2 false false false false false false true false false true false false false true true true true false false false true false true true true true false true true true true true
Um zu beschreiben, wie man die durch eine solche Wahrheitstabelle vorgegebene Logik programmieren kann, müssen wir zuerst auf die logischen Operationen eingehen.
Grundlegende logische Operatoren
In den gängigen Programmiersprachen findet man zwei Arten von Operationen auf Wahrheitswerten, die durch spezielle Symbole – die Operatoren – ausgedrückt werden.
- einstellige Operatoren
- sie erzeugen aus einem Wahrheitswert einen anderen. In JavaScript oder PHP gibt es den NOT Operator, der zu einem Wahrheitswert sein Gegenteil liefert.
- zweistellige Operatoren
- sie verknüpfen zwei Wahrheitswerte und liefern einen neuen. JavaScript oder PHP kennen Operatoren für das logische UND und das logische ODER.
Operatoren auf Wahrheitswerten werden logische Operatoren genannt, die Wahrheitswerte selbst auch logische Werte.
Das Verhalten der logischen Operatoren ist über so genannte Wahrheitstabellen definiert. Dabei werden alle Kombinationen möglicher Eingangswerte betrachtet und der daraus entstehende Ergebniswert notiert.
Die Wahrheitstabelle eines einstelligen Operators wie NOT sieht so aus:
A | NOT A |
---|---|
TRUE | FALSE |
FALSE | TRUE |
Diese Tabelle liest sich so: Es gibt einen Eingangswert, bezeichnet als A, und einen Ausgangswert, der als „NOT A“ bezeichnet wird. Für jeden Wert von A wird angegeben, welchen Wert „NOT A“ dann annehmen soll.
Zweistellige Operatoren wie UND benötigen eine umfangreichere Wahrheitstabelle, weil es zwei Eingangwerte gibt und damit vier Kombinationen möglich sind. Es ist auch möglich, in einer Wahrheitstabelle das Verhalten mehrerer logischer Operationen parallel darzustellen. Die folgende Tabelle zeigt die Definition der Operatoren UND und ODER
A | B | A UND B | A ODER B |
---|---|---|---|
TRUE | TRUE | TRUE | TRUE |
TRUE | FALSE | FALSE | TRUE |
FALSE | TRUE | FALSE | TRUE |
FALSE | FALSE | FALSE | FALSE |
Weil es bei zwei Operanden vier Zeilen gibt, sind insgesamt 24=16 unterschiedliche zweistellige logische Operatoren möglich. Eine Übersicht dazu folgt weiter unten.
Für die weitere Beschreibung sollen die in den Sprachen der C-Familie gängigen Symbole ! für NOT, && für UND und || für ODER verwendet werden. Manche Programmiersprachen verwenden & und | statt && und ||, aber diese Symbole haben in Sprachen wie JavaScript, PHP oder C eine andere Bedeutung und würden hier nur verwirren.
Boole? Wer ist Boole?
George Boole war ein englischer Mathematiker, der sich mit Aussagenlogik beschäftigt hat und erkannte, dass sich das mathematische Konzept der Algebra darauf anwenden lässt. Seine Erkenntnisse wurden von Peano[2] weiterentwickelt und sind die Grundlage für die heutigen logischen Operatoren. Die Begriffe logischer Operator und boolescher Operator werden synonym verwendet.
Grundregeln
Genau wie bei den arithmetischen Operationen gibt es auch für logische Operationen bestimmte grundlegende Eigenschaften. Diese muss man kennen, um logische Ausdrücke umformen und vereinfachen zu können.
Grob gesagt kann man sich die Rechnung mit booleschen Werten so vorstellen, als würde FALSE der Zahl 0 entsprechen und TRUE der Zahl 1. Dann ist NOT a
die Rechnung 1 - a
und a AND b
entspricht der Multiplikation a∙b
. Die ODER Operation fällt aus dem Rahmen. Ansatzweise hat a OR b
die Entsprechung a + b
, aber weil man mit 1 + 1 = 2
den erlaubten Wertebereich verlassen würde, muss man eine Sonderregel einführen und definieren, dass 1 + 1
den Wert 1 ergibt.
Kommutativgesetz und Assoziativgesetz
Für UND (&&) und ODER (||) gilt ein Kommutativgesetz (Reihenfolge der Operatoren) und ein Assoziativgesetz (Auswertungsreihenfolge):
Kommutativgesetz
A && B | = | B && A |
A || B | = | B || A |
Assoziativgesetz
( A && B ) && C | = | A && ( B && C ) |
( A || B ) || C | = | A || ( B || C ) |
Manche Programmiersprachen nehmen sich die Freiheit, die Reihenfolge nicht festzulegen, in der die beteiligten Werte in einem Ausdruck ermittelt werden. Stammen die Werte dann aus Funktionsaufrufen, ist auch die Reihenfolge dieser Aufrufe nicht mehr gegeben. In JavaScript oder PHP ist das nicht so, hier ist für jeden Operator eine Assoziativität festgelegt, die für die logischen Operatoren bestimmt, dass gleichrangige Operatoren von links nach rechts ausgewertet werden.
Was Ihnen in JavaScript oder PHP aber passieren kann, ist eine Kurzschluss-Auswertung. Wenn der AusdruckA || B
auszuwerten ist und sich A als WAHR ergibt, ist das Ergebnis auf jeden Fall WAHR und B wird nicht mehr ermittelt. Das heißt: falls sich B durch einen Funktionsaufruf ergibt, wird die Funktion nicht mehr aufgerufen. Das Gleiche gilt in A && B
, wenn A den Wert FALSCH hat.Auflösen von Klammern 1: Distributivgesetze
In der Zahlenmathematik kann man lediglich die Multiplikation ausklammern. In der booleschen Algebra geht das überraschenderweise sowohl mit UND als auch mit ODER, deshalb gibt es zwei Distributivgesetze. Wenn Sie das nicht glauben, dann stellen Sie die Wahrheitstabelle auf und prüfen es nach.
( A && B ) || C | = | ( A || C ) && ( B || C ) |
( A || B ) && C | = | ( A && C ) || ( B && C ) |
Auflösen von Klammern 2: Gesetze von De Morgan
Darüber hinaus gibt es den Fall, dass die NOT-Operation auf eine in Klammern gesetzte UND oder ODER Operation trifft. In solchen Fällen kann der Wunsch bestehen, die Klammern aufzulösen. Diese Umformung folgt Regeln, die dem englischen Mathematiker Augustus De Morgan[3] zugeschrieben werden, obwohl William of Ockham sie bereits im 13. Jahrhundert formuliert hat.
Salopp formuliert lauten sie: Wird eine NOT-Klammer um einen UND oder ODER Term aufgelöst, wird aus dem UND ein ODER und aus ODER ein UND.
!(A && B) | = | !A || !B |
!(A || B) | = | !A && !B |
Beim Auflösen einer NOT-Klammer werden die De Morgan Regeln leider zu oft übersehen. Ihre Richtigkeit wird durch die nachfolgenden Wahrheitstabellen belegt:
A | B | A && B | !(A && B) | !A | !B | !A || !B |
---|---|---|---|---|---|---|
FALSE | FALSE | FALSE | TRUE | TRUE | TRUE | TRUE |
FALSE | TRUE | FALSE | TRUE | TRUE | FALSE | TRUE |
TRUE | FALSE | FALSE | TRUE | FALSE | TRUE | TRUE |
TRUE | TRUE | TRUE | FALSE | FALSE | FALSE | FALSE |
A | B | A || B | !(A || B) | !A | !B | !A && !B |
---|---|---|---|---|---|---|
FALSE | FALSE | FALSE | TRUE | TRUE | TRUE | TRUE |
FALSE | TRUE | TRUE | FALSE | TRUE | FALSE | FALSE |
TRUE | FALSE | TRUE | FALSE | FALSE | TRUE | FALSE |
TRUE | TRUE | TRUE | FALSE | FALSE | FALSE | FALSE |
Vorrangregeln in Programmiersprachen
Wichtig zu wissen ist auch, dass Programmiersprachen Regeln definieren, nach denen bestimmte Operatoren Vorrang vor anderen haben. Beim Rechnen wissen wir, dass 2+3⋅4 als 2+(3⋅4) zu interpretieren ist und nicht als (2+3)⋅4. Bei den logischen Operationen gilt, dass NOT die höchste Rangstufe hat, AND die zweithöchste und OR die niedrigste. Der Ausdruck A || !B && C ist demzufolge als A || ( ( !B) && C) zu lesen.
Weitere logische Operationen
Wie in der Einleitung erwähnt, sind mehr als nur die grundlegenden booleschen Operationen möglich. Es gibt vier einstellige Operationen, 16 zweistellige und - wenn man sie definieren möchte - 256 mögliche dreistellige Operationen.
Einstellige Operationen
Außer der bekannten NOT Operation gibt es noch die Identität (sie liefert ihre Eingabe zurück), die Tautologie ┬ (sie liefert immer TRUE) und die Kontradiktion ┴ (immer FALSE)
A | true | false | Bezeichnung | JavaScript |
---|---|---|---|---|
ID A | true | false | Identität | A |
NOT A | false | true | Negierung | !A |
┬ | true | true | Tautologie | true |
┴ | false | false | Kontradiktion | false |
Es klingt unsinnig, Operationen außer NOT überhaupt zu erwähnen. Im Rahmen funktionaler Programmierung, wo Funktionen zur Laufzeit zu komplexeren Funktionen komponiert werden können, kann es aber durchaus zweckmäßig werden, Identität, Tautologie oder Kontradiktion als Bausteine in einem vorgefertigten Ablauf einzusetzen.
Zweistellige Operationen
Die folgende kombinierte Wahrheitstabelle zeigt alle 16 möglichen zweistelligen logischen Operatoren im Überblick. Damit die Tabelle nicht zu breit wird, sind die Zeilen und Spalten gegenüber üblichen Wahrheitstabellen vertauscht. Die meisten dieser Operatoren werden von JavaScript (oder auch anderen Sprachen) nicht direkt unterstützt, daher wurde eine Spalte „JavaScript“ angefügt, die zeigt, wie man diesen Operator mit JavaScript formulieren muss.
A | true | true | false | false | |||
---|---|---|---|---|---|---|---|
B | true | false | true | false | Bezeichnungen | JavaScript | |
Operation
|
┬ | true | true | true | true | Tautologie | true |
A || B | true | true | true | false | Logisches ODER | A || B | |
B ⇒ A | true | true | false | true | B ⇒ A (Implikation, aus B folgt A) | A || !B | |
A | true | true | false | false | Immer A | A | |
A ⇒ B | true | false | true | true | A ⇒ B (Implikation, aus A folgt B) | !A || B | |
B | true | false | true | false | Immer B | B | |
A ⇔ B | true | false | false | true | Äquivalenz A ⇔B | A == B | |
A && B | true | false | false | false | Logisches UND | A && !B | |
NAND | false | true | true | true | Negiertes UND | !(A && B), !A || !B | |
A ⊕ B | false | true | true | false | A ⊕ B (XOR, Entweder-Oder) | A != B | |
!B | false | true | false | true | Not B | !B | |
!(A ⇒ B) | false | true | false | false | Negierte Implikation | A && !B | |
!A | false | false | true | true | Not A | !A | |
!(B ⇒ A) | false | false | true | false | Negierte Implikation | !A && B | |
NOR | false | false | false | true | Negiertes ODER | !(A || B), !A && !B | |
┴ | false | false | false | false | Kontradiktion | false |
Die meisten dieser Operatoren sind relativ gut verstehbar. Es gibt die klassischen Operatoren UND, ODER, ENTWEDER-ODER (A⊕B, XOR) und GENAU-WIE (Äquivalenz, A⇔B), es gibt die Negation von UND und ODER (auch NAND und NOR genannt), es gibt Projektions-Operatoren, die nur einen Operanden verwenden und dann seine Identität oder seine Negation liefern (A, B, !A, !B), es gibt natürlich Tautologie und Kontradiktion
Es gibt aber auch 4 Operatoren, die etwas schwieriger zu verstehen sind. Die Schreibweise A ⇒ B
ist als mathematische Schlussfolgerung zu lesen, d.h. A ⇒ B
ist TRUE, wenn eine korrekte mathematische Folgerung vorliegt. Wenn A den Wert FALSE hat, ist die Implikation immer richtig (aus etwas falschem kann man alles folgern), und wenn A den Wert TRUE hat, muss auch B gleich TRUE sein, damit die Implikation richtig ist.
Herleiten von logischen Ausdrücken
In vielen Fällen benötigt man für eine Programmentscheidung nur eine oder zwei Bedingungen und kann sie direkt aufschreiben.
Kommen dagegen mehrere Bedingungen zusammen, steht man vor einer Fülle möglicher Kombinationen, deren Strukturierung nicht leicht ist. Hier gibt es mehrere Hilfsmittel.
Karnaugh-Veitch Diagramm
Kommen wir noch einmal auf das "2 aus 3" Beispiel vom Anfang zurück.
A B C >=2 F F F F F F W F F W F F F W W W W F F F W F W W W W F W W W W W
Möchte man diese Wahrheitstabelle mit den in JavaScript oder PHP verfügbaren Operatoren !, && und || abbilden, wird die Darstellung schnell unübersichtlich. Es gibt 4 Zeilen, deren Ergebnis WAHR ist, und wenn man naiv herangeht, müsste man die Abfrage so schreiben:
if ((!A && B && C) || (A && B && !C) || (A && !B && C) || (A && B && C)) ...
Wie vereinfacht man das - systematisch? Dazu gibt es die so genannten Karnaugh-Vietch Diagramme[4] oder kurz KV-Diagramme. Ein solches Diagramm stellt eine Wahrheitstabelle mit 3 oder 4 Eingangswerten so dar, dass man logisch zusammenhängende Blöcke direkt sieht. Man beginnt mit einem einfachen Diagramm für eine Variable:
A=false | A=true |
---|---|
0 | 1 |
Eine Wahrheitstabelle mit nur einer Variablen und 2 Zeilen würde man nun so in das Diagramm übertragen, dass Zeile 1 in Zelle 0 kommt und Zeile 2 in Zelle 1. Aber für eine Variable lohnt sich ein KV-Diagramm nicht.
Für eine zweite Variable spiegeln wir dieses Rechteck nach unten. Im gespiegelten Bereich addieren wir 2 zu den Zellnummern:
A=false | A=true | |
---|---|---|
B=false | 0 | 1 |
B=true | 2 | 3 |
Das reicht schon für eine Wahrheitstabelle mit zwei Variablen und 4 Zeilen. Für eine dritte Variable spiegeln wir nach rechts und addieren im gespiegelten Bereich 4 auf die Zellnummern:
C=false | C=true | |||
---|---|---|---|---|
A=false | A=true | A=true | A=false | |
B=false | 0 | 1 | 5 | 4 |
B=true | 2 | 3 | 7 | 6 |
Für eine vierte Variable spiegelt man nochmals nach unten und addiert 8 - dafür sei auf den referenzierten Wikipedia-Artikel verwiesen.
Die Nummern in den Zellen entsprechen der Zeilennummer in der Wahrheitstabelle (ab 0 gezählt). Wenn wir die "2 aus 3" Wahrheitstabelle in ein KV-Diagramm eintragen, erhalten wir:
C=false | C=true | |||
---|---|---|---|---|
A=false | A=true | A=true | A=false | |
B=false | false | false | true | false |
B=true | false | true | true | true |
Um nun herauszufinden, wie sich mit möglichst wenig Abfragen der grüne Bereich abdecken lässt, versuchen wir, ihn mit Quadraten oder mit Rechtecken abzudecken. Rechtecke müssen dabei aber eine Zellenzahl bedecken, die einer Zweierpotenz entspricht. Dabei ist es erlaubt, ja sogar erwünscht, wenn diese Quadrate und Rechtecke sich überlappen.
In unserem Beispiel könnte man das 1x2 Rechteck in Spalte 3 wählen, sowie in Zeile 2 das 2x1 Rechteck ab der 2. Spalte und das 2x1 Rechteck ab der 3. Spalte. Was all diese Rechtecke gemeinsam haben, ist die Zelle mit der Nummer 7 (hier sind A, B und C true). Wir verwenden nun die Logikanteile, die die verwendeten Flächen gemeinsam haben:
- Rechteck in Zeile 2, ab Spalte 2: A=true UND B=true
- Rechteck in Spalte 2: A=true UND C=true
- Rechteck in Zeile 2, ab Spalte 3: B=true UND C=true
Diese UND-Gruppen werden nun durch ODER kombiniert:
istMindestens2 = (A && B) || (A && C) || (B && C);
Das ist deutlich kürzer als der naiv gefundene Logikausdruck. Durch Anwendung des Distributivgesetzes könnte man daraus noch dies machen:
istMindestens2 = A && (B || C) || (B && C);
Aber die Anzahl der Logikoperationen wird dadurch nicht geringer.
Diese Darstellung (UND-Gruppen durch ODER verbunden) nennt man die disjunktive Form eines Logikausdrucks („Disjunktion“ ist in der Aussagenlogik ein Begriff für ODER).
Entscheidungstabellen
Entscheidungstabellen sind eine Technik, um ein Regelwerk tabellarisch darzustellen. Dafür schreibt man zunächst alle Bedingungen auf, die für die benötigte Entscheidung vorausgesetzt sind. Darunter folgen dann alle Aktionen, die abhängig von diesen Bedingungen auszuführen sind. Entdeckt man dabei, dass eine Bedingung einen Wert verwendet, der erst von einer Aktion der Entscheidungstabelle bestimmt wird, so muss man die Tabelle in zwei ETs teilen.
Nun muss man Regeln formulieren. Man betrachtet eine Aktion, und überlegt sich, welche Bedingungen dafür wichtig sind und ob sie dafür Wahr oder Falsch ein müssen. Eine Regel kennt also drei Zustände: Wahr (W), Falsch (F) oder Irrelevant (-). Jede Regel, die man für eine Aktion formulieren kann, schreibt man neben den Bedingungen und Aktionen als Spalte auf. Bei den Bedingungen W, F oder -, und bei der Aktion ein X. Die anderen Aktionen bekommen erstmal ein "-".
Beispielsweise könnte man folgendes aufschreiben. "Vorsichtig fahren" soll bedeuten: Die Ampel ist kaputt, man muss die Kreuzung selbstständig überqueren.
Rot leuchtet J - - - - Gelb leuchtet - J - - - Grün leuchtet - - J - - Kreuzung ist frei - - - N - Anhalten X - - X - Gas geben - X - - - Ruhig weiterfahren - - X - - Vorsichtig fahren - - - - X
Die letzte Regel in dieser Adhoc-Tabelle ist eine Else-Regel, die ausgeführt wird, wenn keine andere Regel zutrifft. Man sieht recht schnell, dass diese ET nicht funktioniert. Zum Beispiel würde man bei Rot-Gelb nicht wissen, was man tun soll, weil zwei Regeln zutreffen. Die Tabelle gibt zwar die „Prinzipien“ der Ampelsteuerung wieder, ist aber für eine autonome Fahrzeugsteuerung unbrauchbar.
Der nächste Schritt besteht also darin, diese Widersprüche zu finden und zu entscheiden, was nun wirklich zu tun ist. Als Hilfestellung dazu kann man sich überlegen, dass eine Regel mit Irrelevanzmarkern in Wahrheit nicht eine Regel darstellt, sondern eine Zusammenfassung mehrerer Regeln. Eine Regel (J,J,J,-) repräsentiert eigentlich die Regeln (J,J,J,J) und (J,J,J,N). Die erste Regel der obigen Entscheidungstabelle steht für 8 ausformulierte Regeln, von denen vier ein J bei "Ampel zeigt Gelb" haben und daher mit der zweiten Regel kollidieren. Die folgende ET spezifiziert die Regeln genauer und stellt den Widerspruch deutlicher dar:
Rot leuchtet J J J N - - - Gelb leuchtet N J J J - - - Grün leuchtet - - - - J - - Kreuzung ist frei - - - - - N - Anhalten X X - - - X - Gas geben - - X X - - - Ruhig weiterfahren - - - - X - - Vorsichtig fahren - - - - - - X
Man sieht, dass Regel 2 dieser ET falsch ist: Bei Rot-Gelb fährt man los, statt anzuhalten. Diese Regel wird also gestrichen. Aber natürlich ist das nicht der einzige Widerspruch gewesen.
Statt auf diese Weise mühsam alle Widersprüche zu identifizieren, kann man eine andere Vorgehensweise wählen. Die ist ebenfalls mühsam, führt aber sicherer zum Ziel. Vor allem hat sie den Vorteil, dass man sich alle möglichen Kombinationen der Eingangsbedingungen systematisch vor Augen führt und damit sicher ist, keine zu übersehen.
Sich ausgehend von den Aktionen Bedingungen zu überlegen, ist in einer Brainstorming-Phase sinnvoll, in der es darum geht, die Bedingungen zu finden, die für die Entscheidungsfindung relevant sind. Hat man diese Bedingungen, kann man zur systematischen Analyse übergehen, indem man alle möglichen Regeln aufschreibt. Bei vier Ja/Nein Regeln sind es 16 Stück. Und dann kreuzt man für jede Regel an, welche Aktion zutrifft.
Rot leuchtet J J J J J J J J N N N N N N N N Gelb leuchtet J J J J N N N N J J J J N N N N Grün leuchtet J J N N J J N N J J N N J J N N Kreuzung ist frei J N J N J N J N J N J N J N J N Anhalten - X - X - X X X - X - X - X - X Gas geben - - X - - - - - - - X - - - - - Ruhig weiterfahren - - - - - - - - - - - - X - - - Vorsichtig fahren X - - - X - - - X - - - - - X -
Diese Matrix sieht sehr nach dem Zweiersystem aus. Ein "J" kann ein Eins-Bit in einer Binärzahl sein, ein "N" ein Null-bit. "Rot" könnte man also den Wert 8 zuordnen, "Gelb" die 4, "Grün" die 2 und "Frei" die 1. Damit hätte die erste Regel den Wert 15, die zweite die 14, und so weiter. Dann könnte man direkt losprogrammieren:
Binärmethode zur Realisierung von Entscheidungstabellen
Und tatsächlich gibt es Entscheidungstabellentools, die exakt auf diese Weise vorgehen. Dieses Vorgehen ist aber nicht unbedingt das effizienteste, weil die Regeln oftmals so aussehen, dass für bestimmte Aktionen längst nicht alle Bedingungen von Bedeutung sind. Man kann aber in jeden Zweig einen Zähler einbauen und dann im Test beweisen, dass die Testfälle alle Entscheidungszweige der Tabelle abdecken.
Eine effizientere Implementierung, die nur die nötigen Bedingungen abfragt, beginnt damit, zunächst einmal die Tabelle nach Aktionen zu sortieren und dann zu schauen, welche Regeln sich nur in einer Bedingung unterscheiden, aber die gleiche Aktion auslösen.
Rot leuchtet J J N N N J N J J J J J N N N N Gelb leuchtet J N J N N J J J J N N N J J N N Grün leuchtet J J J N J N N J N J N N J N J N Kreuzung ist frei J J J J J J J N N N J N N N N N Anhalten - - - - - - - X X X X X X X X X Gas geben - - - - - X X - - - - - - - - - Ruhig weiterfahren - - - - X - - - - - - - - - - - Vorsichtig fahren X X X X - - - - - - - - - - - - Übereinstimmungen A A B B C C D D E E F F
Rot leuchtet - J N N - J J J N N Gelb leuchtet J N N N J J N N J N Grün leuchtet J J N J N - J N - - Kreuzung ist frei J J J J J N N - N N Anhalten - - - - - X X X X X Gas geben - - - - X - - - - - Ruhig weiterfahren - - - X - - - - - - Vorsichtig fahren X X X - - - - - - -
Die Aufgabe, Widersprüche zu suchen, wird nun durch die Aufgabe ersetzt, Irrelevanzen zu finden. Dafür betrachtet man zwei Regeln, bei denen die gleichen Aktionen angekreuzt sind. Unterscheiden sie sich nur an einer Stelle, so ist diese Bedingung irrelevant. Das ist beispielsweise bei den Regeln 1 und 5 oder 2 und 6 der Fall.
Das Finden von Irrelevanzen ist übrigens nicht unbedingt nötig. Man kann das, was in dieser ausformulierten Entscheidungstabelle steht, sofort programmieren. Es wird nur sehr viel Schreibarbeit sein, und der Code ist nicht unbedingt der schnellste. Man verwendet dafür die sogenannte "disjunktive Normalform" für logische Ausdrücke, in der man mehrere Teilbedingungen mit ODER verknüpft. Innerhalb einer Teilbedingung wird nur UND und NICHT verwendet. Das entspricht genau den Entscheidungstabellen-Regeln: Die Bedingungen innerhalb einer Regel sind durch UND verbunden, und eine Aktion wird ausgeführt, wenn diese ODER jene ODER eine dritte Regel zutreffen. Die Aktion "Vorsichtig fahren" könnte man also durch diesen logischen Ausdruck erkennen:
if ( (rot && gelb && grün && frei) || (rot && !gelb && grün && frei) || (!rot && gelb && grün && frei) || (!rot && !gelb && !grün && frei)) { // vorsichtig fahren }
Wenn man beachtet, dass für Regel 1 und 5 die Prüfung auf gelb irrelevant ist, verkürzen sich die beiden ersten Vierfach-UND zu einem Dreifach-UND.
Darüber hinaus kann man nun das Distributivgesetz anwenden und die Teilbedingung && frei
ausklammern:
if ( frei && ((rot && grün) || (!rot && gelb && grün) || (!rot && !gelb && !grün)) ) { // vorsichtig fahren }
Ein weiterer scharfer Blick zeigt, dass man !rot
aus den Teilbedingungen 2 und 3 ausklammern kann:
if ( frei && ( (rot && grün) || !rot && ((gelb && grün) || (!gelb && !grün))) ) { // vorsichtig fahren }
Und NUN lohnt sich noch eine Kenntnis der zweistelligen Operationen; das, was nach Ausklammern von !rot
übrig geblieben ist, stellt die Äquivalenzrelation ⇔ dar, die sich durch den Gleichheitsoperator abbilden lässt. Ohne Kenntnis der zu Grunde liegenden Entscheidungstabelle versteht man kaum noch, wie diese Bedingung zu Stande gekommen ist, daher lohnt es sich, die ET im Code als Kommentar zu dokumentieren.
// ----------------------------------------- // | -------------------- | // | .---------. | .----------.| | if ( frei && ( (rot && grün) || !rot && (gelb == grün) ) ) { // vorsichtig fahren }
Manchmal ist es auch nicht so leicht, zu erkennen, in welcher Richtung man Redundanzen zusammenfasst. Ein Beispiel sind die Regeln 5, 6 und 7. Man kann nicht alle 3 zusammenfassen, und müsste sich für 5+7 oder 6+7 entscheiden. Schaut man weiter, findet man, dass 5+7 die bessere Idee ist, weil nämlich die Regeln 1,3,5,7,9,11,13 und 15 eine Achtergruppe bilden, die nur noch von der Bedingung "rot" abhängt. Für die Systematisierung solcher Überlegung gibt es Standardalgorithmen der Informatik. Einer davon sind Karnaugh-Veitch Diagramme, die im folgenden Abschnitt vorgestellt werden sollen.
Schwieriger ist es, wenn Regeln mehrere Aktionen auslösen. Es stellt sich dann die Frage, ob man die Regeln mit gleichem Aktionsmuster zusammenzufassen versucht und die Abfragebedingung dafür optimiert, oder ob man - wie im Fall der Regel "Anhalten" oben, die Bedingungen nur einer dieser Aktionen prüft. Oft hilft hier nur, beide Möglichkeiten zu betrachten und das bessere Ergebnis zu verwenden.
Optimieren von logischen Ausdrücken
Weblinks
Verwendete Bezüge
- ↑ Boolesche Algebra Wikipedia-Artikel
- ↑ Wikipedia: Peano-Axiome der booleschen Algebra
- ↑ Wikipedia: Augustus De Morgan
- ↑ Wikipedia: Karnaugh-Veitch Diagramm