Entwickler-Ecke

Algorithmen, Optimierung und Assembler - kgV mehrerer größerer Zahlen


zemy - Mi 16.03.05 22:01
Titel: kgV mehrerer größerer Zahlen
Ich will die Zeit von 1 Superkonvergenz zur nächsten Ausrechnen (alle Planis in Reihe) Die Zeitabstände zwischen den Konvergenzen 2 Planis waren schnell ermittelt. Hier die Werte:

Zitat:

T der Konvergenz zweier Planeten in Sekunden
Merkur und Venus: 12490723 Sekunden
Venus und Erde: 50449536 Sekunden
Erde und Mars: 67387895 Sekunden
Mars und Jupiter: 70542186 Sekunden
Jupiter und Saturn: 626771479 Sekunden
Saturn und Uranus: 1430239139 Sekunden
Uranus und Neptun: 5408462799 Sekunden


Wenn mehrere Planis in Reihe stehen sollen, isses ja einfach nur das KGV (kleinste Gemeinsame Vielfache). Blos wie ermittle ich das am effizientesten? Das Ergebnis ist ja größer als der Wertebereich von Int64... Habe es jetzt über Primfaktrorenzerlegung und (also auch mal interessehalber ne Frage in Richtung optimierung) Hier mein Code, der das errechnet:


Delphi-Quelltext
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:
  ZeroMemory(@Prims,sizeof(Prims));
  specialPrims:=1;

  for i := 0 to 7 do
    begin
      ZeroMemory(@tmpPrim,sizeOf(tmpPrim));
      rest:=collisions[i];
      divisor:=1;
      PrimFound:=false;

      repeat
        inc(divisor);
        if divisor>125000 then                // Prims > 125000 :(
          begin
            specialprims:=specialprims * rest;
            PrimFound:=true;
          end;

        if rest mod divisor = 0 then // Teiler?
          begin
            inc(tmpPrim[divisor]);
            rest:=rest div divisor;
            divisor:=1;
          end;
      until (rest=0or PrimFound;

      for j := 0 to 125000 do
        if Prims[j]<tmpPrim[j] then
          Prims[j]:=tmpPrim[j];
    end;


  // Extrem Multiplying
  tGes:=1*specialPrims;
  for i := 0 to 125000 do
    if Prims[i]<>0 then
      tGes:=tGes * Prims[i];

das kgv steht dann in tges als extended... (1,34090900765759E19, das es nen floatwert ist, stört nich, Abweichung ist OK)

nochmal die Fragen auf den Punkt gebracht:
a) stimmt das Ergebnis überhaupt? Habe da so meine Zweifel...
b) wie geht es eleganter? 2 Arrays über 125'000 Felder, die Mehrmals voll durchlaufen werden, kann wohl nicht die effizienteste Lösung sein :(

MfG Zemy


MiChri - Do 17.03.05 10:26

Moin
Meine Idee um das kgV effizient zu berechnen währe folgendes:

Quelltext
1:
2:
3:
4:
Eigabe A,B Ausgabe k
1. C:=GGT(A,B) //mit Euklidischem Algo
2. k=(A/C)*B
Fertig

Der Euklidische Algorithmus ist wahnsinnig schnell,er brauch für int64 maximal 64
Schritte (ein Schritt besteht aus einer Division, und einer Subtraktion).
Den Algo findest du übrigens überall im Internet z.B. hier Wikipedia [http://de.wikipedia.org/wiki/Euklidischer_Algorithmus]

Gruß Christian


jasocul - Do 17.03.05 10:30

Ich hätte das vermutlich anders gelöst.
Zwei Planeten nehmen. kgV berechnen. Dieses ist die neue Umlaufzeit. Dann den nächsten Planeten nehmen und mit dem neuen Wert das nächste kgV berechnen. u.s.w.

Theoretisch sollte das funktionieren. Habs aber jetzt nicht geprüft.


delfiphan - Do 17.03.05 13:42

Die Zahl wird eh grösser sein als die Lyapunov Zeit des Sonnensystems. Über 5-10 Millionen Jahren lassen sich keine sinnvollen Berechnungen über das Planetensystem machen, da es sich in dieser Grössenordnung chaotisch verhält.
Aus Spass kannst du es natürlich trotzdem mal tun, in nem vereinfachten Modell. Man sollte dann einfach nicht behaupten, dass in so und so vielen Jahren alle Planeten wirklich in einer Reihe stehen werden... ;)


