Programmkorrektheit, Die Spezifikation


17

Aus Wikipedia: In der theoretischen Informatik wird die Korrektheit eines Algorithmus behauptet, wenn gesagt wird, dass der Algorithmus in Bezug auf eine Spezifikation korrekt ist.

Das Problem ist jedoch, dass es keine triviale Aufgabe ist, die "passende" Spezifikation zu erhalten, und es gibt keine 100% korrekte Methode (soweit ich weiß), um die richtige zu finden. Es handelt sich nur um eine Schätzung Nehmen Sie ein Prädikat als Spezifikation, nur weil es wie das "Eine" "aussieht. Warum nehmen Sie das Programm nicht als korrekt, nur weil es korrekt" aussieht "?


2
Da die Spezifikation hoffentlich weniger kompliziert ist als das Programm, werden weniger Fehler auftreten als beim Programm.
user253751

1
Beachten Sie, dass ein Typsystem eine Form der Spezifikation ist - wir können es verwenden, um einige nicht triviale Eigenschaften von Programmen zu beweisen. Je reicher das Typensystem ist, desto stärker sind die Eigenschaften, die wir nachweisen können.
Gardenhead

@immibis, aber wenn es nur einen Fehler hat, ist das Ganze falsch
Maykel Jakson

@MaykelJakson Richtig ... Ich habe einmal versehentlich einen Widerspruch in ein Axiom in Rodin gesetzt (was ich versuchte, war korrekt, aber die Syntax war falsch). Es dauerte eine Weile, bis ich bemerkte, dass "hmm, der Auto-Prover scheint gerade ungewöhnlich gut zu funktionieren".
user253751

Antworten:


30

Zuallererst haben Sie vollkommen recht: Sie haben ein echtes Problem. Die formale Verifizierung überträgt das Problem des Vertrauens in die Programmkorrektheit auf das Problem des Vertrauens in die Spezifikationskorrektheit.

Es gibt jedoch mehrere Gründe, warum dieser Prozess immer noch nützlich sein kann.

  1. Spezifikationen sind oft einfacher als der Code selbst. Betrachten Sie beispielsweise das Problem des Sortierens eines Arrays von Ganzzahlen. Es gibt ziemlich ausgefeilte Sortieralgorithmen, die clevere Dinge tun, um die Leistung zu verbessern. Die Angabe ist jedoch recht einfach: Die Ausgabe muss in aufsteigender Reihenfolge erfolgen und eine Permutation der Eingabe sein. Somit ist es wohl einfacher, Vertrauen in die Richtigkeit der Spezifikation zu gewinnen als in die Richtigkeit des Codes selbst.

  2. Es gibt keine einzige Fehlerquelle. Angenommen, eine Person schreibt eine Spezifikation auf und eine andere Person schreibt den Quellcode und überprüft dann formell, ob der Code der Spezifikation entspricht. Dann müsste jeder unentdeckte Fehler sowohl in der Spezifikation als auch im Code vorhanden sein. In einigen Fällen ist dies bei einigen Arten von Fehlern weniger wahrscheinlich: Es ist weniger wahrscheinlich, dass Sie den Fehler bei der Prüfung der Spezifikation und den Fehler bei der Prüfung des Quellcodes übersehen. Nicht alle, aber einige.

  3. Teilspezifikationen können wesentlich einfacher sein als der Code. Betrachten Sie beispielsweise die Anforderung, dass das Programm frei von Pufferüberlauf-Schwachstellen ist. Oder die Anforderung, dass es keine außerhalb der Grenzen liegenden Fehler für den Arrayindex gibt. Dies ist eine einfache Spezifikation, die ziemlich offensichtlich eine gute und nützliche Sache ist, um beweisen zu können. Jetzt können Sie mit formalen Methoden nachweisen, dass das gesamte Programm dieser Spezifikation entspricht. Das mag eine ziemlich komplizierte Aufgabe sein, aber wenn Sie erfolgreich sind, gewinnen Sie mehr Vertrauen in das Programm.

  4. Die Spezifikationen ändern sich möglicherweise weniger häufig als der Code. Ohne formale Methoden müssen wir jedes Mal, wenn wir den Quellcode aktualisieren, manuell überprüfen, dass das Update keine Fehler oder Mängel verursacht. Formale Methoden können diese Belastung möglicherweise verringern: Angenommen, die Spezifikation ändert sich nicht, sodass Softwareupdates nur Änderungen am Code und keine Änderungen an der Spezifikation beinhalten. Dann müssen Sie bei jedem Update nicht mehr prüfen, ob die Spezifikation noch korrekt ist (sie hat sich nicht geändert, es besteht also kein Risiko, dass neue Fehler in die Spezifikation aufgenommen wurden), und Sie müssen nicht mehr prüfen, ob der Code noch vorhanden ist richtig (der Programmverifizierer prüft das für Sie). Sie müssen immer noch prüfen, ob die ursprüngliche Spezifikation korrekt ist, müssen sie jedoch nicht jedes Mal erneut prüfen, wenn ein Entwickler einen neuen Patch / ein neues Update / eine neue Änderung vornimmt.

