Autor |
Beitrag |
Heiko
      
Beiträge: 3169
Erhaltene Danke: 11
|
Verfasst: Di 26.02.08 19:59
Hallo,
wir soll(t)en in der Schule das TSP (Traveling-Salesman-Problem) implementieren. Da es sich dabei um ein NPC-Problem handelt, haben wir eine Optimierung behandelt, die schneller zur Lösung korrekten führt (allerdings weiterhin NPC). Allerdings trinkt man Algo irgendwo zwischendurch Kaffee (eher Schlafmittel, als nen Aufputschmittel  ). Die Ursache dafür ist mir leider bis dato unbekannt. Sieht einer von ecuh den?
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328:
| function TGraph.TSP(var Way: TIntArray): Integer; const CReserveCount = 100;
type PTSPData = ^TTSPData; TTSPData = record MateID: Integer; WayLen: Integer; next : PTSPData; end;
PTSPList = ^TTSPList; TTSPList = record next: PTSPList; ID : integer; conn: PTSPData; end;
PWay = ^TWay; TWay = record next: PWay; ID : integer; end;
PPriorityList = ^TPriorityList; TPriorityList = record ID : integer; next : PPriorityList; way : PWay; WayLen: Integer; MinWay: Integer; data : PTSPList; end;
PMemData = ^TMemData; TMemData = record next: PMemData; end;
var i, j : Integer; NodeCnt : Integer; newP, curP : PPriorityList; Priority : PPriorityList; PriorityFree : PPriorityList; tmpLC, tmpLP, tmpPr: PTSPList; tmpC,tmpP,newD, cur, tmpO : PTSPData; tmpFreeMemPos : Integer; num : integer; childCnts : array of integer; StartNode : Integer; wayC, wayP : PWay;
MaxMemSize : Integer; ReserveMemSize : Integer; MemPos : PMemData;
procedure GetNewPriorityItems; var i : integer; oldMemPos: Pointer; curData : PPriorityList; prev : PPriorityList; begin oldMemPos:=MemPos; GetMem(MemPos, ReserveMemSize); MemPos^.next:=oldMemPos;
prev:=nil; curData:=PPriorityList(Integer(MemPos)+SizeOf(PMemData)); for i := CReserveCount-1 downto 0 do begin curData^.next:=prev; prev:=curData; inc(Integer(curData), MaxMemSize); end; dec(Integer(curData), MaxMemSize); PriorityFree:=curData; end;
procedure FreeAllPriorityItems; var nextMemPos: PMemData; begin while MemPos<>nil do begin nextMemPos:=MemPos^.next; FreeMem(MemPos, ReserveMemSize); MemPos:=nextMemPos; end; end;
procedure WriteMinWayToCurrentPriorityItem(item: PPriorityList); begin item^.MinWay:=0; tmpLC:=item^.data; tmpLP:=nil; while tmpLC<>nil do begin if tmpLC^.conn=nil then begin if tmpLP=nil then item^.data:=tmpLC^.next else tmpLP^.next:=tmpLC^.next; end else begin inc(item^.MinWay, tmpLC^.conn^.WayLen); tmpLP:=tmpLC; end; tmpLC:=tmpLC^.next; end; inc(item^.MinWay, item^.WayLen); end;
begin Result:=-1; SetLength(Way, 0); if _NodeCount = 0 then exit; PriorityFree:=nil; MemPos:=nil;
MaxMemSize:=SizeOf(TPriorityList); SetLength(childCnts, _NodeCount); for i := _NodeCount-1 downto 0 do begin NodeCnt:=0; for j := High(_NodesList[i]) downto 0 do if TryStrToInt(_NodesList[i][j].Caption, num) and (num>-1) then inc(NodeCnt); childCnts[i]:=NodeCnt; if NodeCnt>0 then inc(MaxMemSize, SizeOf(TTSPList)+NodeCnt*SizeOf(TTSPData)); end; ReserveMemSize:=MaxMemSize*CReserveCount+SizeOf(PMemData); GetNewPriorityItems;
Priority:=PriorityFree; PriorityFree := PriorityFree^.next; Priority.next := nil; Priority.way := nil; Priority.ID := _NodeCount; Priority.data:=nil; tmpFreeMemPos:=Integer(@Priority.data)+SizeOf(PTSPList); for i := _NodeCount-1 downto 0 do begin if childCnts[i]>0 then begin with Priority^ do begin tmpLC:=data; data:=PTSPList(tmpFreeMemPos); ID:=i; data^.next:=tmpLC; data^.ID:=i; data^.conn:=nil; end; inc(tmpFreeMemPos, SizeOf(TTSPList)); for j := High(_NodesList[i]) downto 0 do begin if TryStrToInt(_NodesList[i][j].Caption, num) and (num>-1) then begin tmpC:=Priority^.data^.conn; tmpP:=nil; while (tmpC<>nil) and (tmpC.WayLen<num) do begin tmpP:=tmpC; tmpC:=tmpC^.next; end;
newD:=PTSPData(tmpFreeMemPos); newD^.MateID:=_NodesList[i][j].ID; newD^.WayLen:=num; newD^.next:=tmpC; if tmpP=nil then Priority^.data^.conn:=newD else tmpP^.next:=newD; inc(tmpFreeMemPos, SizeOf(TTSPData)); end; end; end; end; SetLength(childCnts, 0); if Priority^.ID<>_NodeCount then begin StartNode:=Priority^.ID; Priority^.WayLen:=0; WriteMinWayToCurrentPriorityItem(Priority);
while Priority<>nil do begin if Priority^.data^.next=nil then begin if Priority^.data^.conn^.MateID=StartNode then begin Result:=Priority^.WayLen+Priority^.data^.conn^.WayLen; SetLength(Way, _NodeCount); Way[_NodeCount - 1]:=Priority^.ID+1; for i := _NodeCount - 2 downto 0 do begin Way[i]:=Priority^.way^.ID+1; Priority^.way:=Priority^.way^.next; end; Priority:=nil; end; end else begin cur:=nil; tmpLP:=nil; tmpLC:=Priority^.data; while tmpLC<>nil do begin if tmpLC^.ID=Priority^.ID then begin cur:=tmpLC^.conn; if tmpLP=nil then Priority^.data:=tmpLC^.next else tmpLP^.next:=tmpLC^.next; end else tmpLp:=tmpLC; tmpLC:=tmpLC^.next; end; while cur<>nil do begin if PriorityFree = nil then GetNewPriorityItems; newP:=PriorityFree; PriorityFree:=PriorityFree^.next;
tmpFreeMemPos:=Integer(@newP.data)+SizeOf(PTSPList); newP^.way := PWay(tmpFreeMemPos); newP^.way^.ID:=Priority^.ID; newP^.way^.next:=nil; inc(Integer(tmpFreeMemPos), SizeOf(TWay)); wayP:=newP^.way; wayC:=Priority^.way; while wayC<>nil do begin wayP^.next:=PWay(tmpFreeMemPos); wayP^.next^.ID:=wayC^.ID; wayP:=wayP^.next; wayC:=wayC^.next; inc(Integer(tmpFreeMemPos), SizeOf(TWay)); end; wayP^.next:=nil;
newP^.ID := cur^.MateID; newP^.WayLen:=Priority^.WayLen+cur^.WayLen; newP^.data:=nil; tmpPr:=Priority^.data; while tmpPr<>nil do begin with newP^ do begin tmpLC:=data; data:=PTSPList(tmpFreeMemPos); ID:=cur^.MateID; data^.next:=tmpLC; data^.ID:=tmpPr^.ID; data^.conn:=nil; end; inc(tmpFreeMemPos, SizeOf(TTSPList)); tmpO:=tmpPr^.conn; tmpC:=newP^.data^.conn; while tmpO<>nil do begin if (tmpO^.MateID<>cur^.MateID) and (not ((tmpO^.MateID=Priority^.ID) and (cur^.MateID=tmpPr^.ID))) then begin newD:=PTSPData(tmpFreeMemPos); newD^.MateID:=tmpO^.MateID; newD^.WayLen:=tmpO^.WayLen; newD^.next:=nil; if tmpC=nil then newP^.data^.conn:=newD else tmpC^.next:=newD; inc(tmpFreeMemPos, SizeOf(TTSPData)); tmpC:=newD; end; tmpO:=tmpO^.next; end; tmpPr:=tmpPr^.next; end; WriteMinWayToCurrentPriorityItem(newP); if newP^.data<>nil then begin curP:=Priority; while (curP^.next<>nil) and (curP^.next^.MinWay<newP^.MinWay) do curP:=curP^.next; newP^.next:=curP^.next; curP^.next:=newP; end; cur:=cur^.next; end; curP:=Priority; Priority:=Priority^.next; curP^.next:=PriorityFree; PriorityFree:=curP; end; end; end else begin if _NodeCount=1 then begin Result:=0; SetLength(Way, 1); Way[0]:=0; end; end; FreeAllPriorityItems; end; |
Zum Algo: Normalerweise wird eine Tiefensuche verwendet. Zur "Abkürzung" nimmt man nun keine Tiefensuche mehr, sondern wählt immer den Weg als nächstes, der am wahrscheinlichsten zur geringsten Lösung führen könnte. Dazu wird bei jedem Schritt ermittelt, was die minimale Kante an den ürbigen Knoten ist + den bereits zurückgelegten Wert. Das ganze hat also eine Prioritätswarteschlange.
Als zusatzliche, Delphi-spezifische, Optimierung habe ich über den Speichermanager einen eigenen "Speichermanager" gelegt, der mit Verschnitt arbeit, aber eben weiß, ohne nachgucken zu müssen, wo noch genug Speicher frei ist.
Von daher weiß ich auf Anhieb nicht, warum mein Algo deutlich langsamer ist, als die einfache Tiefensuche (auf einen vollständigen Graphen mit 10 Knoten braucht meiner 4,7min, während die Tiefensuche 150ms braucht  )
|
|
Horst_H
      