Adrian - Do 17.03.05 14:35

Servus!

Nach dem Film "Tomb Raider" bzw. Fredl Fesls "Prophezeiung" habe ich mich auch schon mal an die Berechnung gemacht, allerdings auch ohne Berücksichtigung der chaotischen Verhältnisse. Genau weiß ich nicht mehr, was dabei rauskam, aber es war ein millionen- bis billiardenfaches der bisherigen Lebenszeit unseres Universums - also doch etwas mehr als die 5000 Jahre, die in dem Film angegeben waren.
Interessant wäre es aber immerhin - Aufgabe an die Astronomen! - wirklich zu berechnen, wann die Planeten in einer Reihe stehen, wobei natürlich sowohl die momentanen Positionen zu berücksichtigen wären, als auch die Tatsache, daß auch Planeten auf der "anderen" Seite der Sonne in die Reihenbildung einbezogen werden können.

Gruß,

Adrian


zemy - Do 17.03.05 18:09

Klar sind es nur Näherungswerte, was ich da berrechne.. Die Umlaufzeiten sind auch schon nur Näherungswerte und die Planeten werden als perfekte Kreisbahnen beschrieben, d.h. nichts mit Kepler usw.. Eine solche Konstelation ist allerdings nicht sooo selten.. Wenn man einen gewissen Toleranzwinkel einbezieht.

Back2Topic: Hatte das KgV auch erst über andere algorythmus... Ist allerdings zu groß gewurden die Zahl (größer als der Wertevorrat) und mit Float-Zahlen ist ein KGV nicht möglich wegen der fehlenden Signifikanten Ziffern. Obwohl: es könnte eigentlich in int64 reinpassen :D (so hart an der Grenze usw.)


Allesquarks - So 20.03.05 15:05

Hab Deinen Quelltext noch nicht vollständig verstanden. Allerdings würde ich sagen überprüfst Du einfach Deine Zahl (Rest) auf Teilbarkeit mi jeder Zahl. Dabei zählst du einfach durch. Könntest Dir aber die geraden Zahlen schenken (außer die Zahl 2).
In der Zweiten if-Routine setzt Du glaube ich, wenn Du einen Teiler gefunden hast den Divisor auf 1 zurück und zählst mit dem neuen Rest erneut durch?! Du wirst aber niemals einen neuen Primteiler finden als den den Du von unten zählend als letztes gefunden hast (Divisoren werden schneller größer ==> Rest wird schneller kleiner).
Das mit der 125000 hab ich noch nicht verstanden (ist das die Wurzel aus deinen Umlaufzeiten)?


zemy - So 20.03.05 19:02

Hätte ich vieleicht mit dazu schreiben sollen... Es wird sich eine Zahl genommen, in Primfaktoren zerlegt und veglichen, ob Ob die Anzahl größer als die schon bekannte. Wenn ja, wird der wert darauf gesetzt. Ich hatte aber einen Fehler im multiplizieren..



Delphi-Quelltext
1:
2:
3:
  tGes:=1*specialPrims;
  for i := 0 to 125000 do
    tGes:=tGes * power(i,Prims[i]);


So ist es richtig... bekomme ich aber 1,53189931634106E55 als Ergebnis raus, das passt dann in kein normales int mehr rein. die 125'000 ist (rund) die Wurzel von der Zeit zwischen Neptun und Pluto, also die größte in der Reihe. Ab da kann ich von ausgehen, das es ne Primzahl ist. Dieser Wert wird dann gleich mit übernommen. Es fehlt aber immernoch das prüfen, ob die Primzahl schon mal mit hinzugerechnet wurde... habe jetzt aber leider auch keine Zeit für :(

MfG Zemy