Vom Lügen und vom Raten
oder
Corriger la fortune

Als Kind habe ich mich für Bücher begeistert, in denen Roboter menschliches Verhalten zeigen. Dort wurde immer wieder die nicht näher erläuterte Behauptung aufgestellt, Roboter könnten nicht lügen. Auch wenn ich die narrative Freiheit der Autoren nie angezweifelt habe, fragte ich mich dennoch: Wieso eigentlich?

Sehr viel später habe ich erfahren, daß meine Skepsis berechtigt war. Sobald ein Automat über eine Seele, einen Charakter, einen freien Willen oder was sonst noch zu dem gehören mag, was man als Persönlichkeit bezeichnet, verfügt, ist kein Grund einzusehen, warum er nicht aus freien Stücken die Unwahrheit sagen sollte. Solange er (wie alle Computer, die wir heute kennen) strikt an die Anweisungen gebunden ist, die in ihn einprogrammiert sind, ist erst recht nicht zu verstehen, warum er nicht genausogut falsche wie richtige Auskünfte geben sollte. Im Gegenteil: Stellen Sie sich vor, Sie sollten eine Taschenrechnersimulation programmieren. Es sollte wesentlich leichter sein, eine solche zu erstellen, wenn die Ergebnisse falsch sein dürfen oder sollen1.

Es gibt allerdings durchaus etwas, wozu Automaten - ob mit oder ohne Persönlichkeit - nicht imstande sind, nämlich die Generierung zufälliger Werte sowie solcher, die eine zufällige Komponente beinhalten (also geratener oder geschätzter Werte). Der Grund dafür ist zumindestens in vereinfachter Darstellung leicht einzusehen und ganz und gar unmysteriös: Maschinen sind bauartbedingt nur für das fehlerfreie Abarbeiten vorgegebener Arbeitsroutinen vorgesehen. Solange die Forderung 'fehlerfrei' erfüllt ist, sind die Ergebnisse vorhersehbar und deshalb nicht zufällig. Sobald diese Forderung nicht mehr erfüllt ist, wird die Maschine unzuverlässig.

Der Verzicht auf Zufallszahlen in der Programmierung ist nicht hinnehmbar; sowohl die Simulation naturwissenschaftlicher Modelle als auch Computerspiele - um nur zwei Beispiele zu nennen - benötigen zwingend zufällige Werte. Die Informatik reagiert mit ihrer Standardmethode, wenn das gewünschte nicht erreichbar ist, indem sie sich statt des gewünschten einer anderen Sache bedient, die für den vorliegenden Anwendungsfall hinreichend Ähnlichkeiten mit dem gewünschten aufweist. Das bedeutet hier: Statt echter Zufallszahlen werden zwar nicht zufällige, aber immerhin unvorhersehbare Werte verwendet (in der Regel als Pseudozufallszahlen bezeichnet).

Es gibt zwei gängige Methoden, Pseudozufallszahlen zu erzeugen:

Die erste Methode ist nicht so unsinnig, wie es auf den ersten Blick erscheint. Ihr Vorteil besteht darin, zufällig wirkende Ergebnisse2 liefern zu können; ihr Nachteil besteht in der Deckungsgleichheit der Werte bei wiederholter Anwendung.

Die zweite Methode ist effektiv, aber nicht immer unbedenklich. Wollen Sie beispielsweise auf Knopfdruck des Nutzers hin eine Zahl zwischen 0 und 999 erhalten, könnten Sie einfach diese Zahl mit dem Millisekundenwert der aktuellen Uhrzeit belegen. Dieser Wert ist zwar alles andere als zufällig, aber unter der Annahme, daß der Nutzer den Zeitpunkt des Knopfdrucks nicht bewußt wählt (jedenfalls nicht mit einer Präzision im Millisekundenbereich), wird im vorliegenden Anwendungsfall der Millisekundenwert die gleichen Eigenschaften wie eine zufällig ausgewählte Zahl haben.
Die Schwierigkeit besteht in der Synchronisation der Wertebereiche. Hätten Sie im vorangegangenen Beispiel eine Zahl zwischen 0 und 666 erhalten wollen, wäre ein naheliegender Vorschlag, den Zufallswert als Millisekunden % 667 (allgemeiner gesprochen: Millisekunden geteilt durch Höchstwert, davon der Rest) zu berechnen. Das genügt der Aufgabenstellung insofern, daß der ermittelte Wert a) unvorhersehbar ist, b) innerhalb des Wertebereichs liegt und c) alle Werte des Wertebereichs möglich sind. Dennoch ist die Vorgehensweise suboptimal, da statistisch gesehen die untere Hälfte des Wertebereichs doppelt so häufig3 ausgewählt werden wird wie die obere.