Beiträge: 1654
Erhaltene Danke: 244
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: Mi 27.02.08 08:53
Hallo,
soll man jetzt herausfinden, in welcher Ecke das Programm am längsten verweilt?
Dies kannst Du doch selber testen, in dem Du einfach ein paar globale Zaehler in Deine Funktionen einbaust.
Ein vollständiges Programm mit einlesbaren Daten wäre hilfreicher, um es in Delphi selbst zu betrachten, so kann man nichts testen und jeden Gedanken erfassen.Das ist mir heute morgen zu undurchsichtig, wo blebt der Kaffee...
Also häng mal ein minimales lauffähiges Programmschnipsel mit Daten an.
Gruß Horst
P.S.:
Ein Struktogramm könnte Dir auch die Augen öffnen wo es klemmt oder sich verrennt.
|
|
martin_yt
Hält's aus hier
Beiträge: 14
|
Verfasst: Mi 27.02.08 12:58
Hi
wie mein Vorredner schon andeutete, ist es für uns fast unmöglich herauszufinden, wo das Programm unnötig viel Zeit verbraucht. Aber das kannst du ja auch selbst herausfinden, indem du jede Prozedur einzeln auf Herz und Nieren (und insbesondere auf ihre Laufzeit) untersuchst.
Zu Deinem Algorithmus habe ich selbst aber ein paar Fragen:
Du sagst, normalerweise wird Tiefensuche verwendet: Das ist ja genau genommen nur das Ausprobieren aller Möglichkeiten, brute force, oder irre ich mich?
Zitat: |
Zum Algo:
...
wählt immer den Weg als nächstes, der am wahrscheinlichsten zur geringsten Lösung führen könnte. Dazu wird bei jedem Schritt ermittelt, was die minimale Kante an den übrigen Knoten ist + den bereits zurückgelegten Wert.
...
|
Darf ich fragen, wie du den "Knoten, der am wahrscheinlichsten zur geringsten Lösung führen könnte" ermittelst? Aus deiner Algorithmen-Beschreibung wird mir das nicht ganz klar: nimmst Du einfach von den vom aktuellen Knoten wegführenden Kanten diejenige zum Weg dazu, die die geringsten Kosten hat? So klingts in Deiner Beschreibung fast. Allerdings gehe ich davon aus, dass Du das nicht meinst, denn dieser "greedy" Algorithmus führt nicht immer zur korrekten Lösung, was du eingangs sagtest.
Erklär ihn mir doch bitte nochmal. Oder führe ihn vielleicht an einem kleinen Beispiel aus.
(sorry, aus dem Code werde ich einfach nicht schlau, da helfen mir auch die gutgemeinten Kommentare nicht  )
Zitat: |
Von daher weiß ich auf Anhieb nicht, warum mein Algo deutlich langsamer ist, als die einfache Tiefensuche (auf einen vollständigen Graphen mit 10 Knoten braucht meiner 4,7min, während die Tiefensuche 150ms braucht )
|
Hast du die normale Tiefensuche auch implementiert? Woher hast du die 150ms für den Graphen mit 10 Knoten? Das wäre nämlich eine Spitzenzeit für 10 Knoten!
Ich hab vor geraumer Zeit selbst vier Algorithmen (BruteForce, dynamische Programmierung, einen gierigen Algoritmus und eine randomiesiete Suche) in Octave (/Matlab) implementiert und solch eine Zeit ist fast schon utopisch für 10 Knoten, sofern Du wirklich immer die kürzesten Rundreisen findest!
Zum Schluß verweise ich noch auf einen Artikel, der Dich sicher auch interessieren könnte: www-i1.informatik.rw...gorithmus/algo40.php
Gruß,
martin
|
|
Horst_H
      
