Im Mai 1833 wurde sie mit achtzehn Jahren am Hof vorgestellt. Einen Monat später lernte sie bereits ihren zukünftigen Kollegen Charles Babbage kennen. Sie zeigte reges Interesse an seiner "Difference engine".
1835 heiratete sie Lord William King und wurde in den folgenden Jahren sehr in ihren Haushalt eingespannt. 1836 gebar sie Byron Noel, 1837 Anne Isabella und 1839 Ralph Gordon. Glücklicherweise hatte Ada (wie fast jede Adelsdame) eine Haushälterin, die ihr die Arbeit erleichterte.
Über ihr Bild (1835) sagte Ada, es sei nicht ganz originalgetreu; es zeige mehr weibliche Züge, als sie hat, und unterdrücke die Erscheinung der Mathematikerin in ihr.
1838 bekam Adas Mann den Titel "First Earl of Lovelace" verliehen. In der damaligen Zeit war es verpönt, adelige Frauen arbeiten zu lassen, und dementsprechend hatten Frauen keinen Zutritt zu wissenschaftlichen Instituten und Bibliotheken. Als "First Earl of Lovelace" konnte William für seine Frau die gewünschten mathematischen Werke aus den Bibliotheken holen. Da es auch nicht schicklich galt, als Begleitung des Mannes in die Bibliothek zu kommen, blieb Ada zu Hause.
Ab 1840 arbeitete Ada mit Charles Babbage zusammen, der mechanische Rechenmaschinen entwarf. 1842/43 entstand dann Adas Lebenswerk: Die "Übersetzung und Anmerkungen zu L. F. Menabreas Erklärungen zur Analytical Engine", deren Anmerkungen fast den dreifachen Umfang der Übersetzung selbst erreichten. In diesen Anmerkungen finden sich erstmals die Ideen der Programmiersprachenkonstrukte.
Später wurde Ada zunehmend kränklich, fand Gefallen an Pferderennen und Wetten und machte viel Schulden. Mit fast 35 Jahren starb Ada am 27. November 1850.
Die Difference Engine konnte nur diese Funktion berechnen, und Charles Babbage kam bald auf die Idee, eine Maschine zu bauen, die alle Algorithmen berechnen können sollte. Diese nannte er Analytical Engine. Auch diese arbeitet komplett mechanisch.
Ein Papierdrucker kann das endgültige Ergebnis der Berechnungen auf Papier bannen.
Verglichen mit heutigen Rechnern kann man sagen: Die Mühle ist der Prozessor, die Säulen das RAM, das Adreßwerk der Bus mit der Adreßlogik des RAM, der Kartenleser der Programmspeicher, der Kartendrucker kann Tabellen schreiben, das Regal mit den vorbereiteten Kartenstapeln ist die Funktionsbibliothek, der Papierdrucker die Ausgabe. Dies ist aber nur ein grober Vergleich, der aufgrund mancher Konzeptionsunterschiede keiner eingehenden Prüfung standhält. Wer will, darf auch die Analytical Engine mit einem heutigen Prozessor statt Computer vergleichen, da sich Teile der Rechnerarchitektur im Prozessor wiederspiegeln. (Rechenwerk, Register, Registeradressierung, ...)
Um die Analytical Engine komplett programmieren zu können, sind also folgende Programmkarten notwendig:
- Supplying Cards: Diese Karten dienen dazu, den Inhalt einer Säule auf die Mühle zu transferieren. In einem normalen Programm werden zwei Supplying Cards pro Operation benötigt. Liest der Kartenleser eine Supplying Card, so macht das Adreßwerk die gefragte Säule ausfindig und liest deren Inhalt aus, indem er die Scheiben der gefragten Säule auf die Null-Position dreht und in der Mühle die Scheiben einer Mühlen-internen Säule in der anderen Richtung um ebensoviele Ziffern dreht.
Sobald die zweite Supplying Card eingelesen wurde, wird die Mühle gestartet.
Es drängt sich dem Mitdenker ein offensichtliches Problem auf: Eine Säule wird ausgelesen, indem sie gelöscht wird, und die Variable ist anschließend nicht mehr auf der Säule vorhanden. In den meisten Algorithmen werden aber viele Variablen mehrfach verwendet, und um dieses Problem zu lösen, gibt es zwei Arten von Supplying Cards:
1. nicht-rückschreibende Supplying Cards: Diese arbeiten wie eben beschrieben.
2. rückschreibende Supplying Cards: Diese weisen das Adreßwerk an, die gelesene Variable nach dem Auslesen wieder auf die Säule zurückzuschreiben. Somit bleibt die Variable auf der Säule erhalten, aber das Adreßwerk benötigt etwas mehr Zeit, bis es fertig ist.
- Receiving Cards: Diese Karten enthalten wie die Supplying Cards die Adresse einer Säule und weisen das Adreßwerk an, das Ergebnis der Mühlenoperation auf ebendiese Säule zu schreiben. Manchmal muß nach einer Operation das Ergebnis auf mehrere Säulen geschrieben werden, weil das Ergebnis dieser Operation im weiteren Programm auf verschiedene Weise weiter bearbeitet wird. In solch einem Fall folgen dann mehrere Receiving Cards aufeinander. Dies ist die schnellste Möglichkeit der Vervielfältigung einer Variable; alles andere ist wesentlich langsamer.
Wird also in einer Berechnung beispielsweise der Logarithmus von 5 benötigt, so läutet eine Glocke an der Analytical Engine, und ein sichtbares Rad (mit Funktionsnamen) und eine sichtbare Säule zeigen dem Operator an, daß der Logarithmus von 5 benötigt wird. Der Operator sucht dauraufhin die Tabellenkarte aus dem Regal, die den Logarithmus von 5 enthält, und legt sie in die Analytical Engine ein.
Eine Tabellenkarte enthält drei Daten: Die Funktion der Karte (zB ln), die gefragte Zahl (zB 5) und den Tabelleneintrag (zB ln 5 = 1,6094379). Somit kann die Analytical Engine nachprüfen, ob der Operator sich versehentlich vergriffen hat, und bei Bedarf ein Warnglöckchen anschlagen und die Anfrage wiederholen.
Man sieht: Es wurden alle möglichen Fehlerquellen ausgeschaltet. Wenn der Algorithmus fehlerlos programmiert wurde, dann kann man sich hundertprozentig auf die Richtigkeit der Ergebnisse der Berechnungen verlassen.
In der Konstruktionszeichnung (Draufsicht!) von Charles Babbage kann man auf der linken Seite das komplizierte Räderwerk der Mühle erkennen. Die Operationen + und - sind sehr einfach, lassen sich mit zwei Säulen realisieren und gehen ziemlich schnell; aber die Operationen * und / mußten auf geschickte Weise mit Hilfe von möglichst wenigen Additionen und Subtraktionen realisiert werden. Die regelmäßig angeordneten Kreise auf der rechten Seite stellen in der Draufsicht die ersten Variablensäulen dar. In der fertig gebauten Analytical Engine hätte es noch viel mehr Säulen gegeben: vermutlich 200. Der lange Stab in der Mitte, der mit Rack betitelt ist, und der große Kreis in der Mitte sind das Adreß- und Steuerwerk. Links oben sieht man eine Operationskarte, rechts oben eine Variablenkarte, rechts unten eine Zahlenkarte. Der Maßstab im linken unteren Eck ist drei Fuß lang (etwa ein Meter). Kartenleser, Drucker usw. sind nicht abgebildet.
Der Sohn von Charles Babbage hat tatsächlich die Mühle der Analytical Engine mit einem Papierdrucker nachgebaut. (Bild) Es fehlen der Kartenleser, die Variablensäulen, das Adreßwerk, das Steuerwerk und der Kartendrucker, und deshalb ist diese Maschine nicht programmierbar und nur sehr eingeschränkt verwendungsfähig. Sie kann +, -, * und / und rechnet nur auf 29 Stellen genau. Sie steht jetzt im Londoner Wissenschaftsmuseum.
Der Sketch of the Analytical Engine wurde also von Ada Augusta, Countess of Lovelace ins Englische übersetzt, mit umfangreichen Kommentaren versehen und mit A. A. L. unterschrieben. Es wäre in der damaligen Zeit nicht gut gewesen, mit voll ausgeschriebenem Namen zu unterschreiben, da das hartnäckige Gerücht kursierte, Frauen seien nicht für wissenschaftliche Arbeit fähig, könnten nur unlogische Gedanken bewerkstelligen und gehörten in den Haushalt. Zu dem Zeitpunkt, als sich herumsprach, wer hinter A. A. L. steckte, hatten sich die Anmerkungen bereits hoher wissenschaftlicher Beliebtheit erfreut, und daher war man nur noch erstaunt, daß eine Frau sie geschrieben hatte, anstatt den Wert herabzusetzen.
Die Kommentare erreichten fast den dreifachen Umfang des ursprünglichen Textes und enthalten:
Um die Arbeit der Ada Lovelace noch verständlicher zu machen, wollen wir hier auf ihr Beispiel von der Berechnung der Bernoullizahlen eingehen.
Da die Analytical Engine keine mathematischen Formeln auflösen kann, muß als Vorarbeit ein Algorithmus entwickelt werden, mit dem die Maschine programmiert werden kann.
(1) ist die Formel für die Bernoulli-Zahlen.
(2) ist eine andere Entwicklung des Bruches x / (ex - 1).
Wenn man (1) durch (2) teilt, ergibt sich (3).
Wenn man das Produkt in (3) ausrechnet, ergibt sich ein Polynom (4),
dessen Koeffizienten Dn immer gleich 0 sind.
Der Inhalt der Koeffizienten D2n ist in (5) genau aufgelistet.
Zusammengefaßt und mit (2n)! multipliziert ergibt sich (6), das sich als
Algorithmus eignet: B2n-1 wird aus dem Rest der Summe ausgerechnet.
Zur namentlichen Vereinfachung der einzelnen Teile dieses Algorithmus siehe (7).
Aufgrund der Formel (6) hat dann Ada ihr Programm entwickelt.
Es sei hier in lesbarer, aber nicht ursprünglicher Form wiedergegeben:
| V1-V3 | Data Variables |
| V4-V13 | Working Variables |
| V21-... | Result Variables |
| Operation | Ergebnis | Löschen | Bemerkung | |
| 0 | 1V1=1; 1V2=2; 1V3=n | (Vorbelegung) | ||
| Block A: | ||||
| 1 | 1V4=1V5=1V6=1V2*1V3 | 2n | 1V6=Zähler=Z | |
| 2 | 2V4=1V4-1V1 | 2n-1 | ||
| 3 | 2V5=1V5+1V1 | 2n+1 | ||
| 4 | 1V11=1V5/2V4 | (2n-1)/(2n+1) | 0V4=0V5=0 | |
| 5 | 2V11=1V11/1V2 | (2n-1)/(2n+1)/2 | ||
| 6 | 1V13=0V13-2V11 | -(2n-1)/(2n+1)/2=A0 | 0V11=0 | |
| 7 | 1V10=1V3-1V1 | n-1=i | ||
| Block B: | ||||
| 8 | 1V7=1V3+0V7 | 2=Nenner | Nenner=N | |
| 9 | 3V11=1V6/1V7 | (2n)/2=A1 | ||
| 10 | 1V12=1V21*2V11 | B1*A1 | ||
| 11 | 2V13=1V12+1V13 | A0+B1*A1=B3 | 0V12=0 | |
| 12 | 2V10=1V10-1V1 | i-- | ||
| Block C: | ||||
| 13 | 2V6=1V6-1V1 | Z-1 | ||
| 14 | 2V7=1V1+1V7 | N+1 | ||
| 15 | 1V8=2V6/2V7 | (Z-1)/(N+1) | ||
| 16 | 4V11=1V8*3V11 | Ak-2*(Z-1)/(N+1)=Ak-1 | 0V8=0 | |
| 17 | 3V6=2V6-1V1 | Z-1 | ||
| 18 | 3V7=1V1+2V7 | N+1 | ||
| 19 | 1V9=3V6/3V7 | (Z-1)/(N+1) | ||
| 20 | 5V11=1V9*4V11 | Ak-1*(Z-1)/(N+1)=Ak | 0V9=0 | |
| 21 | 2V12=1V22*5V11 | Bk*Ak | ||
| 22 | 3V13=2V12+2V13 | Summe(Bk*Ak)=Bk+2 | 0V12=0 | |
| 23 | 3V10=2V10-1V1 | i-- | ||
| Block D: | ||||
| 24 | 1V24=4V13+0V24 | schreibe Ergebnis | ||
| 25 | 1V3=1V1+1V3 | n++ | 0V6=0V7=0 |
Dabei bedeutet aVb eine eindeutige Variable.
b repräsentiert die Säule, auf der aVb gespeichert ist, und da
der Wert einer Säule sich oft ändert und somit nicht die Eindeutigkeit aufweist, die
in der Mathematik notwendig ist, werden mit dem linken oberen Index a die mathematischen Variablen auf einer
Säule eindeutig unterschieden.
0Vb bedeutet eine "leere" Säule und hat immer den Wert Null.
(0Vb=0)
In der Implementation unterscheiden sich die Variablenkarten 3V5
und 7V5 natürlich nicht, da die Variablenkarten nur die gesuchte
Säule 5 adressieren.
Am Anfang des Programmes werden die ersten drei Säulen mit den benötigten Werten (1, 2 und n) initialisiert. n besagt, die wievielte Bernoullizahl berechnet werden soll. 1 und 2 sind Konstanten, die während vom Programm verwendet werden. Die Konstanten werden anfangs auf Säulen initialisiert, weil es nicht vorgesehen ist, Konstantenkarten im laufenden Programm zu verarbeiten.
Block A berechnet A0.
Operation 1 multipliziert 2 (in 1V2) mit n (in 1V3)
und schreibt das Ergebnis mit Hilfe von drei direkt aufeinanderfolgenden
Receiving Cards gleich auf drei Säulen: V4, V5 und V6.
Von V4 wird in Operation 2 die Konstante 1 abgezogen und das Ergebnis wieder
auf dieselbe Säule geschrieben.
Normalerweise werden in Programmen rückschreibende Supplying Cards verwendet,
die zwar etwas langsamer als nicht-rückschreibende Supplying Cards sind,
aber dafür den Inhalt der Säule nicht löschen; in Operation 2 jedoch ist die
erste Supplying Card V4 eine Nicht-Rückschreibende, weil die Säule
mit der Receiving Card V4 sowieso wieder überschrieben wird.
Außerdem ändert sich jetzt der linke obere Index der Variable auf der Säule von
1V4 auf 2V4, da in der Mathematik
2n nicht dasselbe wie 2n-1 ist, und somit die mathematisch eindeutige Identifikation
einen neuen Index verlangt.
In Operation 3 wird zu V5 1 addiert, und in Operation 4 werden die Ergebnisse
aus den beiden vorhergehenden Operationen dividiert.
Nach der Divisions-Operationskarte der Operation 4 folgen also eine nicht-rückschreibende
Supplying Card V4, eine nicht-rückschreibende Supplying Card V5
und eine Receiving Card V11.
Die beiden Supplying Cards sind deshalb nicht-rückschreibend, weil 2n-1 und 2n+1 im
weiteren Programm nicht mehr benötigt werden.
Die Werte auf diesen beiden Säulen werden also im weiteren Ablauf mit
0V4 und 0V5 betitelt, da sie keine
Bedeutung mehr haben.
Operation 5 dividiert 1V11 durch 2, und Operation 6 zieht
1V11 von 0V13 ab (das bisher keinen Wert hatte,
also auf Null steht), wodurch 1V11 mit negativem Vorzeichen in
V13 landet. Dieses entspricht dem A0 unseres Algorithmus.
Operation 7 schreibt n-1 in V10.
Block B berechnet den nächsten Summanden unseres Algorithmus.
Die Addition in Operation 8 kopiert die Konstante 2 in die (vorher leere) Säule V7.
Diese Initialisierung ist für die weitere Vorgehensweise in Block C notwendig.
Die Operationen 9, 10 und 11 berechnen A0+A1*B1,
wobei A1=(2n/2).
B1 wird in Operation 10 aus 1V21 ausgelesen und befindet sich
dort aus einer früheren Berechnung dieser (niedrigeren) Bernoullizahl.
A0+A1*B1 befindet sich anschließend in V13.
Gelöscht wird dabei nur 1V12, da alle anderen in den Säulen
befindlichen Variablen später noch benötigt werden.
In Operation 12 wird V10 dekrementiert.
Block C berechnet daraufhin A3*B3 und addiert es zu V13.
Wenn man sich die Formel (6) der Algorithmusentwicklung
genau betrachtet, dann erkennt man, daß Ak die Anzahl der möglichen k-fachen
Permutationen aus 2n Elementen ist, und in Folge läßt sich diese leicht berechnen, indem
man zu Ak-2 das Produkt (2n-(k-3))/(k-1) * (2n-(k-2))/(k-0) multipliziert.
In den Operationen 13 bis 20 wird dies gemacht. Man erinnere sich, daß bereits V6
mit 2n vorbelegt ist, V7 mit 2 und V11 mit A2=2n/2.
Operation 13 dekrementiert V6, Operation 14 inkrementiert V7,
Operation 15 dividiert diese voneinander und Operation 16 multipliziert das Ergebnis zu V11.
Operationen 17 bis 20 wiederholen genau diese Aktionen bis auf einen Unterschied:
Statt V8 zum Zwischenspeichern des Bruches verwendet Ada V9.
Dieser Unterschied wäre nicht notwendig gewesen, da 1V8 schon nicht
mehr benötigt wird; jedoch hatte Ada sehr viel Wert auf die Deutlichkeit der Eindeutigkeit gelegt.
Bis auf diesen Unterschied sind alle Karten der Operationen 13 bis 16 zu denen der Operationen 17 bis 20
identisch.
Nach Operation 20 steht in V11 der Wert von A3, und Operation 21 und 22 addieren
A3*B3 zur bisherigen Summe in V12.
Operation 23 dekrementiert V10.
In Formel <7> unserer Algorithmusentwicklung kann man ablesen, daß
B2n-1=-nV13.
Ada beachtet jedoch das negative Vorzeichen nicht und kopiert in Operation 24 die Bernoulli-Zahl,
die in V13 steht, als Ergebnis in den nächsten freien Platz der Result-Variables (im gegebenen Beispiel
nach V24.
Wer jedoch B9 berechnen will (n=5), für den ist der Algorithmus noch nicht zu Ende:
Nach der Ausführung des Blockes C muß er noch insgesamt zwei weitere Male den Block C ausführen,
bis er B9 in V13 stehen hat.
Das ist deshalb so einfach möglich, weil Block C lediglich zwei Multiplikanden zu
Ak in V11 multipliziert und Ak*Bk zur bisherigen
Summe in V13 addiert und gleichzeitig darauf achtet, daß die von der nächsten Verwendung des Blockes
C benötigten Werte in V6, V7, V11 und V13 korrekt
enthalten sind.
Zur Berechnung von B9 müssen also der Reihe nach die Blöcke A, B, C, C, C, D durchlaufen werden.
Dabei müssen natürlich B1, B3, B5 und B7 aus
früheren Berechnungen bereits bekannt sein un in den Result Variables V21-... stehen.
Es fällt auf, daß deshalb in Operation 21 bei jedem neuen Durchlauf des Blockes C eine
andere der Result Variables benötigt wird. Bei der Lösung dieses Problemes dachte
Ada Lovelace bereits an relative Adressierung, indem die Position der gefragten Säule durch die Zahl auf
einer anderen Säule indiziert wird; aber mehr findet sich in ihren Ausführungen nicht,
und Charles Babbage hat seine Maschine nie fertig gebaut.
Die ganze Berechnung der Bernoulli-Zahlen sollte natürlich komplett ohne menschliches Eingreifen, 100%ig zuverlässig und extrem schnell berechnet werden, und daher gibt es noch weitere wichtige Elemente in dem Programm:
Die Säule V10 ist ein Zähler, der von n abwärts zählt.
Sobald dieser Zähler auf Null herabgezählt hat, bedeutet das, daß in V13
die errechnete Bernoullizahl B2n-1 steht.
Wird also V10=0 erreicht, so wird an den Anfang des Blockes D verzweigt,
ansonsten wird wieder an den Anfang des Blockes C gesprungen.
Dies ist eine echte Schleife, so wie wir sie heute kennen.
Ada Byron Lovelace hat damals natürlich noch keine Programmsteuerungsanweisungen gekannt,
und daher mußte sie auf die natürliche Sprache ausweichen.
Es hatte bisher kein "if ... then ... else", kein "do ... while", kein "for (i=n; i; i--)" gegeben,
und sie hat diese Sachen in ihrem Programm - wenn auch noch anders (manchmal gar nicht) benannt - eingeführt.
Die Abfragen auf V10==0 existieren jeweils auch am Ende der Blöcke A und B.
Aber auch eine Rekursion ist in dem Programm enthalten:
Operation 25 inkrementiert n in V3 und löscht gleichzeitig den Inhalt von
V6 und V7 mit Hilfe von zwei extra eingefügten nicht-rückschreibenden
Supplying Cards, deren Werte in der Mühle nicht weiterverwendet werden.
Nach Operation 25 (und dem Ausgeben der errechneten Bernoulli-Zahl auf dem Kartendrucker
zum Zwecke der späteren Weiterverwendung)
wird sofort mit Operation 1 (und dem erhöhten n) weitergemacht.
Zur Berechnung einer neuen Bernoullizahl muß unbedingt von vorne begonnen werden, weil
sich ja das n, das überall in der Berechnung vorkommt, ändert.
Das Rezept zur Anwendung des Bernoullizahlen-Programmes ist also ganz einfach:
Das Programm mit n=1 in den Kartenleser einlegen und die Maschine starten.
Und schon rattern (mit linear anwachsender Verzögerung) die Bernoullizahlen aus dem Drucker.
Über die Säulen läßt sich also zusammenfassend sagen:
V1 und V2 enthalten lediglich Konstanten,
V3 indiziert die gerade zu berechnende Bernoullizahl und ist gleichzeitig
ein Anzeiger für die Anzahl der Durchläufe durch das gesamte Programm;
V4 und V5 werden zur Berechnung von A0 benötigt,
V6 und V7 zählen die zur Berechnung von Ak benötigten Zähler
und Nenner der Multiplikatoren durch;
V8 und V9 dienen als Hilfsvariable bei der Produktbildung;
V10 indiziert die Anzahl der noch zu berechnenden Summanden für die aktuelle Bernoullizahl;
V11 enthält Ak,
V12 dient zur Zwischenberechnung von Bk*Ak,
V13 enthält den bisher berechneten Teil der Summe und
ab V21 werden die Ergebnisse gespeichert.
Die entspannende Übung, das Programm auf heutige Verhältnisse zu übertragen, überlasse ich dem Leser.