Meist werden beide Möglichkeiten miteinander verbunden: Eine Zeitangabe regelt Einstiegspunkt und Art, wie die Liste der Zufallszahlen durchschritten wird. Moderne Skriptsprachen erledigen das automatisch für Sie, da ihre Erfinder die (durchaus plausible) Ansicht vertreten, Zufallswerte sollten grundsätzlich abwechslungsreich sein. Bei maschinennahen Sprachen müssen Sie das selbst erledigen - haben dafür aber die Möglichkeit, den Einstiegspunkt den individuellen Bedürfnissen anzupassen.

→ Initialisierung von Zufallswerten in C

In C beispielsweise werden durch [int] rand(void); aus der Bibliothek <stdlib.h> Pseudozufallszahlen erzeugt; ohne weitere Angaben allerdings immer die gleichen. Den Einstiegspunkt in die Liste kann man durch [void] srand(unsigned int);, ebenfalls aus <stdlib.h> festlegen, die Zeitangabe erhalten Sie durch [time_t] time(time_t *); aus <time.h>.
Wenn Sie einen reinen C - Compiler Ihr eigen nennen, sollte srand(time(NULL)); zu Beginn der main - Funktion genügen; falls Ihr Compiler sich eher für C++ zuständig fühlt und dementsprechend pingelig bezogen auf Typsicherheit ist, sollten Sie ihn nicht verärgern und etwas wie das folgende schreiben:



Codebeispiel in C

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

int main(int argc, char** argv) {
    int i;
    int limit = 10;
    
    time_t t;
    time(&t);
    srand((unsigned int) t);
    
    printf( "\nRandom numbers between 0 and %d:\n", limit );
    for ( i = 0; i < limit; i++ ) {
        //time(&t);
        //srand((unsigned int) t);
        printf( "%d \t", (rand() % limit) );
    }

    return (EXIT_SUCCESS);
}
                        

Ein häufig begangener Fehler besteht darin, sozusagen zur Sicherheit vor jeder Generierung einer Pseudozufallszahl die Initialisierung erneut aufzurufen. De facto bewirkt das das Gegenteil des gewünschten: Liegen die Zeitpunkte der Generierung weit genug auseinander, verschenken Sie lediglich die Vorteile der Listennutzung; liegen sie dicht beieinander wie in obigem Beispiel, erhalten Sie nur eine einzige Pseudozufallszahl (und die immer wieder). Die beiden orangefarbenen Zeilen dürfen also auf keinen Fall auskommentiert werden.

→ Zufällige Reihenfolge feststehender Werte

Manchmal soll eine Sammlung mit Werten von x bis y in zufälliger Reihenfolge befüllt werden, wobei jeder der Werte genau einmal vorkommen soll. Man fasse das als Variation der Aufgabe Schüttle eine Sammlung gründlich durch auf (eventuell verbunden mit der Erstellung der Ursprungsmenge). Als naheliegender Ansatz erscheint, jeweils eine Pseudozufallszahl zu ermitteln, sie, falls sie noch nicht vorhanden ist, hinzuzufügen, falls doch, eine neue zu ermitteln. Diese Herangehensweise ist unbedingt zu vermeiden. Folgender Codeschnipsel, um die Konzentration nicht durch formelle Dinge abzulenken in Javascript geschrieben, soll das demonstrieren:



Codebeispiel in Javascript

                        
/**
 * AVOID THIS
 */

function contains_element (the_array, the_element) {
    for ( var i = 0; i < the_array.length; i++ ) {
        element_containing_proove++;
        if ( the_array[i] == the_element )
            return 1;
    }
    return 0;
}

var collection = [];
var LIMIT = 100;
var i, random_value;
var element_containing_proove = 0;
var used_random_values = 0;

for ( i = 0; i < LIMIT; i++ ) {
    do {
        used_random_values++;
        random_value = Math.floor(Math.random() * LIMIT);
    }
    while ( contains_element(collection, random_value) );
    collection[collection.length] = random_value;
}

console.log( "element_containing_proove: " + element_containing_proove );
console.log( "used_random_values: " + used_random_values );
                        

Je voller die Zielsammlung wird, desto mehr nutzlose Pseudozufallszahlen müssen erzeugt und wieder verworfen werden. Das wirkt sich umso stärker aus, wenn die angestrebte Zielsammlung groß ist.