Beiträge: 1654
Erhaltene Danke: 244
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: Mi 27.02.08 17:31
Hallo,
die Geschwindigkeit hängt ja auch vom Rechner ab.
Mittels eines Abständefeldes 10x10 , die nur auf der Diagonalen mit 1e300 belegt, sonst mit Zufallszahlen aus ]0,100[
war das Finden meist in etwa 0.13 Sekunden erledigt (2.3 Ghz AMDX2).
Üblicherweise ist der Weg von A nach B gleich dem von B nach A, aber das habe ich weggelassen. Das heisst 0,15 Sekunden sind für den Brute Force Ansatz plausibel.
Gruß Horst
P.S.:
Ich hoffe, ich habe das jetzt nicht falsch gepinselt. Jetzt ohne Random, sodass erst weit noch unten gesucht werden muss.
Das sind jetzt 0.05 s (Cool&Quite sollte man schon abschalten  )
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112:
| program Project1;
{$APPTYPE CONSOLE}
uses SysUtils; const MAX = 10; MAX_1 = MAX-1; type
tINdex = 0..MAX;
tIndexArray = array[tINdex] of tIndex; tCheckArray = array[tIndex,tIndex] of double;
var t,t0,t1:TdateTime; CheckArray : tCheckArray; MomMinIndex, AbgearbeitetIndex : tIndexArray; Wegstrecke, momMinWeg : double; sp,ze,i: integer;
function NextPermut(var a:tIndexArray;n:integer): Boolean;
var k,j,r,s : integer; temp : integer; procedure swap(i,j :integer); begin temp := a[i]; a[i] := a[j]; a[j] := temp; end;
begin k := n-1; while (k>0) AND(a[k] > a[k+1]) do k:=k-1; if k >0 then begin j := n; while a[k] > a[j] do j:=j-1; swap(j,k); r:=n; s:=k+1; while r>s do begin swap(r,s); r:=r-1; s:=s+1; end; NextPermut:= true; end else NextPermut := false end;
begin randseed := 147110815; For ze := 1 to Max Do For sp := 1 to Max do If sp <> ze then CheckArray[ze,sp] := (MAX-sp+1)*(MAX-ze+1) else CheckArray[ze,sp] := 1e300;
For sp := 1 to Max-1 do AbgearbeitetIndex[sp] := sp+1; AbgearbeitetIndex[MAX] := 1; MomMinWeg := 1e299; t0 := time;
repeat WegStrecke :=0.0; ze := 1; for i := 1 to MAX do begin Wegstrecke := Wegstrecke+CheckArray[ze,AbgearbeitetIndex[i]]; if WegStrecke > MomMinWeg then break; ze := AbgearbeitetIndex[i] end; IF Wegstrecke < momMinWeg then begin MomMinWeg :=WegStrecke; MomMinIndex:=AbgearbeitetIndex; end; until NOT(NextPermut(AbgearbeitetIndex,Max_1)); writeln(MomMinWeg:10:5); ze := 1; for i := 1 to MAX do begin writeln(ze:2,' -> ',MomMinIndex[i]:2,CheckArray[ze,MomMinIndex[i]]:10:4); ze := MomMinIndex[i]; end;
writeln(FormatdateTime(' hh:mm:ss.zzz',time-t0)) ; readln; end. |
|
|
Heiko 
      
Beiträge: 3169
Erhaltene Danke: 11
|
Verfasst: Mi 27.02.08 21:36
Horst_H hat folgendes geschrieben: | soll man jetzt herausfinden, in welcher Ecke das Programm am längsten verweilt? |
Ja  . Das es beim abarbeiten der Prioritätswarteschlange ist, ist klar. Aber wo genau nicht (ich vermute beim Kopieren für die neuen Prioritätswarteschlangen). Mein Problem ist, bei 4 Städten hat mein Algo ne Geschwindigkeit von ca. 0,6ms.
Ich bin momentan dabei mit nem Profiler das auseinanderzunehmen, aber dazu muss ich den Code weiter zerlegen, da man keine Zwischenpunkte Zeitmesspunkte setzten kann  .
Horst_H hat folgendes geschrieben: | Ein Struktogramm könnte Dir auch die Augen öffnen wo es klemmt oder sich verrennt. |
Ich vermute mal, dass es nicht daran liegt. Denn den Algo habe ich lang genug durch den Kopf gewälzt, bevor ich mich für die herangehensweise entschieden habe  .
martin_yt hat folgendes geschrieben: | Du sagst, normalerweise wird Tiefensuche verwendet: Das ist ja genau genommen nur das Ausprobieren aller Möglichkeiten, brute force, oder irre ich mich? |
Japp, ne Tiefensuche ist Bruteforce. Eine andere genaue Möglichkeit gibt es ja leider nicht. Allerdings ist es keine reine "Tiefensuche". Normalerweise geht ja die Tiefensuche gleich alle Kinder durch. Bei der Optimierung die ich drin habe ("branch and bound" [wenn ich mich recht entsinne ist Branch & cut das gleiche, aber da habe ich mich hereingelesen]), wird nun keine Rekursion mit den Kindern aufgerufen, sondern die Kinder werden in eine Prioritätswarteschlamge abgelegt. Man muss also kein komplettes Bruteforce machen, sondern nur so lange, bis man einen Weg gefunden hat, denn dass es der kürzeste ist, liegt ja an der Prioritätswarteschlange.
martin_yt hat folgendes geschrieben: | Darf ich fragen, wie du den "Knoten, der am wahrscheinlichsten zur geringsten Lösung führen könnte" ermittelst? Aus deiner Algorithmen-Beschreibung wird mir das nicht ganz klar: nimmst Du einfach von den vom aktuellen Knoten wegführenden Kanten diejenige zum Weg dazu, die die geringsten Kosten hat? So klingts in Deiner Beschreibung fast. |
minimal geringste Kosten = aktuelle Kosten (man ist ja irgendwie zum Knoten gegangen)+Summe, der minimalen Kanten die von einem Knoten wegführen, von allen unbesuchten Knoten. Man hat also den minimal noch möglichen Weg.
martin_yt hat folgendes geschrieben: | Allerdings gehe ich davon aus, dass Du das nicht meinst, denn dieser "greedy" Algorithmus führt nicht immer zur korrekten Lösung, was du eingangs sagtest. |
Greedy wollte ich nicht  .
martin_yt hat folgendes geschrieben: | Erklär ihn mir doch bitte nochmal. Oder führe ihn vielleicht an einem kleinen Beispiel aus. |
Wenn ich Zeit habe, vorm Wochenende wahrscheinlich nimmer (der Profiler muss auch so lange warten)  , kann ich ja mal das an einem Beispiel verdeutlichen.
martin_yt hat folgendes geschrieben: | (sorry, aus dem Code werde ich einfach nicht schlau, da helfen mir auch die gutgemeinten Kommentare nicht ) |
Japp, bei denen muss man den Algo kennen, damit man die versteht. Aber eigentlich waren die mehr für mich selber gedacht bzw. für den lehrer, damit er sieht, was wo ungefähr passiert.
martin_yt hat folgendes geschrieben: | Hast du die normale Tiefensuche auch implementiert? Woher hast du die 150ms für den Graphen mit 10 Knoten? Das wäre nämlich eine Spitzenzeit für 10 Knoten! |
Einer aus meinen InfoLK. Er meinte nur, dass er ein paar Abbruchbedingungen dabei hat, wodurch es doch nen bissl schneller geworden ist (liefert aber immer noch die genaue Lösung). Was genau: ka (Tiefensuche solls aber weiterhin sein).
Dynamische Programmierung ist die nächste Stufe, die ich machen werde (sobald die hier schneller ist) . Wobei ich bei meiner jetzigen vlt. ein bisschen sparsammer mit dem RAm umgehen sollte. Bei wild20 habe ich das Proggi nach 15min abgerochen, da es schon 1GB Auslagerungsdateien+350MB RAM hatte  .
@Horst: Dein Algo ist Nährung, oder? Denn Ramdom kann ja nur darauf deuten  .
Demo-Dateien (4,7min bei metrisch10; 480ms bei wild10).
Gruß
Heiko
Einloggen, um Attachments anzusehen!
|
|
Horst_H
      
Beiträge: 1654
Erhaltene Danke: 244
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: Do 28.02.08 00:06
Hallo,
bei mir wurde mal das Feld der Abstände mit Zufallszahlen belegt.
Nein meine Version durchsucht alles, solange es Sinn macht.
Siehe die repeat Schleife. Das ist der komplette Algorithmus.
Ich dachte das hätte mit der Kürze des Programmes leicht nachvollziehen können.
Das ist doch nicht so unübersichtlich wie Deine Version.
Angehme Ruh
Horst
Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| ■ Free Pascal IDE Version 1.0.10 [2007/09/09] ■ Compiler Version 2.2.0 ■ GBD Version GDB 6.2.1 ■ Cygwin "C:\FPC\2.1.4\bin\i386-win32\cygwin1.dll" version 1005.18.0.0 Running "c:\fpc\2.1.4\bin\i386-win32\tsp.exe " ^CRunning "c:\fpc\2.1.4\bin\i386-win32\tsp.exe " Kürzester Weg 224.00000 1 -> 9 20.00 XXX90.0080.0070.0060.0050.0040.0030.0020.0010.00 9 -> 3 16.00 20.0018.0016.0014.0012.0010.00 8.00 6.00 XXX 2.00 3 -> 7 32.00 80.0072.00 XXX56.0048.0040.0032.0024.0016.00 8.00 7 -> 5 24.00 40.0036.0032.0028.0024.0020.00 XXX12.00 8.00 4.00 5 -> 6 30.00 60.0054.0048.0042.00 XXX30.0024.0018.0012.00 6.00 6 -> 4 35.00 50.0045.0040.0035.0030.00 XXX20.0015.0010.00 5.00 4 -> 8 21.00 70.0063.0056.00 XXX42.0035.0028.0021.0014.00 7.00 8 -> 2 27.00 30.0027.0024.0021.0018.0015.0012.00 XXX 6.00 3.00 2 -> 10 9.00 90.00 XXX72.0063.0054.0045.0036.0027.0018.00 9.00 10 -> 1 10.00 10.00 9.00 8.00 7.00 6.00 5.00 4.00 3.00 2.00 XXX 00:00:00.050 |
|
|
martin_yt
Hält's aus hier
Beiträge: 14
|
Verfasst: Do 28.02.08 02:27
Hi
zu den Laufzeiten:
Ich hab meine Bruteforce-Implementierung wieder ausgekramt und unter die Lupe genommen.
Es scheint wohl an Octave zu liegen, dass bei mir (mit nem AMD Athlon X2500 m @1.8GHz) 10 Knoten schon 5min dauern:
Zum Test habe ich Horsts Algorithmus zum Erstellen aller Permutationen 1 zu 1 in Octave implementiert. Allein das Durchgehen z.B. aller 8! Permuationen für ne Tour durch 9 Städte dauert ~ 9sekunden, weshalb mich die insgesamt grauenhaft schlechten Laufzeiten nun wirklich nicht mehr wundern.
Geschockt von der Octave-Performance werd ich das ganze wohl doch nochmal in eine "ordentliche Programmiersprache" übersetzen, damit meine Ergebnisse konkurrenzfähiger werden
. . . und Octave darf fürs erste nur noch Matrizen multiplizieren ...
gruß martin
|
|
Horst_H
      
Beiträge: 1654
Erhaltene Danke: 244
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: Fr 29.02.08 18:36
Hallo,
ich habe Das Programm noch etwas getunt, sodass es nicht erfolgverpsrechende Wege nicht mehr weiterverfolgt (unter der Annahme das Wege,Kosten immer größer Null )und schon bekannte Teile nicht neu rechnen. Das bringt leider nur eine Reduzierung auf ein knapp unter ein Drittel bei n= 13.
Statt 1min 45 Sekunden nun 29 Sekunden.Aber mit größerem n wird der Einfluß etwas größer, da mehr eingespart wird.
Ist also im Prinzip irrelevant, ist ja immer noch brute force.
Hier mal mit 14, dauert so 5min 30 beim 1.7 Ghz Pentium M
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160:
| program TSP_3;
{$APPTYPE CONSOLE}
uses SysUtils; const MAX = 14; MAX_1 = MAX-1; type
tINdex = 0..MAX;
tIndexArray = array[tINdex] of tIndex; tCheckArray = array[tIndex,tIndex] of double; tTempSumArray = array[tIndex] of double;
var t,t0,t1:TdateTime; CheckArray : tCheckArray; TempSum : TTempSUmArray; MomMinIndex, AbgearbeitetIndex : tIndexArray; Wegstrecke, momMinWeg : double; AktTiefe, sp,ze,i: integer;
procedure Umsort(var a:tIndexArray;von:integer);
var i,j,temp : integer; begin For i := von+1 to Max-1 do For j := Max-1 downto i do If a[j-1]<a[j] then begin temp:= a[j]; a[j] := a[j-1]; a[j-1]:= temp; end; end;
function NextPermut(var a:tIndexArray): integer;
var k,j : integer; temp : integer; procedure swap(k,j :integer); begin temp := a[k]; a[k] := a[j]; a[j] := temp; end;
begin k := MAX-2; while (k>0) AND (a[k] > a[k+1]) do dec(k); result :=k; if k >0 then begin j := MAX-1; while a[k] > a[j] do dec(j); swap(j,k); j:=Max-1; inc(k); while j>k do begin swap(j,k); inc(k); dec(j); end; if result > j then result := j; end; end;
begin For ze := 1 to Max Do begin TempSum[ze] := 0.0; For sp := 1 to Max do If sp <> ze then CheckArray[ze,sp] := (MAX-sp+1)*(MAX-ze+1) else CheckArray[ze,sp] := 1e300; end;
For sp := 1 to Max-1 do AbgearbeitetIndex[sp] := sp+1; AbgearbeitetIndex[MAX] := 1;
Akttiefe := 1; MomMinWeg := 1e299; t0 := time;
repeat ze := 1; WegStrecke:= 0.0; if AktTiefe >1 then begin ze := AbgearbeitetIndex[AktTiefe-1]; WegStrecke:= tempSum[Akttiefe-1]; end;
for i := AktTiefe to MAX do begin Wegstrecke := Wegstrecke+CheckArray[ze,AbgearbeitetIndex[i]]; tempSum[i] := WegStrecke; if WegStrecke > MomMinWeg then begin Umsort(AbgearbeitetIndex,i); break; end; ze := AbgearbeitetIndex[i] end; IF Wegstrecke < momMinWeg then begin MomMinWeg :=WegStrecke; MomMinIndex:=AbgearbeitetIndex; end; AktTiefe := NextPermut(AbgearbeitetIndex); until AktTiefe <= 0;
writeln(MomMinWeg:10:5); ze := 1; for i := 1 to MAX do begin write(ze:2,' -> ',MomMinIndex[i]:2,CheckArray[ze,MomMinIndex[i]]:4:0,' '); For sp := 1 to Max do IF ze<>sp then write(CheckArray[ze,sp]:5:2)
else write(' XXX'); writeln; ze := MomMinIndex[i]; end;
writeln(FormatdateTime(' hh:mm:ss.zzz',time-t0)) ; readln; end. |
Gruß Horst
|
|
Horst_H
      
Beiträge: 1654
Erhaltene Danke: 244
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: Fr 29.02.08 19:43
Hallo,
Ich habe jetzt metrisch10.txt eingebaut.
Dein Weg:
1-7-6-10-8-9-2-4-3-5-1
Und es wird auch der gleiche Weg gefunden, nur in umgekehrter Richtung( 5 kommt ja vor 7) jeweils 2330 LE lang.
Fürwahr Diene Version braucht über 82 MB Hauptspeicher kurz vor Ende der Berechnungen (390 Sekunden 90% Last da Musik läuft.. ).
Bei einem so dicht besetzten Graphen bietet sich doch ein Array an.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172:
| program TSP_3;
{$APPTYPE CONSOLE}
uses SysUtils; const MAX = 10; MAX_1 = MAX-1; type
tINdex = 1..MAX;
tIndexArray = array[tINdex] of tIndex; tCheckArray = array[tIndex,tIndex] of double;
tTempSumArray = array[tIndex] of double; var t,t0,t1:TdateTime; CheckArray : tCheckArray = ((000,266,344,267,303,314,298,299,254,268), (266,000,258,140,200,216,193,195,114,142), (344,258,000,226,267,279,262,263,210,226), (267,140,226,000,318,329,314,315,273,286), (303,200,267,318,000,319,303,305,260,274), (314,216,279,329,319,000,298,299,254,267), (298,193,262,314,303,298,000,315,272,285), (299,195,263,315,305,299,315,000,200,217), (254,114,210,273,260,254,272,200,000,232), (268,142,226,286,274,267,285,217,232,000));
TempSum : TTempSUmArray; MomMinIndex, AbgearbeitetIndex : tIndexArray; Wegstrecke, momMinWeg : double; AktTiefe, sp,ze,i: integer;
procedure Umsort(var a:tIndexArray;von:integer);
var i,j,temp : integer; begin For i := von+1 to Max-1 do For j := Max-1 downto i do If a[j-1]<a[j] then begin temp:= a[j]; a[j] := a[j-1]; a[j-1]:= temp; end; end;
function NextPermut(var a:tIndexArray): integer;
var k,j : integer; temp : integer; procedure swap(k,j :integer); begin temp := a[k]; a[k] := a[j]; a[j] := temp; end;
begin k := MAX-2; while (k>0) AND (a[k] > a[k+1]) do dec(k); result :=k; if k >0 then begin j := MAX-1; while a[k] > a[j] do dec(j); swap(j,k); j:=Max-1; inc(k); while j>k do begin swap(j,k); inc(k); dec(j); end; if result > j then result := j; end; end;
begin For ze := 1 to Max Do begin TempSum[ze] := 0.0; For sp := 1 to Max do If sp <> ze then else CheckArray[ze,sp] := 1e300; end;
For sp := 1 to Max-1 do AbgearbeitetIndex[sp] := sp+1; AbgearbeitetIndex[MAX] := 1;
Akttiefe := 1; MomMinWeg := 1e299; t0 := time;
repeat ze := 1; WegStrecke:= 0.0; if AktTiefe >1 then begin ze := AbgearbeitetIndex[AktTiefe-1]; WegStrecke:= tempSum[Akttiefe-1]; end;
for i := AktTiefe to MAX do begin Wegstrecke := Wegstrecke+CheckArray[ze,AbgearbeitetIndex[i]]; tempSum[i] := WegStrecke; if WegStrecke > MomMinWeg then begin Umsort(AbgearbeitetIndex,i); break; end; ze := AbgearbeitetIndex[i] end; IF Wegstrecke < momMinWeg then begin MomMinWeg :=WegStrecke; MomMinIndex:=AbgearbeitetIndex; end; AktTiefe := NextPermut(AbgearbeitetIndex); until AktTiefe <= 0;
writeln(MomMinWeg:10:5); ze := 1; for i := 1 to MAX do begin write(ze:2,' -> ',MomMinIndex[i]:2,CheckArray[ze,MomMinIndex[i]]:5:0,' '); For sp := 1 to Max do IF ze<>sp then write(CheckArray[ze,sp]:5:0)
else write(' XXX'); writeln; ze := MomMinIndex[i]; end;
writeln(FormatdateTime(' hh:mm:ss.zzz',time-t0)) ; readln; end. |
|
|
Fiete
      
Beiträge: 617
Erhaltene Danke: 364
W7
Delphi 6 pro
|
Verfasst: Sa 01.03.08 14:42
Moin Heiko,
zum TSP gibt es einen Algorithmus von Müller-Merbach, der schnell und eine gute Näherungslösung zur optimalen liefert.
Für Anzahl der Orte<11 hilft sicher alle Permutationen testen.
Das Verfahren startet mit zwei beliebigen Orten e[i] und e[k], durch die eine Rundreise (e[i],e[k],e[i]) mit den Kosten C[i,k]+C[k,i] definiert ist. Diese Subtour wird nun aufgebrochen, indem ein zusätzlicher Ort e[j] eingeschoben wird. Man erhält dann zwei mögliche Subtouren: (e[i],e[j],e[k],e[i]) und (e[i],e[k],e[j],e[i]).
Unter diesen wählt man die bessere aus und fährt dann fort, weitere Orte einzufügen. Ist z.B. (e[i],e[j],e[k],e[i]) die bessere Variante und wird e[p] hinzugenommen, so kann e[p] zwischen e[i] und e[j], zwischen e[j] und e[k] oder zwischen e[k] und e[i] gesetzt werden. Man wählt die beste von den drei Varianten aus, im nächsten Schritt die beste unter vier Möglichkeiten etc.
Im Anhang ist ein Beispiel.
Lösung mit Permutation Pascal Anno 1995
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55:
| {$R+,G+} PROGRAM RUNDREISE_MITTELS_PERMUTATIONEN; USES CRT; CONST N=10;M=11; TYPE FELD=ARRAY[1..M]OF INTEGER; VAR A,B:FELD; TABELLE:ARRAY[1..N,1..N]OF REAL; SYM,TAUSCH,AN,I,J:INTEGER; Z,MIN,KOSTEN:REAL; TASTE:CHAR; PROCEDURE BERECHNUNG; BEGIN KOSTEN:=0; FOR I:=1 TO AN-1 DO KOSTEN:=KOSTEN+TABELLE[A[I],A[I+1]]; KOSTEN:=KOSTEN+TABELLE[A[AN],A[1]]; IF KOSTEN<MIN THEN BEGIN B:=A;MIN:=KOSTEN END; J:=AN;Z:=Z+1 END; BEGIN CLRSCR;WRITE('Anzahl der Orte (2-',N:2,') ? '); REPEAT GOTOXY(27,1);CLREOL;READLN(AN) UNTIL (AN>1) AND (AN<M); WRITE('Ist die Tabelle symmetrisch (J/N) ? '); REPEAT TASTE:=UPCASE(READKEY) UNTIL TASTE IN['J','N'];WRITELN(TASTE); FOR I:=1 TO AN DO BEGIN GOTOXY(I*(3+N DIV AN),3);WRITE(I:3); GOTOXY(1,3+I*(N DIV AN));WRITE(I:2) END; FOR I:=1 TO AN DO BEGIN IF TASTE='J' THEN SYM:=I ELSE SYM:=1; FOR J:=SYM TO AN DO BEGIN GOTOXY(J*(3+N DIV AN),3+I*(N DIV AN)); IF I=J THEN TABELLE[I,J]:=0 ELSE READLN(TABELLE[I,J]); GOTOXY(J*(3+N DIV AN),3+I*(N DIV AN));WRITE(TABELLE[I,J]:3:0); IF TASTE='J' THEN BEGIN TABELLE[J,I]:=TABELLE[I,J]; GOTOXY(I*(3+N DIV AN),3+J*(N DIV AN));WRITE(TABELLE[I,J]:3:0) END END END; FOR I:=1 TO AN DO A[I]:=I;A[AN+1]:=1;Z:=0;MIN:=1E37;BERECHNUNG; WHILE J>2 DO BEGIN TAUSCH:=A[2];FOR I:=3 TO J DO A[I-1]:=A[I];A[J]:=TAUSCH; IF A[J]=J THEN J:=PRED(J) ELSE BERECHNUNG END; GOTOXY(1,20);WRITELN(Z:10:0,' Routen habe ICH geprüft !'); WRITELN('Kosten=',MIN:12:4); WRITE('Route: ');FOR I:=1 TO AN+1 DO WRITE(CHR(B[I]+64)) END. |
Lösung Müller-Merbach Pascal Anno 1989
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53:
| PROGRAM MUELLER_MERBACH; USES CRT; CONST N=19;M=20; TYPE FELD=ARRAY[1..M]OF INTEGER; VAR A,B:FELD; TABELLE:ARRAY[1..N,1..N]OF REAL; SYM,TAUSCH,K,L,P,AN,I,J:INTEGER; ZUWACHS,MIN,KOSTEN:REAL; TASTE:CHAR; BEGIN CLRSCR;WRITE('Anzahl der Orte (2-',N:2,') ? '); REPEAT GOTOXY(27,1);CLREOL;READLN(AN) UNTIL (AN>1) AND (AN<M); WRITE('Ist die Tabelle symmetrisch (J/N) ? '); REPEAT TASTE:=UPCASE(READKEY) UNTIL TASTE IN['J','N'];WRITELN(TASTE); FOR I:=1 TO AN DO BEGIN GOTOXY(I*(3+N DIV AN),3);WRITE(I:3); GOTOXY(1,3+I*(N DIV AN));WRITE(I:2) END; FOR I:=1 TO AN DO BEGIN IF TASTE='J' THEN SYM:=I ELSE SYM:=1; FOR J:=SYM TO AN DO BEGIN GOTOXY(J*(3+N DIV AN),3+I*(N DIV AN)); IF I=J THEN TABELLE[I,J]:=0 ELSE READLN(TABELLE[I,J]); GOTOXY(J*(3+N DIV AN),3+I*(N DIV AN));WRITE(TABELLE[I,J]:3:0); IF TASTE='J' THEN BEGIN TABELLE[J,I]:=TABELLE[I,J]; GOTOXY(I*(3+N DIV AN),3+J*(N DIV AN));WRITE(TABELLE[I,J]:3:0) END END END; A[1]:=1;A[2]:=1;FOR I:=3 TO AN+1 DO A[I]:=0; B:=A;KOSTEN:=0;ZUWACHS:=0; FOR I:=2 TO AN DO BEGIN A[I+1]:=A[I];A[I]:=I;MIN:=1E37; FOR J:=I DOWNTO 2 DO BEGIN K:=A[J-1];L:=A[J+1];P:=A[J]; ZUWACHS:=TABELLE[K,P]+TABELLE[P,L]-TABELLE[K,L]; IF ZUWACHS<MIN THEN BEGIN MIN:=ZUWACHS;B:=A END; TAUSCH:=A[J];A[J]:=A[J-1];A[J-1]:=TAUSCH END; KOSTEN:=KOSTEN+MIN;A:=B END; GOTOXY(1,22);WRITELN('Kosten=',KOSTEN:12:4); WRITE('Route: ');FOR I:=1 TO AN+1 DO WRITE(CHR(A[I]+64)) END. |
Gruß
Fiete
Einloggen, um Attachments anzusehen!
_________________ Fietes Gesetz: use your brain (THINK)
|
|
Horst_H
      
Beiträge: 1654
Erhaltene Danke: 244
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: Sa 01.03.08 19:37
Hallo,
ich bin gespannt, wieviel die Vorgehensweis von Heiko an Berechnungen spart.
Ich habe bei mir mal einen Zähler innerhalb der for Schleife innerhalb der repeat Schleife eingebaut.
Wenn man keine Zwischensumme und keinen vorherigen Abbruch bei Überschreiten des bisherigen minimalen Weges nutzt sind es genau 10 (i=1..MAX) *10! = 3268800 ~3,27 Mio Durchgänge
Durch Nutzung der Zwischensumme des ertsen Teilstückes, das man schon kennt, sind es noch 1,35 Mio.
zusätzlich Abbruch beim Überschreitung sind es noch 1,22 MIO. Umsort sparte 8000 also nichts, da dies auch bei den Werten fast nur im vorletzten Feld passiert,
Bei Heiko sind mit 82MB sind ja fast alle Wege Kombinationen komplett als Listenelemente in irgendeiner Liste im Hauptspeicher gelandet,m aber nicht rechtzeitig freigegeben worden.
Da läuft mit dem eigenem Speichermanager etwas mächtig schief.
Lange Rede keinen Sinn. Es macht scheinbar den größten Sinn, sich den vorherigen Weg zu merken. hier fast 60%.
Abbruch wegen Wegüberschreitung spart nur minimal ca 3 %
Natürlich ist das auch Effekt der lexikografischen Permutation.
Gruß Horst
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Sa 01.03.08 20:39
Es gibt auch eine Näherungslösung für das TSP unter Nutzung von Kohonen-Netzten (Neuronale Netze), die selbst für n=200 und größer noch in brauchbarer Zeit (~500 Iterationen) gute näherungslösungen liefert. Hab derzeit dafür aber nur Java-Source da, müsst ich erst portieren ...
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
Heiko 
      
Beiträge: 3169
Erhaltene Danke: 11
|
Verfasst: Sa 01.03.08 22:06
@Närhungslösungen: In em Falle geht es mir wirklich um die genaue Lösung. Nährunslösung ist hier leider nicht mehr gefragt  .
Horst_H hat folgendes geschrieben: | ich bin gespannt, wieviel die Vorgehensweis von Heiko an Berechnungen spart.
Ich habe bei mir mal einen Zähler innerhalb der for Schleife innerhalb der repeat Schleife eingebaut. |
Dürfte es einige Tests abkürzen. Zählst du die Schleifen innehralb der Hauptschleife mit, also bei mir fdas kopieren und so?
Horst_H hat folgendes geschrieben: | Und es wird auch der gleiche Weg gefunden, nur in umgekehrter Richtung( 5 kommt ja vor 7) jeweils 2330 LE lang. |
Japp, das dürfte nen kleines Prob sein. Evtl. wäre eine Voruntersuchung angebracht, ob der Graph metrisch ist oder nicht. Und wenn ja, da nen paar Stellen abkürzen (ich berechne im Prinzip ja alles doppelt).
Horst_H hat folgendes geschrieben: | Fürwahr Diene Version braucht über 82 MB Hauptspeicher kurz vor Ende der Berechnungen (390 Sekunden 90% Last da Musik läuft.. ). |
Ja, das liegt daran, dass ich viel Verschnitt zulasse, um nen Quäntchen Performance rauszuholen. Wenn ich diesen Verschnitt wegmachen würde, könnte ich meinen eigenen Speichermanager nicht mehr gebrauchen, da er auf Verschnitt basiert  .
Horst_H hat folgendes geschrieben: | Bei einem so dicht besetzten Graphen bietet sich doch ein Array an. |
Joar, ich könnte ein paar Zeiger einsparen.
PS @Horst: Hattest du nicht geschrieben gehabt, dass du mit deinem Verfahren einen anderen Weg gefunden hattest, der kürzer ist?
Ich glaube, ich muss den Code mal überarbeiten
aber heute wohl eher nicht mehr, nachdem ich 12h in einer Turnhalle gestanden hatte (Schul-Fußball-Hallenturnier  ).
|
|
|