Der Einsatz evolutionärer Computermodelle bei der Untersuchung historischer und politischer Fragestellungen

Eckhart Arnold

1 Einleitung
2 Evolutionäre Erklärungen
3 Computermodelle zur Simulation evolutionärer Vorgänge
    3.1 Ein Beispiel: Die Simulation des iterierten Gefangenendilemmas
        3.1.1 Die Implementation des Computerturniers
        3.1.2 Die Ergebnisse des Computerturniers
        3.1.3 Iteriertes Gefangenendilemma mit variablen Auszahlungen: Lohnt sich die Heiratsschwindler-Strategie?
    3.2 Erweiterung zur populationsdynamischen Simulation
    3.3 Möglichkeiten und Grenzen von Computermodellen bei der Untersuchung evolutionärer Prozesse
4 Beispiele für evolutionäre Erklärungsansätze im Bereich der Kulturwissenschaften
5 Zitierte Literatur
6 Anhang: Programmcode des Computerturniers

3.1.1 Die Implementation des Computerturniers

Insgesamt nahmen 12 unterschiedliche Strategien an dem hier beschriebenen Computerturnier teil. Es lohnt sich nicht, alle Strategien einzeln zu beschreiben, zumal ihre Namen meist selbsterklärend sind (Random, Tit for Tat, Always friendly). Die drei Strategien GraciousTFT, Tester und Analyst sollen jedoch mit Hilfe von Quellcodebeispielen etwas näher erläutert werden. Die Beispiele aus dem Programmcode sind in der Programmiersprache Python geschrieben, einer Interpretersprache, die sich wegen ihrer Einfachheit und der guten Lesbarkeit des Programmcodes für solche Aufgaben empfiehlt.

Die Strategie GraciousTFT ist eine Variante von Tit for Tat, die jedoch längere Folgen gegenseitiger Bestrafungen erkennt und durch ein Friedensangebot zu beendigen versucht. Damit beseitigt GraciousTFT eine Schwäche, die Tit for Tat im Zusammentreffen mit böswilligen Varianten des eigenen Typs aufweist. Im folgenden Programmausschnitt steht ein Rückgabewert von 1 für Kooperation und ein Rückgabewert von 0 für eine Defektion.

    def nextMove(self, round, myMoves, opMoves):
        if round == 1:
            return 1                                      # start friendly
        elif round > 6 and ((opMoves[-5:] == [0,0,0,0,0] and \
                             myMoves[-5:] == [0,0,0,0,0]) or \
                            (opMoves[-5:] == [0,1,0,1,0] and \
                             myMoves[-5:] == [1,0,1,0,1])):
            return 1                                      # peace offer
        else:
            if opMoves[-1] == 1: return 1                 # play tit for tat
            else:                return 0

Die Strategie Tester ist dem Buch von Axelrod entnommen. Sie versucht zunächst durch eine mutwillige Defektion festzustellen, ob sich der Gegner ausnutzen lässt. Wenn ja, dann defektiert sie bei jedem zweiten Zug. Wenn nicht, spielt sie Tit for Tat.

    def nextMove(self, round, myMoves, opMoves):
        if round <= 2:
            return 0                                      
        elif round == 3:
            if opMoves[-1] == 0:  self.state = "TFT"
            else:                 self.state = "Deceiver"
            return 1
        elif round == 4:
            return 1
        else:
            if self.state == "TFT":
                if opMoves[-1] == 1:  return 1
                else:                 return 0
            else:
                if round % 2 == 1:    return 0
                else:                 return 1


Analyst schließlich analysiert jeweils die zehn letzten Spielzüge, um festzustellen, ob die gegnerische Strategie ausnutzbar oder böswillig ist. Erweist sie sich als ausnutzbar, so defektiert Analyst. Ist der Gegner böswillig, dann wehrt sich Analyst ebenfalls durch Defektion. Wenn beides nicht eindeutig feststellbar ist, dann spielt Analyst Tit for Tat. Die ersten zehn Züge werden zufällig gewählt.

    def nextMove(self, round, myMoves, opMoves):
        if round <= 10:
            return whrandom.randint(0, 1)   # play random at the beginning
        else:

            # analyse
            
            ex_attempt, ex_success = 0,0
            opex_opportunity, opex_attempt = 0, 0
            
            i = -9
            while i <= -1:
                if myMoves[i-1] == 0:
                    ex_attempt += 1
                    if opMoves[i] != 0: ex_success += 1 # opponent did
                                                        # not punish exploit!
                else:
                    opex_opportunity += 1
                    if opMoves[i] == 0: opex_attempt += 1  # opponnent played 
                                                           # defective without
                                                           # reason
                i += 1

            # and react accordingly

            ret = -1
            if (ex_attempt > 0):
                if (float(ex_success) / float(ex_attempt)) >= 0.6:
                    return 0                    # keep exploiting
                else: ret = 1                   # try to be friendly again

            if opex_opportunity > 0:
                if (float(opex_attempt) / float(opex_opportunity)) <= 0.4:
                    return 1            # opponent isn't really bad
                else:
                    return 0            # opponent tried to deceive to often
            else:
                if ret != -1: return ret      # fallback
                else:
                    if opMoves[-1] == 1: return 1   # play TFT if clueless
                    else:                return 0

t g+ f @