Es gibt zwei sinnvolle Methoden, diese Aufgabe zu lösen; welche zu bevorzugen ist, hängt davon ab, ob die Sammlung eher Eigenschaften einer Liste oder eines Feldes besitzt.
Eine Liste ist eine Sammlung von Elementen, bei denen jedes außer seinem eigentlichen Wert noch einen Verweis auf das folgende Element4 hat. Listen sind unpraktisch, wenn man ein Element mit einer bestimmten Ordnungszahl herausgreifen will, da man immer die ganze Liste abschreiten muß; dagegen ist es sehr einfach, die Verknüpfungen der Liste zu ändern (wodurch man beispielsweise ein einzelnes Element löschen kann oder einen Teil der Liste abkoppeln und wie beim Mischen von Spielkarten hinten anhängen kann).
Ein Feld (mittlerweile auch im deutschsprachigen Raum wohl besser unter dem Namen Array bekannt) ist eine Sammlung von Elementen, die jeweils unter einer Ordnungszahl5 abgespeichert sind und über diese direkt angesprochen werden können. Der Preis dafür sind aufwendige Operationen, um die Elemente innerhalb der Liste zu verschieben.

Das Durchschütteln einer Liste erfolgt nach dem Muster

Das Durchschütteln eines Feldes erfolgt nach dem Muster

In Javascript haben Arrays sowohl Feld- als auch Listeneigenschaften, deshalb ist Javascript sehr gut geeignet zur Gegenüberstellung beider Methoden:



var source = [];
var target = [];


var LIMIT = 10;
var i, random_index;

for ( i = 0; i < LIMIT; i++ )
    source.push(i);

while ( source.length ) {
    random_index = Math.floor(Math.random()
        * source.length);
    target.push(source[random_index]);
    source.splice(random_index, 1);
}

console.log( target );
                        
                        
var source = [];

var LIMIT = 10;
var i, random_index, tmp;

for ( i = 0; i < LIMIT; i++ )
    source[source.length] = i;

for ( i = 0; i < LIMIT; i++ ) {
    random_index = Math.floor(Math.random()
        * LIMIT);
    if ( i == random_index )
        continue;
    tmp = source[i];
    source[i] = source[random_index];
    source[random_index] = tmp;
}

console.log( source );
                        


1 Ob in der Moralphilosophie und Ethik das Äußern der Unwahrheit stets als lügen anzusehen ist, ist nicht Gegenstand dieser Betrachtung. Wenn das Äußern der Unwahrheit seitens einer Maschine als lügen gilt, ist die Behauptung, Roboter könnten nicht lügen, falsch. Wenn das Äußern der Unwahrheit seitens einer Maschine nicht als lügen anzusehen ist, ist die Behauptung, Roboter könnten nicht lügen, trivial und damit belanglos, da das Unterscheidungskriterium zwischen lügen und nicht lügen nicht gegeben ist.
Interessante Betrachtungen zum Thema Können Roboter lügen? finden sich im gleichnamigen Artikel von Raúl Rojas; dort liegt allerdings das Augenmerk auf dem Erfolg einer durch Lügen beabsichtigten Täuschung.  ↑ 

2 Damit soll vermieden werden, daß die Ergebnisse 'zufällig' einem der seltenen Glücks- oder Unglücksfälle entsprechen. Wenn Sie beispielsweise einen zufälligen Wurf mit drei Würfeln demonstrieren wollen, werden drei identische Zahlen nicht Ihr angestrebtes Ziel sein; in der Realität kommt das aber durchschnittlich bei jedem sechsunddreißigsten mal vor. Methoden, 'zufälligere' Werte als bei echtem Zufall zu erzeugen, werden im weiteren Text noch eine Rolle spielen.  ↑ 

3 Wenn die Uhrzeit einen Wert aus den unteren zwei Dritteln annimmt, wird sie unverändert zum Ergebnis. Das letzte Drittel, das außerhalb des gewünschten Wertebereichs ist, fängt jedoch wieder von vorn zu zählen an. Derartige unerwünschte Effekte fallen in der Regel erst bei einer großen Anzahl an Wiederholungen auf, nämlich dann, wenn man sich sicher sein kann, daß trotz aller Zufälligkeit die Gesetze der Wahrscheinlichkeitsrechnung in den Ergebnissen erkennbar sein müßten.  ↑ 

4 Oder auf das vorangehende, oder auf beide... die Elemente, aus denen B-Bäume und T-Bäume gebildet werden, sogar auf noch mehr.  ↑ 

5 Sogenannte assoziative Arrays, eine der Zaubermöglichkeiten von PHP, bei denen die einzelnen Elemente statt mit einer Ordnungszahl über eine Zeichenkette angesprochen werden können, sind ein Sonderfall. Für den hier ausgeführten Gedanken bleibt aber der Aspekt der direkten Ansprache relevant.  ↑