Denken Sie schließlich daran, dass Spezifikationen in der Regel deklarativ sind und nicht unbedingt ausgeführt oder direkt zu Code kompiliert werden können. Überlegen Sie beispielsweise, ob Sie noch einmal sortieren möchten: Die Spezifikation besagt, dass die Ausgabe zunimmt und eine Permutation der Eingabe ist, aber es gibt keine offensichtliche Möglichkeit, diese Spezifikation direkt auszuführen, und keine offensichtliche Möglichkeit für einen Compiler, sie automatisch zu Code zu kompilieren. Es kommt also nicht in Frage, nur die Spezifikation als korrekt zu betrachten und sie häufig auszuführen.

Trotzdem bleibt das Fazit unverändert: Formale Methoden sind kein Allheilmittel. Sie übertragen einfach das (sehr harte) Problem des Vertrauens in die Code-Korrektheit auf das (nur harte) Problem des Vertrauens in die Spezifikations-Korrektheit. Fehler in der Spezifikation sind ein echtes Risiko, sie sind weit verbreitet und können nicht übersehen werden. In der Tat teilt die Community der formalen Methoden das Problem manchmal in zwei Teile: Bei der Überprüfung geht es darum, sicherzustellen, dass der Code der Spezifikation entspricht; Bei der Validierung geht es darum, sicherzustellen, dass die Spezifikation korrekt ist (unseren Anforderungen entspricht).

Vielleicht haben Sie auch Spaß an der formalen Programmüberprüfung in der Praxis und warum wird nicht mehr nach Kompilierungszeitgarantien gesucht? für mehr perspektiven mit einigem einfluss darauf.


Im Übrigen steigt mit zunehmender Detaillierung einer Spezifikation die Wahrscheinlichkeit, dass sie als Pseudocode geschrieben werden kann. Bei Verwendung Ihres Sortierbeispiels ist eine detailliertere Version von "Die Ausgabe muss in aufsteigender Reihenfolge erfolgen" "Jede Ganzzahl in der Ausgabe muss nach der ersten größer als die vorherige Zahl sein". Dies kann wiederum leicht als etwas wie for each integer I<sub> N</ sub> in set S (where N > 1) { assert I<sub> N</ sub> > I< / sub> <sub> N - 1</ sub> geschrieben werden }. Nicht 100% sicher über die Notation.
Justin Time - Reinstate Monica

Eine gute Spezifikation kann also auch dazu beitragen, den Code zu erstellen und nicht nur zu überprüfen.
Justin Time - Reinstate Monica

1
Die offensichtliche Art, die Sortierspezifikation auszuführen, besteht darin, alle Permutationen der Eingabe aufzulisten und die bestellte auszuwählen. Das Problem mit dieser sollte allerdings sein, klar ...
Derek Elkins links SE

19

Die Antwort von DW ist großartig , aber ich möchte auf einen Punkt eingehen. Eine Spezifikation ist nicht nur eine Referenz, anhand derer der Code überprüft wird. Einer der Gründe für eine formale Spezifikation ist die Validierung durch den Nachweis einiger grundlegender Eigenschaften. Natürlich kann die Spezifikation nicht vollständig validiert werden - die Validierung wäre so komplex wie die Spezifikation selbst und daher ein endloser Prozess. Durch die Validierung erhalten wir jedoch eine stärkere Garantie für einige kritische Eigenschaften.

