Dokumentation als pdf - Universität Bayreuth

Werbung
Dokumentation zum Softwarepraktikum
Abzählen und Konstruktion der Strukturisomere
von Alkanen, Alkenen und Alkinen
Bearbeitet von:
Sabine Böhm
Florian Häberlein
Betreuer:
Dr. Axel Kohnert
Dipl.-math. Sascha Kurz
Universität Bayreuth
Wintersemester 2004/2005
1. Abzählen der Strukturisomere von Alkanen, Alkenen und Alkinen
Basierend auf dem Artikel „Anzahl von Strukturisomeren der Alkane“ von Sascha Kurz haben
wir seine Implementierung des Algorithmus geändert und den Algorithmus auf das Abzählen
von Alkenen und Alkinen erweitert. Die Vorgehensweise wird im folgenden Kapitel
beschrieben.
1.1 Änderung der Implementierung des Algorithmus zum Abzählen der
Strukturisomere der Alkane
Die ursprüngliche Implementierung des Algorithmus 1.14 (counting alkanes) aus dem oben
genannten Artikel basiert auf der Idee, dass Polynome von erzeugenden Funktionen der
Alkane als Vektoren dargestellt werden können, in deren i-ten Komponente der i-te
Koeffizient des Polynoms steht, wobei nur ganzzahlige Koeffizienten vorkommen. Unsere
Aufgabe war es nun die Datenstruktur der Polynome so umzuändern, dass ein Polynom in
einer ganzen Zahl gespeichert werden kann, da die Multiplikation von Polynomen dargestellt
durch Zahlen eine geringere Komplexität und damit eine geringere Laufzeit hat als die
Multiplikation der Polynome dargestellt durch Vektoren. Diese Tatsache beweist folgendes
Diagramm, das die Rechenzeiten eines Programms darstellt, das zwei Polynome 10 mal
multipliziert.
900
Zeit in Sekunden
800
700
600
500
400
300
200
100
Grad des Polynoms
0
10
50
100
200
300
400
500
750
1000
Rechenzeit in Sekunden bei der
Implementierung mit Vektoren
0,01
0,37
1,53
7,26
18,63
41,05
85,61
217,17
814,36
Rechenzeit in Sekunden bei der
Implementierung mit Zahlen
0
0,01
0,16
1,65
3,53
7,19
7,44
36,07
37,35
Unter der Annahme, dass alle Koeffizienten des Polynoms kleiner als 4n sind, wobei n die
maximale Anzahl der C-Atome der abzuzählenden Alkane ist, kann man also eine bijektive
Funktion
vector2number: INn
IN
mit der zugehörigen Umkehrfunktion
number2vector: IN
INn
definieren, die die geforderten Ansprüche erfüllt. Da wir diese Funktionen nach der
kompletten Umänderung der Implementierung nicht mehr benötigten, verzichten wir hier auf
die genaue Funktionsdefinition und richten unser Augenmerk auf das Ergebnis der
Abbildung: Bei einer gegebenen Funktion f(x) = a0 + a1x1 + a2x2 + . . . + anxn werden die
Koeffizienten in einer Zahl gespeichert, die sich aus n+1 Blöcken der jeweiligen Größe 2n-Bit
zusammensetzt. In jedem Block können daher Zahlen bis zu einer Größe von 22n = 4n
gespeichert werden. Da jeder Koeffizient des Polynoms kleiner 4n ist, ist somit die Abbildung
eindeutig, wohldefiniert und bijektiv. Zur Veranschaulichung ist hier der allgemeine Aufbau
der Zahl dargestellt:
011 . . . 0001 110 . . .1011 . . . . . . . . . . . . . . . . 011 . . .1101 110 . . . 1001
(n+1)-ter Block,
der den Koeffizienten an mit
2n-Bits darstellt
n-ter Block, der
den Koeffizienten an-1 mit 2nBits darstellt
2. Block, der
den Koeffizienten a1 mit 2nBits darstellt
1. Block, der
den Koeffizienten a0 mit 2nBits darstellt
Nachdem diese erste Hürde geschafft war, haben wir die verwendeten Funktionen im
Algorithmus der neuen Datenstruktur angepasst.
Die Funktion add, die vorher zwei Polynome in n+1 Schritten addierte, addiert nun die beiden
Polynome, dargestellt durch Zahlen, in einem Schritt. Ebenso die Funktion multiply, die
vorher zwei Polynome komponentenweise in mehreren Schritten multiplizierte, leistet nun
dasselbe, jedoch in einem Schritt. Da bei der Multiplikation von zwei Polynomen vom Grad n
auch Koeffizienten am 0 mit m>n auftreten, uns aber bei der Berechnung nur Koeffizienten
mit m n interessieren, haben wir ein „Gesamtschema“ eingeführt. Dies ist eine Zahl, die aus
(n+1)*2n-Bit 1-ern besteht und mit deren Hilfe man durch den Bitweise-Und-Operator die
überflüssigen Koeffizienten „abschneiden“ kann.
Die Änderung der Funktionen CycleIndex_2, CycleIndex_3 und CycleIndex_4 war nun der
nächste Schritt. In diesen Funktionen wird neben dem Addieren und Multiplizieren von
Polynomen auch die Multiplikation eines Polynoms mit einer natürlichen Zahl, das Teilen
eines Polynoms durch eine natürliche Zahl und die Berechnung von f(xm) durchgeführt.
Das Multiplizieren und das Dividieren durch eine natürliche Zahl wird für jeden Koeffizienten
einzeln vorgenommen, indem man die Zahl mit invertierter Blockreihenfolge in einer
temporären Zahl speichert und dann beim wiederholten Rückspeichern mit erneut
invertierter, also der ursprünglichen Blockreihenfolge, für jeden Block mit der natürlichen
Zahl multipliziert bzw. durch die natürliche Zahl dividiert.
Die Berechnung von f(xm) wird wieder mit Hilfe von Bitoperatoren durchgeführt. Dazu wird
das Polynom bis zum Grad n / m in umgekehrter Reihenfolge der Blöcke, welche die
Koeffizienten beschreiben, in einer temporären Zahl gespeichert. Beim Rückspeichern der
Zahl werden nun zwischen den Blöcken immer m-1 Blöcke bestehend aus 2n 0-ern
eingefügt. Nun steht in der neuen Zahl im 1-ten Block gerade a0, im m-ten Block steht a1
usw. Insgesamt steht nun in der neuen Zahl das Polynom f(xm), was gerade gewünscht war.
Nachdem nun die Datenstruktur geändert wurde und die Funktionen der neuen Datenstruktur
angepasst wurden, ergab sich noch ein Problem, dass vor allem bei der Berechnung für
kleinere n auftrat: In den Funktionen CycleIndex_2, CycleIndex_3 und CycleIndex_4 können
die Koeffizienten von Zwischenergebnissen größer als 4n werden. Damit vergrößerte sich der
Platzbedarf für die Koeffizienten in der Zahl und der Platz des zugewiesenen Blockes reicht
nicht mehr aus. Die Folge war, dass dadurch andere Koeffizienten verändert wurden und es
zu falschen Ergebnissen kam. Um dies zu verhindern wurde die alte Blockgröße 2n
vergrößert, so dass auch für Zwischenergebnisse die größer als 4n sind ausreichend Platz
vorhanden ist.
Das folgende Diagramm zeigt die durchschnittlichen Rechenzeiten der Programme für die
beiden Implementierungen und für verschiedene n. Dabei wurde für die Implementierung mit
Zahlen eine von Sascha Kurz verbesserte Version verwendet.
10000
Zeit in Sekunden
(logarithmisch)
1000
100
10
1
0,1
Anzahl der
C-Atome
0,01
20
40
80
160
320
640
Rechenzeit in Sekunden bei
der Implementierung mit
Vektoren
0,01
0,09
1,26
11,27
103,58
1125,15
Rechenzeit in Sekunden bei
der Implementierung mit
Zahlen
0,01
0,06
0,58
8,81
157,3
3360,84
Man sieht deutlich, dass für mehr als 160 C-Atome die Implementierung mit Vektoren wieder
schneller ist. Dies liegt daran, dass im Originalprogramm sehr speicher- und
rechenzeitsparend gearbeitet wurde. Die durch uns veränderte Version birgt noch weitere
Zeiteinsparmöglichkeiten, sodass die theoretisch schnellere Implementierung mit Zahlen
(siehe Diagramm oben) auch praktisch umgesetzt wirklich schneller ist.
1.2 Erweiterung des Algorithmus auf das Abzählen der Strukturisomere der
Alkene
Ausgehend von der neuen Implementierung mit Zahlen haben wir den Algorithmus 1.14
(counting alkanes) aus dem oben genannten Artikel so geändert, dass nun die
Strukturisomere der Alkene gezählt werden. Dazu wurde die Grenze der maximalen Höhe
der Teilbäume von (n+1)/2 auf n-2 erhöht, da bei einem Alken mit n C-Atomen die größte
Höhe für einen Teilbaum gerade n-2 ist. Die Berechnung der Wurzelbäume blieb natürlich
gleich. Lediglich das Zusammensetzen der Wurzelbäume zu einem Alken hat sich verändert.
Dazu haben wir 5 Fälle unterschieden, wobei wir im folgenden Modell annehmen, dass das
jeweilige Isomer so gedreht oder gespiegelt ist, dass einer Wurzelbäume mit der aktuell
größten Höhe der Wurzelbaum W1 ist.
W1
W3
W2
W4
1. Wurzelbaum W1 ist der einzige Wurzelbaum mit der aktuell größten Höhe, alle
anderen Wurzelbaum haben eine kleinere Höhe
2. Die Wurzelbäume W1 und W2 haben beide die aktuell größte Höhe, die
Wurzelbäume W3 und W4 haben kleinere Höhe
3. Wurzelbaum W1 und genau einer der beiden Wurzelbäume W3 oder W4 haben die
aktuell größte Höhe, die beiden anderen haben eine kleinere Höhe
4. Drei der vier Wurzelbäume haben die aktuell größte Höhe, der vierte hat eine kleinere
Höhe
5. Alle vier Wurzelbäume haben die aktuell größte Höhe
1.3 Erweiterung des Algorithmus auf das Abzählen der Strukturisomere der
Alkine
Wie bei der Änderung des Algorithmus 1.14 (counting alkanes) für Alkene haben wir die
Grenze der maximalen Höhe der Teilbäume der Alkine auf n-2 erhöht. Die Berechnung der
Wurzelbäume blieb natürlich ebenfalls gleich. Beim Zusammensetzen der Wurzelbäume zu
Alkinen haben wir zwei Fälle unterschieden, wobei wir im folgenden Modell annehmen, dass
das jeweilige Isomer so gedreht ist, dass W1 ein Wurzelbaum mit der aktuell größten Höhe
ist.
W1
W2
1. Wurzelbaum W1 und Wurzelbaum W2 haben die aktuell größte Höhe
2. Wurzelbaum W1 hat die aktuell größte Höhe und Wurzelbaum W2 hat eine kleinere
Höhe
2. Konstruktion der Strukturisomere der Alkene und Alkine
Basierend auf dem Artikel „Anzahl von Strukturisomeren der Alkane“ von Sascha Kurz haben
wir seine Implementierung der Algorithmen zur Konstruktion von Alkanen geändert und den
Algorithmus auf die Konstruktion von Alkenen und Alkinen erweitert. Die Vorgehensweise
wird im folgenden Kapitel beschrieben.
2.1 Erweiterung des Algorithmus auf die Konstruktion der Alkene
Ausgehend von der Implementierung der Algorithmen 1.9, 1.12 und 1.13 von Sascha Kurz
haben wir die Implementierung auf die Konstruktion von Alkene erweitert. Wir benutzen
wieder das gleiche Modell eines Alkens wie in Kapitel 1.2. Die Datenstruktur eines Alkens
beschränkt sich somit auf die Speicherung der vier Wurzelbäume. Eine Unterscheidung ob
die längste Kette der C-Atome gerade oder ungerade ist wird nicht mehr benötigt.
Die Konstruktion ist in acht Fälle gegliedert um alle möglichen Moleküle zu konstruieren.
Dabei läuft der Wurzelbaum W1 alle möglichen Wurzelbäume der aktuellen maximalen Höhe
durch. Alle anderen Wurzelbäume laufen alle restlichen Höhen inklusive der aktuellen
maximalen Höhe durch, wobei folgende Fälle unterschieden werden:
1.
2.
3.
4.
5.
6.
7.
8.
Alle Wurzelbäume sind ungleich:
Zwei Wurzelbäume sind gleich (erster Fall):
Zwei Wurzelbäume sind gleich (zweiter Fall):
Zwei Wurzelbäume sind gleich (zweiter Fall):
Je zwei Wurzelbäume sind gleich:
Drei Wurzelbäume sind gleich (erster Fall):
Drei Wurzelbäume sind gleich (zweiter Fall):
Alle vier Wurzelbäume sind gleich:
W1 > W2 > W3 > W4
W1 > W2 > W3 = W4
W1 = W2 > W3 > W4
W1 > W2 = W3 > W4
W1 = W2 > W3 = W4
W1 > W2 = W3 = W4
W1 = W2 = W3 > W4
W1 = W2 = W3 = W4
In jedem der acht Fälle werden dann alle möglichen Stellungen der Wurzelbäume zu der
Liste der Alkene hinzugefügt.
Zur graphischen Ausgabe der Alkene wird eine XML-Datei im GRAPHDB-Format erstellt.
Dabei werden die Wurzelbäume Knoten für Knoten durchgelaufen und anschließend werden
noch die zugehörigen H-Atome in die Datei ausgegeben. Die Dokumentation zum
GRAPHDB-Format findet man unter http://btm2xg.mat.uni-bayreuth.de/GRAPHDB/.
2.2 Erweiterung des Algorithmus auf die Konstruktion der Alkine
Ausgehend von der Implementierung der Algorithmen 1.9, 1.12 und 1.13 von Sascha Kurz
haben wir die Implementierung auf die Konstruktion von Alkine erweitert. Wir benutzen
wieder das gleiche Modell eines Alkins wie in Kapitel 1.3. Die Datenstruktur eines Alkins
beschränkt sich somit auf die Speicherung der zwei Wurzelbäume. Eine Unterscheidung ob
die längste Kette der C-Atome gerade oder ungerade ist wird nicht mehr benötigt.
Bei der Konstruktion werden für den Teilbaum W1 alle möglichen Bäume mit der aktuellen
maximalen Höhe h durchlaufen. Für den rechten Teilbaum werden alle Teilbäume bis zur
Höhe h durchlaufen. Die jeweiligen Alkine werden dann zur Liste der Alkine hinzugefügt.
Zur graphischen Ausgabe der Alkene wird eine XML-Datei im GRAPHDB-Format erstellt.
Dabei werden die Wurzelbäume Knoten für Knoten durchgelaufen und anschließend werden
noch die zugehörigen H-Atome in die Datei ausgegeben. Die Dokumentation zum
GRAPHDB-Format findet man unter http://btm2xg.mat.uni-bayreuth.de/GRAPHDB/.
Herunterladen