Angenommen, Sie entwerfen einen Autopiloten. Dies ist eine ziemlich komplexe Sache, die viele Parameter beinhaltet. Zu den Korrektheitseigenschaften des Autopiloten gehören Dinge wie "Das Auto stößt nicht gegen eine Wand" und "Das Auto fährt dahin, wo es sein soll". Eine Eigenschaft wie „das Auto stößt nicht gegen eine Wand“ ist wirklich sehr wichtig, deshalb möchten wir das gerne beweisen. Da das System in der physischen Welt betrieben wird, müssen Sie einige physische Einschränkungen hinzufügen. Die eigentliche Eigenschaft des Rechensystems wird so etwas wie "Unter diesen Annahmen in Bezug auf die Materialwissenschaften und diesen Annahmen in Bezug auf die Wahrnehmung von Hindernissen durch die Sensoren des Autos wird das Auto nicht gegen eine Wand prallen" sein. Das Ergebnis ist jedoch eine relativ einfache Eigenschaft, die eindeutig wünschenswert ist.

Könnten Sie diese Eigenschaft aus dem Code beweisen? Letztendlich ist es das, was passiert, wenn Sie einen vollständig formalen Ansatz verfolgen¹. Aber der Code hat viele verschiedene Teile; Die Bremsen, die Kameras, der Motor usw. werden alle autonom gesteuert. Eine Korrektheitseigenschaft der Bremsen wäre so etwas wie "Wenn das Signal" Bremsen betätigen "eingeschaltet ist, werden die Bremsen betätigt". Eine Korrektheitseigenschaft des Motors wäre "Wenn das Kupplungssignal ausgeschaltet ist, treibt der Motor die Räder nicht an". Es bedarf einer sehr hochrangigen Sichtweise, um sie alle zusammenzufügen. Durch eine Spezifikation werden Zwischenschichten erstellt, in denen die verschiedenen Komponenten des Systems miteinander verknüpft werden können.

Tatsächlich würde ein so komplexes System wie ein Autopilot mehrere Spezifikationsebenen mit unterschiedlichen Verfeinerungen aufweisen. Bei der Konstruktion wird häufig ein Verfeinerungsansatz verwendet: Beginnen Sie mit einigen Eigenschaften auf hohem Niveau wie „das Auto stößt nicht gegen eine Wand“, und stellen Sie dann fest, dass hierfür Sensoren und Bremsen erforderlich sind, und ermitteln Sie einige grundlegende Anforderungen an die Sensoren, die Bremsen und die Pilotsoftware, dann verfeinern Sie diese grundlegenden Anforderungen erneut in einem Design der Komponente (für den Sensor benötige ich ein Radar, einen DSP, eine Bildverarbeitungsbibliothek usw.). In einem formalen Entwicklungsprozess Jede Spezifikationsebene erfüllt nachweislich die Anforderungen der darüber liegenden Ebene, von den Eigenschaften der höchsten Ebene bis hin zum Code.

Es ist unmöglich, sicher zu sein, dass die Spezifikation korrekt ist. Wenn Sie beispielsweise die Physik falsch verstanden haben, sind die Bremsen möglicherweise nicht wirksam, obwohl die Mathematik, die den Bremsencode mit den formalen Anforderungen in Beziehung setzt, korrekt ist. Es ist nicht gut zu beweisen, dass die Pausen bei 500 kg Belastung effektiv sind, wenn Sie tatsächlich 5000 kg haben. Aber es ist einfacher zu erkennen, dass 500 kg falsch sind, als im Code der Bremsen zu erkennen, dass sie für die physikalischen Parameter des Autos nicht gut genug sind.

¹ Das Gegenteil eines vollständig formalen Ansatzes ist „Ich denke, das funktioniert, aber ich kann nicht sicher sein“. Wenn du dein Leben darauf setzt, scheint das nicht so toll zu sein.


Ist es möglich, nur eine Eigenschaft meines Codes zu beweisen und sicherzustellen, dass sie immer korrekt ist? Zum Beispiel möchte ich nur beweisen, dass der Index eines Arrays niemals außerhalb des Bereichs des Arrays liegt, und das interessiert mich nicht das andere Zeug?
Maykel Jakson

5
@MaykelJakson Sicher! Verwenden Sie das einfach als Ihre Spezifikation. Es ist wahrscheinlich eine schwache Spezifikation, aber nichts hindert Sie daran, dies zu beweisen und formale Methoden anzuwenden, um dies zu beweisen.
Chi
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.