Entwickler-Ecke

Algorithmen, Optimierung und Assembler - Optimierung einer Primzahlfunktion


Kroni - Fr 26.11.04 17:24
Titel: Optimierung einer Primzahlfunktion
Hallo,

könntet ihr vielleicht mal einen Blick auf meine Funktion zum Testen, ob eine Zahl eine Primzahl ist werfen, ob das Testen vielleicht noch etwas schneller geht?

Hier einmal die Funktion:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
function prim(zahl:integer):boolean;  //Funktion zum Prüfen auf Primzahl
var teiler:integer;
    wurzel:extended;
begin
teiler:=1;
result:=true;
wurzel:=sqrt(zahl);

while ((teiler<=wurzel) and (teiler<>(zahl-1)) and result) do
      begin
      if (odd(zahl) or (zahl=2)) then
        begin
        teiler:=teiler+1;
        if ((zahl mod teiler)=0then
         result:=false;
        end
      else
        result:=false;
      end;
end;


Also ist ja schonmal, dass der nur die Ungeraden Zahlen auf Primzahl kontrolliert....und ansonsten weiß ich nicht genau, ob es schneller ist, die Schleife nur dann zu beginnen, wenn das ungerade ist, oder das in der Schleife abzufragen.
Ansonsten wird ja schon wurzel also sqrt(zahl) schon einmal ausgerechnet, das erspart ja auch schon zeit...
ansonsten wäre es sinnvoller, mit einer anderen Boolschen Variablen zu rechnen, und dann das Ergebnis dem Result zuzuweisen, oder direkt so wie ich es geamcht habe, mit Result zu rechnen?

Danke für eure Antworten


Moderiert von user profile iconChristian S.: Topic aus Sonstiges verschoben am Fr 26.11.2004 um 16:27


GTA-Place - Fr 26.11.04 19:12

Hab grad meine eigene Primzahl-Funktion geschrieben:


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:
function Prim (Zahl: Real): String;
var
  Zaehler: Integer;
  Wurzel: Integer;
begin
  if (Zahl = 2OR (Zahl = 3OR (Zahl = 5then
  begin
    Result := 'Prim';
    Exit;
  end;

  Wurzel := Round(sqrt(Zahl));
  Zaehler := 2;

  repeat
    if frac(Zahl / Zaehler) = 0 then
    begin
      Result := 'keine Prim';
      Exit;
    end;
    inc(Zaehler);
  until Zaehler = Wurzel;

  Result := 'Prim';
end;


Bin grad am testen der Geschwindigkeit.

EDIT: Etwa 1000 Zahlen pro Sekunde. Wäre das immer gleich schnell, wären nach 1 Minute 60000 Zahlen überprüft.

EDIT2: 63 Sekunden für 60000 Zahlen. Ich mach jetzt mal die Application.ProcessMessages nur noch alle 500 Zahlen (das heißt er muss jetzt dafür jedes prüfen, ob es 500 sind).

EDIT3: Mh... bringt nix, da man Application.ProcessMessages gar nicht braucht. Ich lasse jetzt nur noch Zahlen anzeigen, die Primzahlen sind (von 2 bis 60000).

EDIT4: 6 Sekunden.
Die größe gefundene Primzahl ist 224,036,583-1. Die Zahl wurde durch das Projekt GIMPS gefunden. Das Projekt GIMPS nutzt wie SETI die Leerlaufzeit der Computer, auf denen GIMPS installiert ist. Warum machen wir nicht auch so ein Projekt?

EDIT5: Für 6.000.000 Zahlen werden 5 Minuten und 2 Sekunden benötigt (wenn ich zu der Zeit etwas anderes mache). Da ich jetzt essen gehe, lasse ich 15 Millionen Zahlen prüfen.


Kroni - Fr 26.11.04 20:14

also gehts du im prinzip alle zaheln von eins bis was weiß ich durch, und guckst, ob die Primzahlen sind oder??

Na ja, dein Programm erinnert mich n bissl an deines*g* aber was mir gar nicht gefällt ist der EXIT Befehl..........Mein Lehrer würde mich dafür töten.
Aber gut, dass du frac benutzt, weil MOD ist ja ends langsam soweit ich weiß. ich gehe jetzt auch essen, bis später =)


st-matze - Fr 26.11.04 20:29


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:
function Prim (Zahl: Real): String;  
var  
  Zaehler: Integer;  
  Wurzel: double;  
begin  
  if (Zahl = 2OR (Zahl = 3OR (Zahl = 5then  
  begin  
    Result := 'Prim';  
    Exit;  
  end;  
//  Wurzel := Round(sqrt(Zahl));  //Runden kosten Zeit
  Wurzel := sqrt(Zahl);
  Zaehler := 2;  
  repeat  
//    if frac(Zahl / Zaehler) = 0 then  // frac ist "Arschlangsam"
// in Testläufen war der Test mit frac 10 - 30 mal langsamer als die modulo operation
    if (Zahl mod Zaehler) = 0 then
    begin  
      Result := 'keine Prim';  
      Exit;  
    end;  
    Inc(Zaehler);  
  until Zaehler >= Wurzel;  
  Result := 'Prim';  
end;


GTA-Place - Fr 26.11.04 20:32

Danke für die Optimierung, aber so darf Zahl nicht Real sein.


st-matze - Fr 26.11.04 20:35

is mir noch gar nicht aufgefallen, dass zahl real ist. nehmt do INT64


GTA-Place - Fr 26.11.04 20:39

Da geht Wurzelziehen nicht.

EDIT: Oh, du hast recht. Durch das weglassen von Round, kommen weniger Zahlen (und hoffentlich nur Primzahlen) und es sieht nur so aus, als wär es langsamer.

EDIT2: Wenn Round weg ist, sinds keine Primzahlen mehr.

EDIT3: Round muss wieder hin, habs grad geprüft und dann ist mod um 156 Millisekunden schneller bei 10-100.000.


st-matze - Fr 26.11.04 21:05


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:
function Prim (Zahl: Real): String;
var
  Zaehler: Integer;
  Wurzel: double;
  Z64: Int64;
begin
  if (Zahl = 2OR (Zahl = 3OR (Zahl = 5then
  begin
    Result := 'Prim';
    Exit;
  end;
  Wurzel := sqrt(Zahl);
  Zaehler := 2;
  Z64 := Trunc(Zahl);  // so kann man real und mod verwenden
  repeat
    if (Z64 mod Zaehler) = 0 then
    begin
      Result := 'keine Prim';
      Exit;
    end;
    Inc(Zaehler);
  until Zaehler > Wurzel;  // da war in der vorherigen variante der fehlerteufel
  Result := 'Prim';
end;


GTA-Place - Fr 26.11.04 21:11

Es macht einen Unterschied von 0.015 Sekunden, wenn ich leere Zeilen hab.

Und hier das OnClick-Ereignis:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
procedure TForm1.StartButClick(Sender: TObject);
var
  Start, Ende: Real;
  X: Integer;
  Erg: String;
begin
  Start := GetTickCount;

  for X := StrToInt(VonEdit.Text) to StrToInt(BisEdit.Text) do
  begin
    Erg := Prim(X);
    if Prim(X) = 'Prim' then
      PrimMemo.Lines.Add(IntToStr(X) + ': Prim');
  end;

  Ende := GetTickCount;
  DauerLab.Caption := 'Diese Überprüfung hat ' + FloatToStr((Ende - Start) / 1000) + ' Sekunden gedauert.'
end;


sourcehunter - Fr 26.11.04 21:13

Ich weiß nicht, wieviel es hilft, aber du könntest noch auf Teilbarkeit durch kleine Primzahlen testen, so zwischen 1..10.


GTA-Place - Fr 26.11.04 21:17

Das wird ja eigentlich gemacht, da immer von 2 bis zur Zahl geprüft wird.

PS: Oben hab ich noch das OnClick-Ereignis gepostet.


st-matze - Fr 26.11.04 21:18

und die letzte Variante für heute:


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:
function Prim (Zahl: Real): String;
var
  Zaehler: Integer;
  Wurzel: double;
  Z64: Int64;
begin
  Z64 := Trunc(Zahl);
  if Odd(Z64) then
  begin
    Result := 'Prim';
    Exit;
  end;
  Wurzel  := sqrt(Zahl);
  Zaehler := 3;        
  repeat
    if (Z64 mod Zaehler) = 0 then
    begin
      Result := 'keine Prim';
      Exit;
    end;
    Inc(Zaehler,2);     //nur ungerade teiler prüfen
  until (Zaehler > Wurzel);
  Result := 'Prim';
end;


Anmerkung:
stringzuweisungen dauern auch sehr lange, weil bei jeder zuweisung neuer speicher belegt wird.

wie wärs stattdessen mit:


Delphi-Quelltext
1:
2:
3:
4:
5:
function Prim (Zahl: Real): boolean;
begin
...
Result:= true;
end;


Ich kenne ja euer rahmenprogramm nicht, ob dort der string unbeding nötig ist.

Frage:

speichert ihr eure primzahlen eigentlich?!?

Edit: Dann könnt ihr nähmlich immer nur die teilbarkeit durch die bereits gefundenen zahlen prüfen.


Kroni - Fr 26.11.04 21:19

macht doch anstatt exit einfach mal mit eine Abbruchbedingung in die Schleife...würd ich schöner finden.
ansonsten klingt das recht logisch und ist ja genauso aufgebaut wie mienes......ist denn eigentlich zahl mod 2 schneller als odd(zahl)???
wobei ich mal auch meine gelesen z7u haben, dass mod so sehr langsam sein soll....ist trunc schneller???
das endet hier ja in nem Contest, wer hat am schnellsten die meisten Primzahlen*ggg*
mit Int64 kann ich leider nicht arbeiten, da ich Delphi3 Pro habe*heul*


GTA-Place - Fr 26.11.04 21:22

1. Wieso "eure"? "deine" muss es heißen, oder meinst du noch das vom Thread-Ersteller?
2. Hab mir gedacht, wenns ein gr. Projekt wird, wird ein Computer mit dem Prog. drauf, z.B. die Zahlen zw. 100 und 200 überprüfen und ein andere Computer, die Zahlen zw. 200 u. 300 überprüfen und dann an den Server schicken, das Ergebnis.


GTA-Place - Fr 26.11.04 21:27

@st-matze: So wird mir 4 und 4*x als Prim angezeigt.


Delphi-Quelltext
1:
2:
3:
4:
5:
  if (Zahl = 2OR (Zahl = 3OR (Zahl = 5then
  begin
    Result := 'Prim';
    Exit;
  end;

Das hab ich verwendet, weil "Wurzel aus 2, 3 und 5 < Zaehler". 5 geht jetzt glaub, da Round fehlt.


Kroni - Fr 26.11.04 21:30

also.....@st-matze
guck dir mal mein Primzahlprogramm an.....bzw die Funktion...da war mein Rückgabewert auch ein Boolean....ist für mich eigentlich Logischer zu sagen,m ob Zahl x true or false ist.....finde ich...
Ansonste hatte ich eigentlich vor, die Ausgabe nich in die Schleife einzubauen, wo ich meinetwegn von 1 bis 1000 gehe um zu gucken, welche Zahlen davon Primzahl sind, weil das total langsam ist => Grafik in Delphi ist wirklich verdammt langsam.


GTA-Place - Fr 26.11.04 21:34

Ich hab String in Boolean geändert.


GTA-Place - Fr 26.11.04 21:43

Ich lass jetzt nur noch die neuste gefundene Primzahl anzeigen.

EDIT: Der zeigt ja gar keine Primzahlen an:
Zitat:
83: Prim
85: Prim
87: Prim
89: Prim
91: Prim
93: Prim
95: Prim
97: Prim
99: Prim


Kroni - Fr 26.11.04 21:46

wie wärs denn, wenn wir n dynamische array nehmen und die primzahlen da alle abspeichern? oder mit einer TStringList oder so??
dynamisches array wollte ich ja eigentlich auch schon machen aba mit delphi 3 gehts alles nich :evil:


GTA-Place - Fr 26.11.04 21:50


Delphi-Quelltext
1:
Inc(Zaehler, 2)                    

Das geht nicht, Zaehler muss um eins erhöht werden.

Jo in einer Stringlist. Die kann man dann auch am Schluß abspeichern.


Kroni - Fr 26.11.04 21:52

weist du denn, wie man ne StringList macht??
du musst dann ja eine createn usw.....grob weiß ichs, aba habs noch nie ausprobiert.....
dann kannst du nachdem du alle Ermittelt hsat quasi erst dann alle Primzahlen ausgeben lassen oder wie auch immer...oder in einer Textdatei speichern....wäre von Vorteil oda?


GTA-Place - Fr 26.11.04 22:01

Genau. Jo, ich weiß wie es geht.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
var
  Prim: TStringList;
begin
  Prim := TStringList.Create;
  Prim.Add(Primzahl);
  Prim.SaveToFile('Pfad');
  Prim.Free;
end;


Kroni - Fr 26.11.04 22:03

aber du gehst doch dann auch von meinetwegen um die Primzahlen zwischen 1 und 100000 herauszufinden alle Zahlen durch,....und guckst dann ob das Primzahlen sind, wobei du ja eigentlich von 9 aus gesehen jede zweite Zahl Prüfen musst oder hab ich mich da grad vertan?


GTA-Place - Fr 26.11.04 22:10

Fast. Wie ich bemerkt habe, reicht es nicht nur jede 2. Zahl zu prüfen.


Kroni - Fr 26.11.04 22:13

doch doch....wenn du von neun ausgehst....dann reicht es doch vollkommen...oda hast du mal eine gerade zahl außer zwei gesehen, die Primzahl ist?


GTA-Place - Fr 26.11.04 22:17

Ich weiß warum es net ging bei mir: Zaehler := 2
Wenns mit 3 beginnt stimmts: 3, 5, 7, 9, 11, 13,...

EDIT: So wird mir aber immer 4*x angezeigt als Prim.


Kroni - Fr 26.11.04 22:18

na ja, vertrau mir mal, einmal ein einziges mal^^ und du wirst sehen, du braucht nur die Hälfte der Zeit, sitmmts? *gggg*


GTA-Place - Fr 26.11.04 22:20

Ne, wird auch 4, 8, 16, 20,... als Prim angezeigt.


Kroni - Fr 26.11.04 22:21

stell nochma den kompletten code online bidde.....dann kann ich dir sagen worans liegt*hoff*
das kann doch nich sein....du musst doch nur die ungeraden zahlen prüfen....wie kommen dann überhaupt die geraden zahlen in die Überprüfung?????


GTA-Place - Fr 26.11.04 22:24


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:
function Prim (Zahl: Real): Boolean;
var
  Zaehler: Integer;
  Wurzel: Double;
  Z64: Int64;
begin
  if (Zahl = 2OR (Zahl = 3then
  begin
    Result := True;
    Exit;
  end;
  Wurzel := sqrt(Zahl);
  Zaehler := 3;
  Z64 := Trunc(Zahl);
  repeat
    if (Z64 mod Zaehler) = 0 then
    begin
      Result := False;
      Exit;
    end;
    Inc(Zaehler, 2);
  until Zaehler > Wurzel;
  Result := True;
end;


Kroni - Fr 26.11.04 22:26

ja, das ist ja die funktion...aba mit welchem code rufst du die Prüfungsfunktion auf?????
in dem code, mit dem du die Zahl, die du prüfst erhöst, darin muss der Fehler sein!


GTA-Place - Fr 26.11.04 22:30


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
  for X := StrToInt(VonEdit.Text) to StrToInt(BisEdit.Text) do
  begin
    if Prim(X) then
      PrimS.Add(IntToStr(X));
      Application.ProcessMessages;
      PrimLab.Caption := 'Aktuelle Primzahl: ' + IntToStr(X);
  end;


Kroni - Fr 26.11.04 22:31


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:
function Prim (Zahl: Real): Boolean;
var
  Zaehler: Integer;
  Wurzel: Double;
  Z64: Int64;
begin
  if (Zahl = 2OR (Zahl = 3then
  begin
    Result := True;
    Exit;
  end;
  Wurzel := sqrt(Zahl);
  Zaehler := 2;
  Z64 := Trunc(Zahl);
  repeat
    if (Z64 mod Zaehler) = 0 then
    begin
      Result := False;
      Exit;
    end;
    Inc(Zaehler);//Du musst doch jede Zahl durchgehen, ob sie Teiler ist!!!!! und dann bitte auch von zwei an....dann muss das funzen
  until Zaehler > Wurzel;
  Result := True;
end;


GTA-Place - Fr 26.11.04 22:36

Hatte ich die ganze Zeit, bis der eine, den neuen Code gepostet hat.


Kroni - Fr 26.11.04 22:49

ok....hab grad mal über was nachgedacht:

1. du kontrollierst nur zahlen, wenn sie ungerade sind und gleich zwei.....
2. wenn die zahl also gerade ist, dann ist die Zahl keine Primzahl...bis auf die Zwei
3. Wenn du also allgemein die Ungeraden Zahlen kontrollierst, fängst du bei der Drei an....und dann musst du nur durch ungerade Teiler teilen......
Das wärs....
das wäre dann die schnellste Version die mir einfällt....
Klingt logsich oda?


GTA-Place - Fr 26.11.04 22:51

Und wie siehts jetzt im Sourcecode aus?


Kroni - Fr 26.11.04 22:57


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
function prim(zahl:integer):boolean;
var teiler:integer;
    wurzel:extended;
begin
result:=true;
teiler:=3;
if ((zahl mod 2)=0then  //Zahl gerade
 result:=false
else
  begin
  if (zahl=2then
    exit
  else
    begin
    while ((teiler<=wurzel) and result) do
      begin
      if ((zahl mod teiler)=0then
        result:=false;
      inc(zahl,2);
      end;
    end;
end;

versuchs ma...habs grad nich getestet


GTA-Place - Fr 26.11.04 23:32

Umgeschrieben siehts so aus:

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:
function Prim (Zahl: Real): Boolean;
var
  Zaehler: Integer;
  Wurzel: Double;
  Z64: Int64;
begin
  Z64 := Trunc(Zahl);
  if (Zahl = 2OR (Zahl = 3then
    Exit;
  if (Z64 mod 2 = 0OR (Zahl = 1then
  begin
    Result := False;
    Exit;
  end;
  Wurzel := sqrt(Zahl);
  Zaehler := 3;
  repeat
    if (Z64 mod Zaehler) = 0 then
    begin
      Result := False;
      Exit;
    end;
    Inc(Zaehler, 2);
  until Zaehler >= Wurzel;
  Result := True;
end;


1-100.000 dauert 9,906 Sekunden.

Wenn man das ganze mit ASM macht dauert es 9,968.
Unsere Fumktion ist besser ^^


GTA-Place - Fr 26.11.04 23:55

Wollt ihr wissen, wieviel jetzt noch für 1-100.000 benötigt wird? 1,188 Sekunden.


Benutzername - Sa 27.11.04 00:32

Ich brauche etwa zwischen 93 und 109 ms, irgendwas past da nicht :-D


Kroni - Sa 27.11.04 00:33

du du du.....
lass doch als Eingabe einer Zahl doch auch direkt nur INT64 zu....erspart auch nochmal zeit....
ansonsten bemerke ich mal, dass 1 laut definition auch eine Primzahl ist (1 ist durch sich selber und eins teilbar)...sowie die Null auch....also mach dann einfach eine da, wo zahl=2 or zahl=3 steht...einfach if zahl<4 then....
ansonsten ist das ja so grob mein Programm;-)
Was hast du denn für eine Taktfrequenz am Rechner??
Ansonsten würd ich, wenn du die Funktion aufrufst....auch nur direkt die Geraden Zahlen rausfiltern...also anstatt eine
FOR i:=start to endwert do

einfach ne while (i<=endwert) do
bla und adnn mit einer ungeraden zahl anfangen und dann i um zwei erhöhen...erspart auch nochmal ein paar millisekunden....


sourcehunter - Sa 27.11.04 00:37

Brauchst du wirklich INT64? Also, wenn nich, dann nimm Integer, denn das ist viel schneller, weil ein 32-Bit-Prozessor einen 64-Bit-Integer emulieren muss. Oder reichen dir etwa 4 Mrd. nicht?


Kroni - Sa 27.11.04 00:38

Zitat:

Ich brauche etwa zwischen 93 und 109 ms, irgendwas past da nicht :-D

mit welcher Funktion denn?
und wofür?*g*
genaue Angaben wären mal nich schlecht;-)


GTA-Place - Sa 27.11.04 00:50


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
function Prim (Zahl: Integer): Boolean;
var
  Teiler: Integer;
  Wurzel: Double;
begin
  Teiler := 3;
  Wurzel := sqrt(Zahl);
  while ((Teiler <= Wurzel) AND (result)) do
  begin
    if Zahl mod Teiler = 0 then
    begin
      Result := False;
      Break;
    end;
    inc(Teiler, 2);
  end;
end;



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:
procedure TMainForm.StartButClick(Sender: TObject);
var
  Start, Ende: Real;
  X, Von, Bis: Integer;
begin
  PrimS := TStringList.Create;
  PrimS.Add('2');
  PrimS.Add('3');

  Von := StrToInt(VonEdit.Text);
  Bis := StrToInt(BisEdit.Text);

  if Von < 4 then
    Von := 4;

  Start := GetTickCount;

  for X := Von to Bis do
  begin
    if odd(X) then
    begin
      if Prim(X) then
      begin
        PrimS.Add(IntToStr(X));
        Application.ProcessMessages;
        PrimLab.Caption := 'Aktuelle Primzahl: ' + IntToStr(X);
      end;
    end;
  end;

  Ende := GetTickCount;
  DauerLab.Caption := 'Diese Überprüfung hat ' + FloatToStr((Ende - Start) / 1000) + ' Sekunden gedauert.';

  PrimS.SaveToFile('test.txt');
  PrimS.Free;
end;


Kroni - Sa 27.11.04 00:57

ich muss dich verbessern.....ich würde die abfrage ob das nun unter drei ist mit in der Funktion machen...denn so , wenn du meinetwegen zahlen von 5 bis 10 eingbist, erscheinen die Zahlen 1 2 3 immer....das ist ja nich sinn der Sache


GTA-Place - Sa 27.11.04 01:00

Habs jetzt so, aber nicht in der Funktion:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
  if Von < 4 then
  begin
    Von := 4;
    PrimS.Add('1');
    PrimS.Add('2');
    PrimS.Add('3');
  end;


Kroni - Sa 27.11.04 01:04

immer noch nich ok.....wiel wenn du drei eingibst wird doch auch 1 2 3 angezeigt...mach doch in der Funktion ne Abfrage...

Delphi-Quelltext
1:
2:
if (zahl<4then
exit;

Fertig....


GTA-Place - Sa 27.11.04 01:11

Jetzt wollen wir mal nicht so kleinlich sein ^^
Ob der jetzt 1, 2, oder 3 (letzte Chance vorbei... Ach ne, das war Kinderkanal, wir sind ja im Delphiforum... :roll: ) eingibt, ist doch eigentlich egal. Und wenn ich es in die Funktion schreibe, dann dauert es länger. Ich kanns ja trotzdm noch umschreiben:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
  if Von = 1 then  
  begin  
    Von := 4;  
    PrimS.Add('1');  
    PrimS.Add('2');  
    PrimS.Add('3'); 
  end;

  if Von = 2 then  
  begin  
    Von := 4;  
    PrimS.Add('2');  
    PrimS.Add('3'); 
  end;

  if Von = 3 then  
  begin  
    Von := 4;   
    PrimS.Add('3'); 
  end;



PS: Für 10 Mio Zahlen braucht das Prog. jetzt 82,984 Sekunden, bisschen schneller als gestern Abend ( :wink: schon 10 Minuten Samstag :wink: ).


Kroni - Sa 27.11.04 01:17

na ja....schreib die Function mal um...und dann mess mal die Zeit;-)


GTA-Place - Sa 27.11.04 01:20

Haha: Um 0,000000000001 Millisekunden langsamer ^^.


Kroni - Sa 27.11.04 01:33

na ja....dann solltest du aba deineProzedur anders schreiben da oben....das geht noch kürzer....aba ich schreib die mal nun wieich das machen würd......
edit: ansonsten solltest du mal noch ne Eingabeabsicherung einbaeun, und nen Zähler....damit du weist, wie viele Primzahlen bzw ob Überhautp eine Primzahl ermittelet wurde.....


GTA-Place - Sa 27.11.04 01:37

Ich habs jetzt in die Funktion eingebaut.
Was meinst du mit Eingabesicherung, was soll gesichert sein?


Kroni - Sa 27.11.04 01:45

na....dass dann zb mal anstatt eine Zahl ein Buchstabe ins Edit Feld eingegeben wird.....dass dann eine Fehlermeldung erscheint....das gehört ja normal zu jedem Programm.


GTA-Place - Sa 27.11.04 01:47

Jo, das kommt ja noch alles...


Kroni - Sa 27.11.04 02:01


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
function Prim (Zahl: Integer): Boolean;
var
  Teiler: Integer;
  Wurzel: Double;
begin
  Teiler := 3;
  Wurzel := sqrt(Zahl); 
  result:=true;  //Hier solltest du auf jeden fall vorher result auf true setzten...sonst ist die Beleggung ja dem Zufall überlassene
  while ((Teiler <= Wurzel) AND (result)) do
  begin
    if Zahl mod Teiler = 0 then
    begin
      Result := False;
      Break;
    end;
    inc(Teiler, 2);
  end;
end;


GTA-Place - Sa 27.11.04 02:04

Eigentlich ist es net dem Zufall überlassen, denn Delphi setzt automatisch Result auf True;


Kroni - Sa 27.11.04 02:06

echt? wusst ich nich......würd ich aba trotzdem machen....;-)


GTA-Place - Sa 27.11.04 02:16

Das machen wir dann alles morgen, aber jetzt geh ich erstmal in's Bett.
Gute Nacht!


Gausi - Sa 27.11.04 04:58

Kroni hat folgendes geschrieben:
ansonsten bemerke ich mal, dass 1 laut definition auch eine Primzahl ist (1 ist durch sich selber und eins teilbar)...sowie die Null auch....also mach dann einfach eine da, wo zahl=2 or zahl=3 steht...einfach if zahl<4 then....

Aus mathematischer Sicht ist das falsch. 1 ist keine Primzahl, sondern eine "Einheit". Also eine Zahl, die man mit einer anderen (ganzen) Zahl zu 1 multiplizieren kann. -1 ist auch eine Einheit. 1 ist speziell, weil es im "Ring" der ganzen Zahlen das "neutrale Element" der Multiplikation ist, d.h. 1*x=x für alle x.
0 ist ebenfalls keine Primzahl, sondern das neutrale Element der Addition, d.h. x+0=0 für alle x.
Und diese "besonderen Zahlen" sind aus der Definition der Primzahlen ausgenommen.

[genug kluggeschissen, viel Erfolg mit dem Programm]


GTA-Place - Sa 27.11.04 09:16

Danke.
Jetzt ist die Frage: Machen wir ein gemeinsames Projekt, oder machst du das weiter, wofür du die Primzahlfunktion benötigt hast und ich mach mein eigenes Projekt?


MrSaint - Sa 27.11.04 09:19

OT
Gausi hat folgendes geschrieben:
1 ist speziell, weil es im "Ring" der ganzen Zahlen das "neutrale Element" der Multiplikation ist, d.h. 1*x=x für alle x.
0 ist ebenfalls keine Primzahl, sondern das neutrale Element der Addition, d.h. x+0=0 für alle x.


*schüttel* das erinnert mich grad an Körperaxiome... igittigitt ;)


ScorpionKing - Sa 27.11.04 09:23

Mal eine andere Sache!
Geht folgende Funktion nicht auch um Primzahlen auszurechnen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
function prim(i: integer): boolean;
var i2:integer;
begin
i2 := i mod 2;
if i2 > 0 then
begin
result := true;
end;
end;


GTA-Place - Sa 27.11.04 09:27

Noch ne Frage: Irgendwer hat gestern gesagt, dass wenn ich die Primzahlen gespeicher hab, muss ich nur noch durch die Primzahlen teilen. Wie mach ich das genau?

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
  while ((Teiler <= Wurzel) AND (Result)) do
  begin
    if Zahl mod StrToInt(PrimS.Strings[Teiler]) = 0 then
    begin
      Result := False;
      Break;
    end;
    inc(Teiler, 2);
  end;

Viel zu langsam und auch noch falsch.


GTA-Place - Sa 27.11.04 09:34

@ScorpionKing: Zeigt 1. falsche Zahlen an und 2. man braucht 23 Sekunden mehr (23 Sek zu 0,9 Sek) und 3. kommt das daher, weil wenn du z.B. die 15 hast:

15 mod 2 = 7 Rest 1

Der Rest ist höher als 0, aber 15 ist keine Primzahl.


Phantom1 - Sa 27.11.04 10:15

So jetzt will ich auch ma:

also eure function braucht bei 0 bis 100000 etwa 100 ms.
meine Function braucht dafür nur etwa 65 ms.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
function isPrim(Zahl: Cardinal): Boolean;
var Teiler, MaxTeiler: Cardinal;
begin
  if zahl<2 then begin
    Result:=False;
    Exit;
  end;

  Result:=True;
  if Zahl=2 then Exit;

  MaxTeiler:=Trunc(Sqrt(Zahl));

  for Teiler:=2 to MaxTeiler do
    if Zahl mod Teiler = 0 then begin
      Result:=False;
      Break;
    end;
end;


mfg


Johannes Maier - Sa 27.11.04 11:12

Gausi hat folgendes geschrieben:
Kroni hat folgendes geschrieben:
ansonsten bemerke ich mal, dass 1 laut definition auch eine Primzahl ist (1 ist durch sich selber und eins teilbar)...sowie die Null auch....also mach dann einfach eine da, wo zahl=2 or zahl=3 steht...einfach if zahl<4 then....

Aus mathematischer Sicht ist das falsch. 1 ist keine Primzahl, sondern eine "Einheit". Also eine Zahl, die man mit einer anderen (ganzen) Zahl zu 1 multiplizieren kann. -1 ist auch eine Einheit. 1 ist speziell, weil es im "Ring" der ganzen Zahlen das "neutrale Element" der Multiplikation ist, d.h. 1*x=x für alle x.
0 ist ebenfalls keine Primzahl, sondern das neutrale Element der Addition, d.h. x+0=0 für alle x.
Und diese "besonderen Zahlen" sind aus der Definition der Primzahlen ausgenommen.

[genug kluggeschissen, viel Erfolg mit dem Programm]

Also meiner Meinung nach lautet die Definition einer Primzahl: Eine Zahl, die genau zwei Teiler hat. Und da sind auch 0 und 1 ausgenommen. Die bekannte Definition "Eine Zahl, die nur durch 1 und sich selbst teilbar ist" ist falsch ... ;)
Aber das mit der Einheit ist auch logisch, nur verstehe ich dann nicht, wieso du sagt: Aus der Definition der Primzahlen ausgenommen? In der richtigen Definition ist sie gar nicht drin ;)


Johannes Maier - Sa 27.11.04 11:17

Phantom1 hat folgendes geschrieben:
So jetzt will ich auch ma:

also eure function braucht bei 0 bis 100000 etwa 100 ms.
meine Function braucht dafür nur etwa 65 ms.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
function isPrim(Zahl: Cardinal): Boolean;
var Teiler, MaxTeiler: Cardinal;
begin
  if zahl<2 then begin
    Result:=False;
    Exit;
  end;

  Result:=True;
  if Zahl=2 then Exit;

  MaxTeiler:=Trunc(Sqrt(Zahl));

  for Teiler:=2 to MaxTeiler do
    if Zahl mod Teiler = 0 then begin
      Result:=False;
      Break;
    end;
end;


mfg


Hmm habe jetzt nichts zum Testen da, aber wäre es nicht vielleicht schneller, die for-Schleife so zu Schreiben:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
for Teiler := 2 to MaxTeiler do begin
  if Zahl mod Teiler = 0 then begin
    Result := False;
    Break;
  end;
  Inc(Teiler);
end;

Das Inc frisst noch ein bisschen Zeit, aber vll. wird das dadurch aufgehoben, dass man nur noch halb so oft mod verwendet ....
Btw: Vll. ist deine Funktion einfach schneller, weil du Cardinal verwendest ^^


GTA-Place - Sa 27.11.04 12:43

Am Cardinal liegts net (ausprobiert), aber an was dann?

Und wie mach ich das hier schneller:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
  while ((Teiler <= Wurzel) AND (Result)) do
  begin
    if Trunc(Zahl / StrToInt(PrimS.Strings[Teiler])) = 0 then
    begin
      Result := False;
      Break;
    end;
    inc(Teiler, 1);
  end;


Gausi - Sa 27.11.04 13:11

Zitat:
Also meiner Meinung nach lautet die Definition einer Primzahl: Eine Zahl, die genau zwei Teiler hat. Und da sind auch 0 und 1 ausgenommen.

Geht auch nicht undnedingt. Wenn mich meine Erinnerung an Zahlentheorie nicht trügt, dann geht das so:
Eine Element (ine ganze Zahl) p heißt prim, wenn aus p=a*b folgt: a ist eine Einheit oder b ist eine Einheit.

Z.B. kann man in den ganzen Zahlen 5 nur so zerlegen: 5=1*5 oder 5=-1*-5. Und 1 und -1 sind Einheiten, also ist 5 eine Primzahl. 6 ist keine, denn 6=2*3, und weder 2 noch 3 sind Einheiten.


GTA-Place - Sa 27.11.04 13:50

Hab mir mal die Unit BigIntegers runtergeladen. Jetzt kann ich größere Zahlen brechnen.

EDIT: Ach das ist ein scheiß. Mal schauen, ob ich so ne Unit selber hinbekomm.


Gausi - Sa 27.11.04 14:07

GTA-Place hat folgendes geschrieben:
Jetzt kann ich größere Zahlen brechnen.

Auch auf die Gefahr hin, dass ich mit meinem Mathe-Gedöns auf die Nerven gehe: Wenn du größere Zahlen durchgehen willst, dann wird dein Test langsam aber sicher uneffizient. Für Int64 mag das naive Verfahren gut gehen, aber wenn man noch (weit) darüber hinaus möchte, wäre es evtl. sinnvoll, mit etwas feinerem als einem Hammer an die Sache ranzugehen.
Als Einstiegspunkt kann ich Suche bei Google "MILLER-RABIN" angeben. Steckt ne Menge Mathematik dahinter, aber es findet sich bestimmt auch irgendwo einfach nur das Verfahren, ohne das ganze drumherum zu beweisen.


Christian S. - Sa 27.11.04 14:30

Suche in: Delphi-Forum, Delphi-Library DIE SUCHE NACH DEN BEIDEN liefert z.B. diesen [http://www.delphi-forum.de/viewtopic.php?p=44435#44435] Beitrag. :-)


GTA-Place - Sa 27.11.04 15:17

Application.ProcessMessages wird jetzt nur noch alle 10000 Zahlen aufgerufen:

Delphi-Quelltext
1:
2:
3:
4:
5:
if X = Step then
        begin
          Application.ProcessMessages;
          inc(Step, 10000);
        end;


1-1000000:
jedes Mal App.: 8,719 Sekunden
nur alle 10000 Zahlen, App.: 2,64 Sekunden


Kroni - Sa 27.11.04 16:24

na ja, ist das einzig fehlerhafte an meiner mathematik die Primzahldefiniton??*heulz*
wenn ihr mir das beweisen könnt, dass 1 und 0 nich dazugehört glaub ich euch das...dann werd ich das auch mal meinen Lehrern zeigen....
Das andere Verfahren will ich mir auch mal angucken....weil ja klar weiß ich, dass unser bisheriges Verfahren ends langsam ist....klar geht das schneller...ich hab auch schon so die Idee wie das gehen könnt mit dem Teilen durch Primzahlen, die kleiner als die Zahl sind....würd auch n Haufen Zeit sparen.
Aber für mich war nur der Reiz da, mal so etwas auf minimalster Zeit aufzubauen....
@GTA:
Wir können uns gerne noch zusammen was überlegen....kein Thema=) hast mich ja schon in ICQ geaddet
edit: bin bis heute abend weg, muss ncoh unsern Laptop reparieren und noch so einige andere Dinge erledigen


patrick - Sa 27.11.04 16:41

ich kann zwar nicht gerade nicht mit eigenem code aufwarten (bin auf dem sprung)aber wenns um primzahlen geht sind diese seiten das richtige:
http://www.mersenne.org/source.htm
und hier kann man nochmal nen anderen pascal source für die primzahlenfindung downloaden:
http://www.codepedia.com/zone24/cat416/16883.htm

ob dieser schneller oder langsamer ist? keine ahung aber von der struktur her glaub ich er müste langsamer sein


BenBE - Sa 27.11.04 17:30

Naja, ich hab das in nem recht alten Projekt mal folgendermaßen gemacht:


PrimProj.dpr
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:
Program PrimProj;

{$APPTYPE CONSOLE}

Uses
    SysUtils,
    Classes,
    Forms;

Function MkLang(Zahl, Ziffern: Integer): String;
Var
    Str: String;
    X: Integer;
Begin
    For X := 1 To Ziffern Do
        Str := Str + '0';
    Result := Copy(Str + IntToStr(Zahl), Length(IntToStr(Zahl)) + 1, Ziffern);
End;

Type
  TArrayInt64 = Array[0..0Of Int64;
  PArrayInt64 = ^TArrayInt64;
Var
  FS: TFileStream;
  Primzahlen: PArrayInt64;
  CurrNum, CurrPrimIdx, MaxPrim, MaxTest: Int64;
  IsPrim, DoTest: Boolean;
  TmpSqrt: Extended;
  Tmp: String;
  STime, ETime: TDateTime;
  PrimCount, PrimMemSize: Integer;

  MS, SS, MM, HH, DD: WORD;
Begin
  MaxPrim := 0;
  While MaxPrim = 0 Do
  Try
    WriteLn('Bis zu welcher Zahl wollen Sie die Primzahlen ermitteln???');
    ReadLn(MaxPrim);
  Except
    MaxPrim := 0;
    Continue;
  End;
  STime := Now;
  GetMem(Primzahlen, 65536);
  PrimMemSize := 65536;
  CurrNum := 2;
  Primzahlen[0] := CurrNum;
  PrimCount := 0;
  Inc(CurrNum);
  Try
    Try
      While CurrNum <= MaxPrim Do
      Begin
        TmpSqrt := CurrNum;
        TmpSqrt := Sqrt(TmpSqrt); //Maximaler Testfaktor
        MaxTest := Round(TmpSqrt);

        IsPrim := true; //Primzahl = ja
        CurrPrimIdx := 0//Aktuelle Position
        DoTest := CurrPrimIdx <= PrimCount; //Im Bereich???

        If DoTest Then //Wenn ja, ...
        Begin
          IsPrim := (TmpSqrt <> Trunc(TmpSqrt));
          DoTest := IsPrim And (Primzahlen[CurrPrimIdx] <= MaxTest);
        End;

        While DoTest Do
        Begin
          IsPrim := CurrNum Mod Primzahlen[CurrPrimIdx] <> 0;
          Inc(CurrPrimIdx); //Nächste Primzahl testen...
          DoTest := IsPrim And (CurrPrimIdx <= PrimCount); //Noch im Bereich???
          If DoTest Then //Wenn ja, ...
          Begin
            DoTest := Primzahlen[CurrPrimIdx] <= MaxTest;
          End;
        End;

        If IsPrim Then
        Begin
          If PrimCount shl 3 + 8 >= PrimMemSize Then
          Begin
            PrimMemSize := PrimMemSize Shl 1;
            ReallocMem(Primzahlen, PrimMemSize);
          End;
          Inc(PrimCount);
          Primzahlen[PrimCount] := CurrNum;
          If PrimCount And $FFF = 0 Then
            Write(IntToStr(CurrNum) + #9);
        End;
        Inc(CurrNum, 2);
      End;
      ETime := Now;
      WriteLn(#13#10);
      Write('Ermitteln der Primzahlen fertig');
      If STime <> ETime Then
      Begin
        WriteLn(': ' + IntToStr(Round((MaxPrim - 1) / ((ETime - STime)
          * 86400))) + ' geprüfte Zahlen pro Sekunde'#13#10);
        DecodeTime(STime, HH, MM, SS, MS);
        DecodeTime(ETime, DD, MM, SS, HH);
        DD := Trunc(ETime - STime);
        WriteLn('Startzeit: ' + DateTimeToStr(STime) + '.' + MkLang(MS,
          3));
        WriteLn('Endzeit:   ' + DateTimeToStr(ETime) + '.' + MkLang(HH,
          3));
        DecodeTime(ETime - STime, HH, MM, SS, MS);
        WriteLn('----------------------------------');
        WriteLn('Benötigt:  ' + Format('%10d %s.%s.%s.%s', [DD,
          MkLang(HH, 2), MkLang(MM, 2), MkLang(SS, 2), MkLang(MS,
            3)]));
      End
      Else
        WriteLn;
    Except
      On E: Exception Do
      Begin
        WriteLn('Fehler ' + E.ClassName + ': ' + E.Message);
      End;
    End;
  Finally
    WriteLn(#13#10,
      'Bitte warten!!!'#13#10'Die gesammelten Daten der Primzahlen werden gespeichert!!!');
    Try
      FS := TFileStream.Create(ChangeFileExt(Application.ExeName,
        '.prm'),
        fmCreate Or fmShareDenyWrite);
      Try
        FS.Size := 0;
        FS.Write(Primzahlen[0], (PrimCount + 1) * SizeOf(Int64));
        WriteLn('Es wurden ' + IntToStr(FS.Size Div SizeOf(Int64)) +
          ' Primzahlen gespeichert ...');
      Finally
                FS.Free;
            End;
        Except
            On E: Exception Do
                WriteLn('Fehler ' + E.ClassName + ': ' + E.Message);
        End;
        FreeMem(Primzahlen);
        Read(Tmp);
    End;
End.


Ich weiß nicht, ob's unter D3 läuft, müsste aber theoretisch, weil ich's ohne Dyn. Arrays hab. Die waren mir zu langsam :D Wer aber genau hinguckt, wird feststellen, dass hier noch einiges zu optimieren geht ...


GTA-Place - Sa 27.11.04 17:32

Wenn ich das richtig sehe, verwendest du eine Funktion um große Zahlen zu berechnen.


Kroni - Sa 27.11.04 19:30

wenn ich aus INT64 mal nen Integer mache, müsste das funktionieren....ja du arbeitest ja schön mit Zeigern usw...soweit cihd as gesehen hab....Array von 0 bis 0 Deklarieren, dann n bissl Speicherplatz reservieren und dann mitm Zeiger das in das Array speichern oder so ähnlich ;-)
So wurde mir das auch mal erklärt....
Ichgucks mir mal an....
Gruß, Kroni


GTA-Place - Sa 27.11.04 20:19

Wir müssten das Überprüfen mal nach dem Sieb des Eratosthenes probieren.
Wir schreiben alle Zahlen in ein Array und löschen alle Zahlen, die durch 2, 3, 5, 7, 11, 13, 17, 19 und 23 teilbar sind. Alle zahlen die übrigbleiben, sind Primzahlen.


AXMD - Sa 27.11.04 20:22

GTA-Place hat folgendes geschrieben:
Wir müssten das Überprüfen mal nach dem Sieb des Eratosthenes probieren.
Wir schreiben alle Zahlen in ein Array und löschen alle Zahlen, die durch 2, 3, 5, 7, 11, 13, 17, 19 und 23 teilbar sind. Alle zahlen die übrigbleiben, sind Primzahlen.


Wenn du obige Zahlenreihe (.., 17, 19, 23, ...) nicht fortführst wirst du bald Zahlen entdecken, die das Sieb "übersieht" ;)

AXMD


BenBE - Sa 27.11.04 20:23

Du kannst anstatt Int64 auch den Comp-Typ nehmen. Ich hab unter D5 nur nen Int64 genommen, da der schneller ist.

Integer hab ich weggelassen, da mein Programm zu schnell ist. Da würden sich Ints nicht lohnen bzw. sogar Fehler liefern, weil der Zahlenbereich zu klein ist. ;-)


Tilo - Sa 27.11.04 20:30

AXMD hat folgendes geschrieben:
GTA-Place hat folgendes geschrieben:
Wir müssten das Überprüfen mal nach dem Sieb des Eratosthenes probieren.
Wir schreiben alle Zahlen in ein Array und löschen alle Zahlen, die durch 2, 3, 5, 7, 11, 13, 17, 19 und 23 teilbar sind. Alle zahlen die übrigbleiben, sind Primzahlen.


Wenn du obige Zahlenreihe (.., 17, 19, 23, ...) nicht fortführst wirst du bald Zahlen entdecken, die das Sieb "übersieht" ;)

AXMD


Lösung: Alle gefundenen Primzahlen in ein Array ablegen(sollte möglichst nicht dynamisch sein).

Probier mal das hier:

http://www.nrg.to/fishhead2/primzahlgen_6.exe

Mit dem Programm habe ich es auf meiner Kiste(2,4GHZ) in 30 bis 45 min (je nach Nutzung) bis zur Millionsten Primzahl geschafft.


GTA-Place - Sa 27.11.04 20:35

http://www.primzahlen.de/files/theorie/sieb.htm hat folgendes geschrieben:
...
Es folgt die 23. Die Zahl 23 wurde bis jetzt nicht weggestrichen und ist deshalb Primzahl. Wir markieren 23 als Primzahl und streichen nun alle durch 23 teilbaren Zahlen, weil diese nicht Primzahlen sein können. Sie hätten jeweils den Teiler 23. Sie liegen alle auf Geraden.
...
Die Zahlen 24, 25 und 26 sind schon gestrichen. Jetzt ist das Ziel erreicht. Alle noch nicht gestrichenen Zahlen sind Primzahlen
...


GTA-Place - Sa 27.11.04 20:42

Tilo, das Prog. ist voll lahm!
Um die Primzahl 1.299.709 zu finden (bis dahin sind es laut Prog. 100000 Primzahlen), braucht das Prog. 30 Sekunden. Mein Prog üperprüft 1.299.709 Zahlen in 3 Sekunden.


Gausi - Sa 27.11.04 21:19

Er findet aber die 1.000.000ste Primzahl! Und ich vermute mal, dass die etwas größer als 1.299.709 ist...


GTA-Place - Sa 27.11.04 21:39

Ich habe gesagt, dass wenn er nur die 100.000 Primzahl sucht 10x langsamer ist, als mein Prog.


GTA-Place - Sa 27.11.04 22:53

1 Million Zahlen in 1.5 Sekunden:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
function Prim(Zahl: LongInt): Boolean;
var
  Teiler, Wurzel: LongInt;
begin
  Result := True;
  if (not odd(Zahl)) OR (Zahl <= 5then
  begin
    if (Zahl <> 2AND (Zahl <> 3AND (Zahl <> 5then
      Result := False;
    Exit;
  end;
  Teiler := 0;
  Wurzel := Trunc(sqrt(Zahl));
  while (Teiler <= Wurzel) AND (Result) do
  begin
    if Zahl mod PrimS[Teiler] = 0 then
      Result := False;
    inc(Teiler);
  end;
end;


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:
38:
39:
40:
41:
42:
43:
procedure TMainForm.StartButClick(Sender: TObject);
var
  Start, Ende: Real;
  X, Z, Step, LPrim: Integer;
begin
  try
    Von := StrToInt(VonEdit.Text);
    Bis := StrToInt(BisEdit.Text);

    SetLength(PrimS, Bis+1);
    Step := 10000;
    LPrim := 0;
    Z := 0;

    if (Von >= 0AND (Bis >= 0AND (Von < Bis) then
    begin
      Start := GetTickCount;

      for X := Von to Bis do
      begin
        if Prim(X) then
        begin
          PrimS[Z] := X;
          LPrim := X;
          Inc(Z);
        end;
        if X = Step then
        begin
          Application.ProcessMessages;
          PrimLab.Caption := 'Aktuelle Primzahl: ' + IntToStr(LPrim);
          inc(Step, 10000);
        end;
      end;

      Ende := GetTickCount;
      DauerLab.Caption := 'Diese Überprüfung hat ' + FloatToStr((Ende - Start) / 1000) + ' Sekunden gedauert.';
    end
    else
      ShowMessage('Ungültige Eingabe(n)!');
  except
    ShowMessage('Es ist ein Fehler aufgetreten!');
  end;
end;


Tilo - So 28.11.04 12:09

GTA-Place hat folgendes geschrieben:
Ich habe gesagt, dass wenn er nur die 100.000 Primzahl sucht 10x langsamer ist, als mein Prog.


liegt es vielleicht daran, das ich meine Prim routine mit folgender zeile Starte:

Delphi-Quelltext
1:
application.OnIdle:=findprim;                    

Dadurch lauft das Programm langsamer.
Ich hatte das Programm schon mal schneller, aber dann wurde der gesamte rechner blockiert und das finde ich nicht gut.

die PrimRoutine ist folgende:

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:
procedure TForm1.findprim(Sender: TObject; var Done: Boolean);
var
a,b,c:integer;
begin
//repeat
if gefunden<soll then
begin
 b:=floor(sqrt(gefunden)); // Suche wird bis
 for a:=1 to b do          //
  if zutesten-sqr(floor(sqrt(zutesten)))<>0 then
   if (zutesten mod primz[a])= 0
    then begin isprim:=false;break end
    else isprim:=true;
 if isprim=true then
 begin
  gefunden:=gefunden+1;
  primz[gefunden]:=zutesten;
  edit2.text:=inttostr(gefunden);
  edit3.Text:=inttostr(zutesten);

 end;
 zutesten:=zutesten+1;
done:=false;
end;
end;


//Edit: Zeile 10 dient dem Test ob die Zahl eine Quadratzahl ist. Wenn nicht gehen durch das Runden der Wurzel die Kommenstellen weg und ein anschließendes Quadrieren für zu einer anderen Zahl.


Pro Durchlauf wird immer nur eine Primzahl getestet. Daurch wird das Programm zwar langsamer, aber Rechnerfreundlicher.

Moderiert von user profile iconChristian S.: Code- durch Delphi-Tags ersetzt.


Phantom1 - So 28.11.04 12:41

@GTA-Place: dein Code ist wirklich beeindruckend schnell. Jedoch ist noch ein kleiner Denkfehler drin, wodurch die Haupschleife in der Prim-Function sinnlos mehr durchlaufen muss:

hier der code den ich meine:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
  while (Teiler <= Wurzel) AND (Result) do  
  begin  
    if Zahl mod PrimS[Teiler] = 0 then  
      Result := False;  
    inc(Teiler);  
  end;


es sollte so aussehen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
  while (PrimS[Teiler] <= Wurzel) AND (Result) do  
  begin  
    if Zahl mod PrimS[Teiler] = 0 then  
      Result := False;  
    inc(Teiler);  
  end;


Desweiteren habe ich mal den Code noch etwas Optimiert, da die Zugriffe auf das Array nicht besonders schnell sind, Pointer sind da effizienter:


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:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
var
  PrimS: Array of Cardinal;

function Prim(Zahl: Cardinal): Boolean;
var Wurzel: Cardinal; Teiler: PCardinal;
begin
  Result := True;
  if not odd(Zahl) OR (Zahl <= 5then
  begin
    if (Zahl <> 2AND (Zahl <> 3AND (Zahl <> 5then
      Result := False;
    Exit;
  end;
  Teiler := @PrimS[0];
  Wurzel := Trunc(sqrt(Zahl));
  while Teiler^ <= Wurzel do
  begin
    if Zahl mod Teiler^ = 0 then begin
      Result := False;
      Break;
    end;
    Inc(Teiler);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
const
  Von = 0;
  Bis = 1000000;
var
  Start, Ende: Single;
  X, LPrim: Cardinal;
  Z: PCardinal;
begin
  SetLength(PrimS, Bis);
  Z := @PrimS[0];
  LPrim := 0;
  Start := GetTickCount;

  for X := Von to Bis do begin
    if Prim(X) then begin
      Z^ := X;
      Inc(Z);
      LPrim := X;
    end;
    if X mod 50000 = 0 then begin
      Application.ProcessMessages;
      Label1.Caption := 'Aktuelle Primzahl: ' + IntToStr(LPrim);
    end;
  end;

  Ende := GetTickCount;
  Label2.Caption := 'Diese Überprüfung hat ' + FloatToStr((Ende - Start) / 1000) + ' Sekunden gedauert.';
end;


Du wirst sehen das man für 1 Million Zahlen nicht mehr 1,5 sekunden braucht, sondern nur noch 0,35 Sekunden!!!!!

mfg
phantom1


GTA-Place - So 28.11.04 12:54

Danke.
Jetzt gibt es noch ein Prob. Ich weiß nicht, ob es durch die Pointer weg ist:
Je mehr Primzahlen gespeichert sind, desto mehr "Virtueller Speicher" wird benötigt und das meldet dann Windows.
Ich hab schon gedacht, nach 1000 Primzahlen oder so, die alle in ne Datei zu speichern und dann das Array, bzw. jetzt den Pointer zu leeren. Aber dann ist wahrscheinlich nicht mehr gewährleistet, dass alle Primzahlen gefunden werden, oder?


EDIT: Das hier kann noch optimiert werden:

Delphi-Quelltext
1:
SetLength(PrimS, Bis);                    

In folgendes:

Delphi-Quelltext
1:
SetLength(PrimS, Trunc(0.4*Bis+1));                    

Denn:

0,4 * 10 = 4 // Zwischen 0 und 10 sind 4 Primzahlen
0,4 * 100 = 40 // Zwischen 0 und 100 sind weniger als 40 Primzahlen.
0,4 * 100.000 = 40.000 // Unterschied, ob die Länge 100.000 oder 40.000 ist (schneller)


BenBE - So 28.11.04 16:12

Tilo hat folgendes geschrieben:
http://www.nrg.to/fishhead2/primzahlgen_6.exe

Mit dem Programm habe ich es auf meiner Kiste(2,4GHZ) in 30 bis 45 min (je nach Nutzung) bis zur Millionsten Primzahl geschafft.

LOL: Mein Programm macht die Millionste nach 0,985 Sekunden:

PrimProj.exe
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
Bis zu welcher Zahl wollen Sie die Primzahlen ermitteln???
1000000

[...]

Ermitteln der Primzahlen fertig: 1015227 geprüfte Zahlen pro Sekunde

Startzeit: 28.11.2004 14:46:38.218
Endzeit:   28.11.2004 14:46:39.203
----------------------------------
Benötigt:           0 00.00.00.985


Und mein Rechner ist ein AMD K6 mit 1,4GHz und SEHR VIELEN Hintergrundprogrammen :D

GTA-Place hat folgendes geschrieben:
Tilo, das Prog. ist voll lahm!
Um die Primzahl 1.299.709 zu finden (bis dahin sind es laut Prog. 100000 Primzahlen), braucht das Prog. 30 Sekunden. Mein Prog üperprüft 1.299.709 Zahlen in 3 Sekunden.


Naja, mein Programm hat nach 30 Sekunden dann schon einmal (fast) alle True-Colorfarben geprüft :D

Beweis:

PrimProj.exe
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
Bis zu welcher Zahl wollen Sie die Primzahlen ermitteln???
16777216

[...]

Ermitteln der Primzahlen fertig: 520224 geprüfte Zahlen pro Sekunde

Startzeit: 28.11.2004 14:58:53.734
Endzeit:   28.11.2004 14:59:25.984
----------------------------------
Benötigt:           0 00.00.32.250

Die gesammelten Daten der Primzahlen werden gespeichert!!!
Es wurden 1077871 Primzahlen gespeichert ...


GTA-Place hat folgendes geschrieben:
Danke.
Jetzt gibt es noch ein Prob. Ich weiß nicht, ob es durch die Pointer weg ist:
Je mehr Primzahlen gespeichert sind, desto mehr "Virtueller Speicher" wird benötigt und das meldet dann Windows.
Ich hab schon gedacht, nach 1000 Primzahlen oder so, die alle in ne Datei zu speichern und dann das Array, bzw. jetzt den Pointer zu leeren. Aber dann ist wahrscheinlich nicht mehr gewährleistet, dass alle Primzahlen gefunden werden, oder?


Ich hab in meinem Programm damit eigentlich keine Probleme. Das einzige was ihr beachten müsst, ist, dass ihr Zugriffe auf den Speichermanager auf ein Minimum reduzieren müsst.

Mein Programm hat, IIRC als ich mal bis 5 Mrd. ermittelt hab etwa 512MB RAM für die ganzen Int64's benötigt, OHNE dass Windows irgendwie gemeckert hätte. Das Problem an den Arrays und dem ständigen neuzuweisen ist, dass der Speicher zu stark fragmentiert und dadurch die Neuzuweisungen SEHR Langsam werden. Deshalb hab ich von Array auf PointerArray umgesattelt, da dadurch der Zugriff zum einen Schneller und zum anderen wesentlich einfacher geht.

Weiterhin wird bei mir nur dann neuer Speicher zugewiesen, wenn dies wirklich notwendig ist. Und wenn dieser Fall eintritt, wird sofort die Speichergröße Verdoppelt, was zwar mit Unter bei 512 MB etwa 4 Sekunden benötigen kann, aber immernoch weniger ist, als wenn ich ständig 64KB anfrage.

GTA-Place hat folgendes geschrieben:
EDIT: Das hier kann noch optimiert werden:

Delphi-Quelltext
1:
SetLength(PrimS, Bis);                    

In folgendes:

Delphi-Quelltext
1:
SetLength(PrimS, Trunc(0.4*Bis+1));                    

Denn:

0,4 * 10 = 4 // Zwischen 0 und 10 sind 4 Primzahlen
0,4 * 100 = 40 // Zwischen 0 und 100 sind weniger als 40 Primzahlen.
0,4 * 100.000 = 40.000 // Unterschied, ob die Länge 100.000 oder 40.000 ist (schneller)

Wieso diese Optimierung. Sind nur mehr Berechnungen und machen das Programm nur noch langsamer...


GTA-Place - So 28.11.04 16:35

Warum? Das kann ich dir sagen:

Das Prog. soll 1.000.000 Zahlen prüfen.
0,4 * 1.000.000 = 400.000
Das Prog hat 1.000.000 Zahlen geprüft und in eine Datei geschrieben.

So, jetzt öffnen wir mal die Datei und was sehen wir?:

78479 Primzahlen und
321.503 Mal die Zahl 0


EDIT: Und du hast was falsch verstanden:

Tilo hat folgendes geschrieben:
Mit dem Programm habe ich es auf meiner Kiste(2,4GHZ) in 30 bis 45 min (je nach Nutzung) bis zur Millionsten Primzahl geschafft.

BenBE hat folgendes geschrieben:
Bis zu welcher Zahl wollen Sie die Primzahlen ermitteln???
1000000


Es ist ein Unterschied, ob du die ein Millionste Primzahl finden willst oder eine Million Zahlen prüfen willst.

Und mein Prog. prüft 1.000.000 Zahlen in 0.3 Sekunden, schneller als deins.


Kroni - So 28.11.04 16:37

Na ja, was man nicht noch alles schneller machen kann....@GTA: gute arbeit, nette gedankengängen haben wir uns da zsuammengereimnt...dzu zwar mehr als ich....aba RESPEKT


GTA-Place - So 28.11.04 16:41

Danke.
Jetzt muss ich noch ne Formel finden, damit nicht über 320.000 Mal die 0 in der Datei steht. Vielleicht so:

0.4*Bis-(Bis/4) // Aber nur, wenn Bis größer als 10

PS: Hab im oberen Thread ein Edit.


GTA-Place - So 28.11.04 16:54


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:
function Prim(Zahl: Cardinal): Boolean;
var
  Teiler: PCardinal;
  Wurzel: Cardinal;
begin
  Result := True;    // Result = True
  if not odd(Zahl) OR (Zahl <= 5then    // Ist die Zahl nich ungerade oder kleiner als 5, dann
  begin
    if (Zahl <> 2AND (Zahl <> 3AND (Zahl <> 5then    // Ist die Zahl nicht 2 und nicht 3 und nicht 5, dann
      Result := False;    // Result = False
    Exit;                 // Exit
  end;
  Teiler := @PrimS[0];    // Teiler = @PrimS[0]
  Wurzel := Trunc(sqrt(Zahl));    // Wurzel aus der Zahl
  while Teiler^ <= Wurzel do    // Solange Teiler^ <= Wurzel ist, mache
  begin    
    if Zahl mod Teiler^ = 0 then    // Ist Zahl / Teiler^ = Rest 0, dann
    begin
      Result := False;    // Result = False
      Break;    // Break
    end;
    Inc(Teiler);    // Teiler erhöhen um 1
  end;
end;


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:
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:
procedure TMainForm.StartButClick(Sender: TObject);
var
  Start, Ende: Real;
  Z: PCardinal;
  X, LPrim: Cardinal;
  PrimF: TStringList;
begin
  try
    Von := StrToInt(VonEdit.Text);    // Start
    Bis := StrToInt(BisEdit.Text);    // Endwert

    if Bis > 10 then
      SetLength(PrimS, Trunc(0.4*Bis-(Bis/4)))    // Größe des Array: 0.4*Bis-(Bis/4)
    else
      SetLength(PrimS, 4);

    LPrim := 0;    // Letze Prim = 0
    Z := @PrimS[0];     // Gefundene Prims = 0

    if (Von >= 0AND (Bis >= 0AND (Von < Bis) then    // Von >= 0; Bis >= 0; Von < Bis;
    begin
      Start := GetTickCount;    // Start-Zeit

      for X := Von to Bis do    // Schleife: Startwert -> Endwert
      begin
        if Prim(X) then    // Funktion ausführen, wenn Prim dann
        begin
          Z^ := X;    // Prim in Array schreiben
          Inc(Z);    // Z erhöhen um 1
          LPrim := X;    // Letze Prim = X
        end;
        if X mod 20000 = 0 then    // Wenn X : 20.000 = Rest 0, dann
        begin
          Application.ProcessMessages;    // Anwendung aktualisieren
          PrimLab.Caption := 'Aktuelle Primzahl: ' + IntToStr(LPrim);    // Akt. Primzahl anzeigen
        end;
      end;

      Ende := GetTickCount;    // End-Zeit
      DauerLab.Caption := 'Diese Überprüfung hat ' + FloatToStr((Ende - Start) / 1000) + ' Sekunden gedauert.';    // Dauer anzeigen

      PrimLab.Caption := 'Speichern...';    // "Speichern..." anzeigen

      Z := @PrimS[0];    // Z auf 0 stellen
      PrimF := TStringList.Create;    // Stringlist erzeugen

      for X := 0 to Length(PrimS)-1 do    // Von 0 bis Größe des Array
      begin
        if Z^ = 0 then    // Wenn Z^ = 0, dann
          Break;    // Break
        PrimF.Add(IntToStr(Z^));    // Prim in Stringlist schreiben
        Inc(Z);    // Z erhöhen um 1
      end;

      PrimF.SaveToFile('Paket.prim');    // Stringlist speichern
      PrimF.Free;    // Stringlist freigeben

      PrimLab.Caption := 'Aktuelle Primzahl: ' + IntToStr(LPrim);    // Akt. Primzahl anzeigen
    end
    else
      ShowMessage('Ungültige Eingabe(n)!');    // Bei falschen Eingaben, Nachricht anzeigen
  except
    ShowMessage('Es ist ein Fehler aufgetreten!');    // Wenn Fehler auftritt, Nachricht anzeigen
  end;
end;


Überprüfung von 20.000.000 Zahlen in 16 Sekunden.
Speichern so schnell, das man "Speichern..." gar net sieht.


Phantom1 - So 28.11.04 18:03

Hier noch eine möglichkeit, bei der kein Byte im RAM verschwendet wird. Jedoch ist die Suche etwa doppelt so langsam.


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:
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:
var
  PrimS: TMemoryStream;

function Prim(Zahl: Cardinal): Boolean;
var Teiler, Wurzel: Cardinal;
begin
  Result := True;
  if not odd(Zahl) OR (Zahl <= 5then
  begin
    if (Zahl <> 2AND (Zahl <> 3AND (Zahl <> 5then
      Result := False;
    Exit;
  end;
  PrimS.Position:=0;
  PrimS.Read(Teiler, SizeOf(Teiler));
  Wurzel := Trunc(sqrt(Zahl));
  while Teiler <= Wurzel do
  begin
    if Zahl mod Teiler = 0 then begin
      Result := False;
      Break;
    end;
    PrimS.Read(Teiler, SizeOf(Teiler));
  end;
  prims.Position:=prims.Size;
end;

procedure TForm1.Button1Click(Sender: TObject);
const
  Von = 0;
  Bis = 15485863// das ist die 1 millionste Primzahl :-)
var
  Start, Ende: Single;
  X, LPrim: Cardinal;
  PrimF: TStringList;
begin
  PrimS:=TMemoryStream.Create;
  try
    LPrim := 0;
    Start := GetTickCount;
    for X := Von to Bis do begin
      if Prim(X) then begin
        PrimS.Write(X, SizeOf(X));
        LPrim := X;
      end;
      if X mod 50000 = 0 then begin
        Application.ProcessMessages;
        Label1.Caption := 'Aktuelle Primzahl: ' + IntToStr(LPrim);
      end;
    end;
    Ende := GetTickCount;
    Label1.Caption:='Aktuelle Primzahl: ' + IntToStr(LPrim);
    Label2.Caption:='Anzahl der gefundenen Primzahlen: '+ IntToStr(PrimS.Size div 4);
    Label3.Caption:='Diese Überprüfung hat ' + FloatToStr((Ende - Start) / 1000) + ' Sekunden gedauert.';

    PrimF := TStringList.Create;
    PrimS.Position:=0;
    while Prims.Position<>Prims.Size do begin
      PrimS.Read(X, SizeOf(X));
      PrimF.Add(IntToStr(X));
    end;
    PrimF.SaveToFile('Paket.prim');
    PrimF.Free;

  finally
    PrimS.Free;
  end;
end;


GTA-Place - So 28.11.04 18:11


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
      for X := 0 to Length(PrimS)-1 do    // Von 0 bis Größe des Array
      begin
        Application.ProcessMessages;
        if Z^ = 0 then    // Wenn Z^ = 0, dann
          Break;    // Break
        PrimLab.Caption := 'Speichern... (' + IntToStr(X+1) + ' / ' + IntToStr(Length(PrimS)) + ')';    // "Speichern..." anzeigen
        PrimF.Add(IntToStr(Z^));    // Prim in Stringlist schreiben
        Inc(Z);    // Z erhöhen um 1
      end;


Beispiel:

Speichern... (10 / 1000)


GTA-Place - So 28.11.04 19:34

Neue Statistik:

Überprüfung von 1.000.000.000 Zahlen in 52 Minuten.
Das sind 508.475.534 Primzahlen.


AXMD - So 28.11.04 20:02

@GTA: Sicher, dass du nicht vertippt hast? Das würde ja heißen, dass jede zweite Zahl eine Primzahl ist

AXMD


GTA-Place - So 28.11.04 20:11

Könnt nur sein, das das falsche Zahlen sind. Ich guck mal nach.


patrick - So 28.11.04 20:50

ich hab den output mal überfolgen: die zahlen wo ich gesehen hab stimmen.

ich hab gerade versucht den code von GTA-Place per assembler zu verschnellern.
bei der suche nach einem noch schnelleren code zum wurzel ziehen bin ich auf diesen code gestoßen:

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:
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:
function IsPrime(N: Cardinal): Boolean; register
// test if N is prime, do some small Strong Pseudo Prime test in certain bounds 
// not PIC safe !! 
// very fast, about 1200 clockcycles 
// copyright by Hagen Reddmann, don't remove this line 
asm 
       TEST  EAX,1            // Odd(N) ?? 
       JNZ   @@1 
       CMP   EAX,2            // N == 2 ?? 
       SETE  AL 
       RET 
@@1:   CMP   EAX,73           // N  < 73 ?? 
       JB    @@C 
       JE    @@E              // N == 73 ?? 
       PUSH  ESI 
       PUSH  EDI 
       PUSH  EBX 
       PUSH  EBP 
       PUSH  EAX              // save N as Param for @@5 
       LEA   EBP,[EAX - 1]    // M == N -1, Exponent 
       MOV   ECX,32           // calc remaining Bits of M and shift M' 
       MOV   ESI,EBP 
@@2:   DEC   ECX 
       ADD   ESI,ESI 
       JNC   @@2 
       PUSH  ECX              // save Bits as Param for @@5 
       PUSH  ESI              // save M' as Param for @@5 
       CMP   EAX,08A8D7Fh     // N >= 9080191 ?? 
       JAE   @@3 
// now if (N < 9080191) and SPP(31, N) and SPP(73, N) then N is prime 
       MOV   EAX,31 
       CALL  @@5              // 31^((N-1)(2^s)) mod N 
       JC    @@4 
       MOV   EAX,73           // 73^((N-1)(2^s)) mod N 
       PUSH  OFFSET @@4 
       JMP   @@5 
// now if (N < 4759123141) and SPP(2, N) and SPP(7, N) and SPP(61, N) then N is prime 
@@3:   MOV   EAX,2 
       CALL  @@5 
       JC    @@4 
       MOV   EAX,7 
       CALL  @@5 
       JC    @@4 
       MOV   EAX,61 
       CALL  @@5 
@@4:   SETNC AL 
       ADD   ESP,4 * 3 
       POP   EBP 
       POP   EBX 
       POP   EDI 
       POP   ESI 
       RET 
// do a Strong Pseudo Prime Test 
@@5:   MOV   EBX,[ESP + 12]   // N on stack 
       MOV   ECX,[ESP +  8]   // remaining Bits 
       MOV   ESI,[ESP +  4]   // M' 
       MOV   EDI,EAX          // T = b, temp. Base 
@@6:   DEC   ECX 
       MUL   EAX 
       DIV   EBX 
       MOV   EAX,EDX 
       ADD   ESI,ESI 
       JNC   @@7 
       MUL   EDI 
       DIV   EBX 
       AND   ESI,ESI 
       MOV   EAX,EDX 
@@7:   JNZ   @@6 
       CMP   EAX,1            // b^((N -1)(2^s)) mod N ==  1 mod N ?? 
       JE    @@A 
@@8:   CMP   EAX,EBP          // b^((N -1)(2^s)) mod N == -1 mod N ?? , EBP = N -1 
       JE    @@A 
       DEC   ECX              // second part to 2^s 
       JNG   @@9 
       MUL   EAX 
       DIV   EBX 
       CMP   EDX,1 
       MOV   EAX,EDX 
       JNE   @@8 
@@9:   STC 
@@A:   RET 
@@B:   DB    3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71 
@@C:   MOV   EDX,OFFSET @@B 
       MOV   ECX,18 
@@D:   CMP   AL,[EDX + ECX] 
       JE    @@E 
       DEC   ECX 
       JNL   @@D 
@@E:   SETE  AL 
end;


Quelle [http://www.delphipraxis.net/post73566.html]
resultat:
10 millionen durchläufe haben mit dem letzten GTA-Code bei mir 6,4 sekunden gebraucht.
dieser code braucht dafür gerademal 0,3 sekunden

dennoch eine weitere optimierung des delphi-codes wäre bestimme interessant.

purer und intelligent geschriebener asm-code ist halt nicht zu schlagen wenn es um mathematik geht.


GTA-Place - So 28.11.04 21:15

Bei mir was der ASM-Code langsamer.

Es gibt ein Problem beim Überprüfen von Zahlen zw. 0 und 100:
Zitat:
Access violation at 0x004059c1: write of address 0x00030e68


Hab ein paar Änderungen vorgenommen:

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:
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:
procedure TMainForm.StartButClick(Sender: TObject);
var
  Start, Ende: Real;
  Z: PCardinal;
  X, Y, LPrim: Cardinal;  // Hier
  PrimF: TStringList;
  CountP: String;
begin
  try
    Von := StrToInt(VonEdit.Text);
    Bis := StrToInt(BisEdit.Text);

    if Bis > 10 then
      SetLength(PrimS, Trunc(0.4*Bis-(Bis/4)))
    else
      SetLength(PrimS, 4);

    LPrim := 0;
    Z := @PrimS[0];
    Y := 0;  // Hier

    if (Von >= 0AND (Bis >= 0AND (Von < Bis) then
    begin
      Start := GetTickCount;

      for X := Von to Bis do
      begin
        if Prim(X) then
        begin
          Z^ := X;
          Inc(Z);
          Inc(Y);  // Hier
          LPrim := X;
        end;
        if X mod 20000 = 0 then
        begin
          Application.ProcessMessages;
          PrimLab.Caption := 'Aktuelle Primzahl: ' + IntToStr(LPrim);    // Akt. Primzahl anzeigen
        end;
      end;

      Ende := GetTickCount;
      DauerLab.Caption := 'Diese Überprüfung hat ' + FloatToStr((Ende - Start) / 1000) + ' Sekunden gedauert.';

      CountP := IntToStr(Y);
      PrimLab.Caption := 'Speichern... (0 / ' + CountP + ')';

      PrimF := TStringList.Create;

      for X := 0 to Length(PrimS)-1 do
      begin
        if PrimS[X] = 0 then
          Break;
        if X mod 1000 = 0 then   // Hier
        begin
          Application.ProcessMessages;
          PrimLab.Caption := 'Speichern... (' + IntToStr(X+1) + ' / ' + CountP + ')';
        end;
        PrimF.Add(IntToStr(PrimS[X]));
      end;

      PrimF.SaveToFile('Paket.prim');
      PrimF.Free;

      PrimLab.Caption := 'Aktuelle Primzahl: ' + IntToStr(LPrim);
    end
    else
      ShowMessage('Ungültige Eingabe(n)!');
  except
    ShowMessage('Es ist ein Fehler aufgetreten!');
  end;
end;


Phantom1 - So 28.11.04 21:47

Ich hab den ASM-Code auch mal getestet bei 10 millionen testdurchläufen: 4,7 sekunden

GTA-Place dagegen: 6,8 sekunden

Ich denke das bekommen wir noch hin ;O)

Ein nachteil gibt es jedoch mit dem Code von GTA-Place, es muss immer von 0 bzw 2 begonnen werden bis zur gewünschten Testzahl.


BenBE - So 28.11.04 22:13

OK, an die 16 Sekunden für 20 Mio. komm ich mit meinem Code noch net so richtig ran (37.5s, dafür aber auf 1,4GHz)

Hab vorhin mal die Prüfung auf 1Mrd. gemacht: Ergebnis 01:57:29.733 mit 141849 Zahlen\Sekunde und 50847534 gefundenen Primzahlen.

Weiterhin ist bei meinem Source noch zu beachten, dass ich dafür Int64-Operationen nutze. Die Wurzel lass ich mir per FPU ziehen:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
    //MaxTest := Trunc(Sqrt(CurrNum*1.0));
    Asm
        FILD    QWORD PTR [CurrNum]
        FSQRT
        FISTP   QWORD PTR [MaxTest]
    End;


Wobei ich vor der Schleife die FPU generell auf Abrunden umschalte:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
    Asm
        FLDCW   WORD PTR [@@TruncCW]
        JMP     @@Skip
        @@TruncCW:
        DW      $1772   //Immer abwärts runden
        @@Skip:
    End;


@GTA: Du hattest vorhin die markierte 5 doppelt...

Könntet Ihr aber mal bei Performance-Informationen die CPU\Takt und RAM angeben? Ich glaub, das hilft bei der Einstufung der Messwerte schon um einiges Weiter.

Aber ehrlichgesagt: An den ASM-Source komm ich bei weitem net ran ... :mrgreen:


GTA-Place - Mo 29.11.04 00:43

EDIT: Problem gelöst, "Inc(Z);" hat gefehlt.


Phantom1 - Mo 29.11.04 11:24

Ich glaube wir sind noch sehr weit vom Optimum entfernt, schaut doch mal in dieses Forum/Thread: http://www.forum-3dcenter.org/vbulletin/showthread.php?t=95859&page=1&highlight=prim

die kommen dort ohne probleme auf folgende Zeiten:

1Mio : 0,02s
5Mio : 0,09s
10Mio : 0,2s
50Mio : 1,1s
100Mio : 2,5s
500Mio : 17,2s
1Mrd : 43s
2^32-cache : 7,5min

Auf Seite 4 des Thread's, erfährt man sogar das es noch viel schneller geht ;O) Ich sag nur primegen.

mfg


Phantom1 - Mo 29.11.04 14:03

Ich habe eben auch nochmal einen neuen Code nach dem SIEB DES ERATOSTHENES geschrieben:

für 10 Mio testzahlen braucht mein Code etwa 0,65 Sekunden (ohne speichern)


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:
procedure SavePrimes(MaxPrime: Cardinal; const FileName: String = '');    
var
  isPrime: Array of Boolean;
  i, j: Cardinal;
  SL: TStringList;
begin
  SetLength(isPrime, MaxPrime+1);
  FillChar(isPrime[2], Length(isPrime)-21);
  for i:=2 to Trunc(Sqrt(MaxPrime)) do
    if isPrime[i] then begin
      j:=i*2;
      while j<=MaxPrime do begin
        isPrime[j]:=False;
        Inc(j, i);
      end;
    end;

  if FileName<>'' then begin
    SL:=TStringList.Create;
    try
      for i:=2 To MaxPrime do
        if isPrime[i] then
          SL.Add(IntToStr(i));
      SL.SaveToFile(FileName);
    finally
      FreeAndNil(SL);
    end;
  end;
end;


GTA-Place - Mo 29.11.04 17:23

http://www.delphipraxis.net/topic40427,0,asc,0.html
sakura hat folgendes geschrieben:
Mein Lieblingsalgorithmus findest Du im Projekt im Anhang, das Sieb des Erasthothes od so Alle Primzahlen zw. 1 und 10.000.000 findet der Algorithmus bei mir in weniger als 1,5 Sekunden


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
    FillChar(PrimeGrid, SizeOf(PrimeGrid), #1);
    PrimeGrid[1] := False;
    for CurrPrimeCandidate := 2 to MAX_PRIME do
    begin
      if PrimeGrid[CurrPrimeCandidate] then
      begin
        NotAPrime := CurrPrimeCandidate * 2;
        while NotAPrime <= MAX_PRIME do
        begin
          PrimeGrid[NotAPrime] := False;
          Inc(NotAPrime, CurrPrimeCandidate);
        end;
      end;
    end;


Tilo - Di 30.11.04 17:53

Okay, habe mal meinen Code umgestellt.
Ein grund wieso meine Routine so langsam war ist, das ich es über Application.Onidle hab laufen lassen.
Wenn ich den Code so um schreibe, das bei jeder 10000 Primzahl eine Programmabgabe wie bei GTA-Place, dann schafft mein Programm 10.000.000 Primzahlen unter 0,5 Sekunden :lol: :P :x 8) :o :D
der Code:

Edit2: Schnellschuss meinerseits, Code liefert keine Primzahlen.


F34r0fTh3D4rk - Di 30.11.04 18:20

ich werf hier mal wieder was ausm edh rein (falls nich schon vorhanden):


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
Function IsPrim(zahl : Integer): boolean;
var
i: integer;
begin
  result := true;
  If zahl = 1 then
  begin
    result := false;
    exit;
  end;
  For i := 2 to (zahl div 2do
  begin
    If ((zahl mod i) = 0then
    begin
      result := false;
      exit;
    end;
  end;
end;


Wie sieht bei euch der aufruf aus ?


GTA-Place - Di 30.11.04 18:22


Delphi-Quelltext
1:
For i := 2 to (zahl div 2do                    

Das muss langsamer sein, denn man muss gar nicht zu Hälfte der Zahl, sondern nur bis zur Wurzel der Zahl prüfen.


F34r0fTh3D4rk - Di 30.11.04 20:19

zufällig war ich auch gerade dabei sowas zu machen, war mir dann aber doch zu langsam
und beim integer maximum schmiert er ja bekanntlich ab...

Ich habe gehört, dass es eine Belohnung geben soll, wenn man eine 100 Stellige
Primzahl findet, also ranhalten jungs !!! 8)


Tilo - Di 30.11.04 22:43

Schnellschussmeinerseits.
Der Code liefert keine Primzahlen, sondern fast alle ungeraden Zahlen
:oops: :oops: :oops: :oops: :oops: :oops: :oops: :oops: :oops:


patrick - Di 30.11.04 22:46

dann müsten wir ja nur den 100-stelligen bereich durchgehen. dann verteilen wir auch noch die bereiche und dann ist alles halb so schlimm. wieviel 100jahre würden wir brauchen? :oops:


BenBE - Di 30.11.04 22:57

6 Monate, unter der Annahme, dass wir die Suchbereiche effektiv nutzen.

In Hinblick auf die Ermittlung brauchen wir ja um eine 100-Stellige Zahl auf Primalität zu prüfen nicht wirklich alle Zahlen zu testen.
Es würde reichen, wenn wir Sagen wir SPPs zur Basis der ersten 1000 Primzahlen durchführen. Dann dürften wir auf jeden Fall auf der sicheren Seite sein ;-)


Phantom1 - Mi 01.12.04 13:58

Ich habe mein code jetzt nochmal optimiert.

Für 10 mio testzahlen dauerts jetzt nur noch 0,3 sekunden, nebenbei habe ich noch den Speicherverbrauch auf ein 1/8 reduziert.

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:
procedure SavePrimes(MinPrime, MaxPrime: Cardinal; const FileName: String = '');
var
  isPrime: Array of Cardinal; 
  i, j: Cardinal;
  SL: TStringList;
begin
  SetLength(isPrime, MaxPrime div 32);
  FillChar(isPrime[0], Length(isPrime)*SizeOf(Cardinal), MAXBYTE); // alle bits auf 1 setzten
  isPrime[0]:=$FFFFFFFC// bit 0 und 1 ist keine primzahl

  for i:=2 to Trunc(Sqrt(MaxPrime)) do
    if isPrime[i div 32shr (i mod 32and 1=1 then begin
      j:=i*2;
      while j<=MaxPrime do begin
        isPrime[j div 32]:=isPrime[j div 32and not (1 shl (j mod 32));
        Inc(j, i);
      end;
    end;

  if FileName<>'' then begin
    SL:=TStringList.Create;
    try
      for i:=MinPrime To MaxPrime do
        if isPrime[i div 32shr (i mod 32and 1=1 then
          SL.Add(IntToStr(i));
      SL.SaveToFile(FileName);
    finally
      FreeAndNil(SL);
    end;
  end;
end;


mfg


Tilo - Mi 06.04.05 18:21

Ich weiss, das dieses Thema alt ist.
Ich habe ich aber mal mit Pointern beschäftigt.
Ich habe mal die "prim" function von GTA-Place um geschrieben:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
function tform1.Prim(Zahl: LongInt): Boolean;
var
  Teiler, Wurzel: LongInt;
  p1prim:PTprim;
begin
  Result := True;
  p1prim:=startprim;
  if (not odd(Zahl)) OR (Zahl <= 5then
  begin
    if (Zahl <> 2AND (Zahl <> 3AND (Zahl <> 5then
      Result := False;
    Exit;
  end;
  Wurzel := Trunc(sqrt(Zahl));
  while (p1prim^.prim <= Wurzel) AND (Result) do
  begin
    if Zahl mod p1prim^.prim = 0 then
      Result := False;
    p1prim:=p1prim^.nextprim;
  end;
end;


Bitte hiermit aufrufen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
 if prim(zutesten) then
 begin
  new(P1prim);
  p1prim^.nextprim:=nil;
  lastprim^.nextprim:=p1prim;
  p1prim^.prim:=zutesten;
  lastprim:=p1prim;
  inc(zutesten)
 end;


zum Intialisieren bitte das verwenden:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
procedure TForm1.FormCreate(Sender: TObject);
var
a,b,c:integer;
p1prim:PTprim;
begin
 new(startprim);
 new(P1prim);
 startprim^.prim:=2;
 startprim^.nextprim:=P1prim;
 p1prim^.prim:=3;
 p1prim^.nextprim:=nil;
 lastprim:=p1prim;
end;


Das Ende ist dann wie folgt:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
p1prim,p2prim:PTprim;
begin
p1prim:=startprim;
while p1prim^.nextprim<>nil do
begin
p2prim:=p1prim;
p1prim:=p1prim^.nextprim;
dispose(p2prim);
end;
dispose(p1prim);
end;


Wichtig ist noch

Delphi-Quelltext
1:
2:
3:
4:
5:
 
  PTPrim=^TPrim;
  TPrim =record
   prim:integer;
   nextprim:PTPrim;


Lastprim und Startprim sind globale PTPrim.


HOffe keinen Nonsens zu posten

Moderiert von user profile iconChristian S.: Code- durch Delphi-Tags ersetzt.


GTA-Place - Mi 06.04.05 18:27

Isses schneller?


Tilo - Do 07.04.05 14:56

Bei meinen Programm bringt es bei der berechnung der ersten 1000 Primzahlen einen Zeitvorteil von 10 bis 20 ms als mit der Verwendung eines statischen Feldes.

Vorteil der Pointer-Methode:
- Das "Feld" wird nach Bedarf aufgebaut ->Vorteil, da Speicherbelastung geringer.

Wenns nicht schneller wäre hätte ich es nicht gepostet.


Omikron2 - Sa 09.04.05 00:59

Es gibt seit 2002 glaube ich einen algorithmus der zahlen auf prim überprüft und nur einen linearen rechenaufwand hat (es müssen nicht mehr alle möglichkeiten ausprobiert werden) googlen hilft vielleicht!
mfg


Tilo - Sa 09.04.05 01:34

@Omikron2
Wenn Du dir die geposteten Algorithmen ansiehst, wirds Du merken, dass a) nut bis zur Wurzel der zutestenden Zahl geprüft wird, Und als mögliche Teiler werden zuvor gefundene Primzahlen verwendet(Sieb des Namenvergessen), was wieder eine Geschwindigkeitserhöhung gibt.

Durch Googeln habe ich folgende Funktion gefunden:
p ist prim -> (2^p)-1 auch prim

2 -> 3
3 -> 7 >> und 5?
7 -> 127 >> hier fehlen noch mehr.

Du siehst, die Funktion f(x)=(2^x)-1 erzeugt zwar Primzahlen aber leider fehlen etliche Primzahlen. Diese Funktion dient nur zum Beweiss das es unendlich viele gibt. Und ich bin mir sicher, das es diese Funktion auch schon vor 2002 existent war.


Ich Bins - Sa 09.04.05 11:44

@Tilo Das kann ich mir nicht so ganz vorstellen. Wenn gelten würde:

p ist prim -> (2^p)-1 auch prim

Dann wäre es doch extrem einfach, eine größere Primzahl als die bisher größte bekannte Primzahl zu finden oder nicht? Wenn jetzt z.B. die Zahl 4527835 die größte bekannte Primzahl wäre (hab jetzt net geguckt obs überhaupt eine ist :D ), dann wüsste man doch auch, dass 2^4527835-1 auch eine Primzahl wäre, und somit hätte man eine viel größere gefunden.

Oder hab ich da jetzt was falsch aufgefasst?


Ich Bins


uall@ogc - Sa 09.04.05 12:03

(2^p)-1

gilt nicht als beweis für primzahlen...
die wahrscheinlichkeit das die zahl eine primzahl ist, ist nur wesentlich größer als wenn man irgend eine andere zahl nimmt


Tilo - Sa 09.04.05 15:13

Sorry, aber diese Funktion habe ich auf einer UNI-Forum Seite [http://www.chemieonline.de/forum/showthread.php?t=3635] gefunden.


Marekventur - Sa 16.04.05 20:14

Hallo!

Wenn ihr richtig große Primzahlen finden wollt, lest euch das mal durch:

Zitat:
Der kleine Fermatische Satz:
Ist p eine Primzahl und b eine zusammengesetzte Zahl, so ist b^p-b ein Vielfaches von p.
Wenn p=7 und b=2: 2^7-2=126.Dann kann man 126/7 teilen.
1979 wurde bewiesen, daß 2^44497-1 eine Primzahl ist. So können wir darauf schließen, daß 3^2^44497-1) -3/ 2^44497-1 ohne Rest teilbar ist.
B^p-b/p ( da kommt Rest 0 heraus) 2^11-2=2046/11
Der Rest ist aber ungleich null und damit ist die Ausgangszahl zusammengesetzt!!!

Pseudoprimzahlen:
2^341-2 ist ein Vielfaches von 341, obwohl 341 eine aus dem Produkt von 11 und 31 zusammengesetzte Zahl ist. Deswegen sind bei der Divisionsmethode Einsparungen zu machen, da es auf den Rest ankommt. Hier kommt die Arithmetik der Kongruenz ins Spiel. 3^1037-3 ist durch 1037 kongruent zu 845 modulo 1037.(Daß heißt, daß die erste Zahl bei der Division durch 1037 die zweite Zahl als Rest liefert. Also kann der Rest bei /1037 nicht null sein, da 1037 zusammengesetzt ist. Jedoch tritt das Problem der Pseudoprimzahlen eher selten auf. Es existieren unterhalb 10^10 455052512 Primzahlen. Zu b=2 gibt es nur 14884 Pseudoprimzahlen, wobei 561 die kleinste ist. Weil der Fermatische Test so gut funktioniert wurde er versucht zu verbessern, daß die Pseudoprimzahlen wegfallen. Man will mehr über zusammengesetzte Zahlen erfahren, damit man herausbekommt auf welche Weise sie sich tarnen. Diese Tests liefern Informationen über getestete Zahlen und nicht nur ob sie „bestanden“ haben oder „durchgefallen“ sind. Diese Ergebnisse ähneln einem Mosaik, wobei die Informationen über die Zahl auf die einzelnen Steinchen verteilt ist. So können wir an Hand dieses Mosaiks sehen, ob es Primzahlen sind. Anfangs der 80 er Jahre wurden effiziente Tests zum Algorythmus gemacht. Len Adleman, Robert Rumely und Carl Pomerance haben dazu beigetragen. Die Abwandlung davon schuf Hendrik Lenstra und beschleunigte die Arbeit. Die Informatik fragt sich, ob man diese Arbeit noch beschleunigen kann. Dafür ist die Komplexitätstheorie verantwortlich. Sie klassifiziert die Beispiele in einfach und schwierig. Wieviel Zeit braucht der Computer?
Nimmt die Zahl der Rechenschritte mit der Funktion k*n^q zu, wobei q und k feste bestimmte Zahlen sind. Kann man dies in polynomialer Zeit schaffen? (einfach)
K*q^n ist nicht in polynomialer Zeit zu schaffen. Deswegen ist es schwierig.

(Ich hoffe, ich hab ein richtiges Zitat....)

Man kann über sehr große Zaheln sehr "schnell" sagen, ob es nicht prim ist.
Leider kann man dann nur sagen, das eine Zahl sehr wahrscheinlich Prim ist.....
Dies wird für die heutigen, gängigen Verschlüßelungen ausgenutzt, die sehr große Primzahlen erfordern. (asymetrische Verschlüßelung, vor allem RSA)

Die Methode: Man nimmt eine beliebige Zahl, die zu prüfen ist (p).
Man geht mit einer Reihe Zahlen (a) mit einer Formel durch. Ist die Gleichung auch nur einmal nicht erfüllt, ist die Zahl 100% nicht prim. Man kann so die Wahrscheinlichkeit, eine Primzahl gefunden zu haben, stark erhöhen, in dem man mit vielen Zahlen testet. Die Formel hab ich im moment nicht griffbereit, aber es war irgendwas mit potenzen in der Modoloebene (erfordert schon einen eigenen Algorythmus *g*) Müsst mal suchen...
Damit hab ich mal 100-Stellig Pseudoprimzahlen in wenigen Minuten finden können (Ihr braucht aber auch sowas wie THugeInt, ich glaub, das istz aber bei D7 schon mit dabei....)

Also, viel Spaß beim Suchen...

Moderiert von user profile iconAXMD: BB-Codec aktiviert


zemy - So 17.04.05 17:31

user profile iconOmikron2 hat folgendes geschrieben:
Es gibt seit 2002 glaube ich einen algorithmus der zahlen auf prim überprüft und nur einen linearen rechenaufwand hat (es müssen nicht mehr alle möglichkeiten ausprobiert werden) googlen hilft vielleicht!
mfg


Stimmt schon, da gibts ein Algo.... dummerweise ist der für Quantencomputer :D Du kannst zwar auf einen normalen PC einen Quantencomputer simullieren (und andersrum), dummerweise steigt die Rechenzeit dann wieder quadratrisch an^^

user profile iconF34r0fTh3D4rk hat folgendes geschrieben:
...
Ich habe gehört, dass es eine Belohnung geben soll, wenn man eine 100 Stellige
Primzahl findet, also ranhalten jungs !!! 8)


100 nicht ganz... 10 Millionen stellen :D Die größte bekannte Primzahl bis dato (225,964,951-1) hat "nur" 7,8Mio. Stellen. hier [http://www.mersenne.org/prime8.txt] ist ein Link. Lest sie, lernt sie, lebt sie :D. Ein paar allgemeine Infos findet ihr hier: http://www.mersenne.org/prime.htm

MfG Zemy


Allesquarks - Mi 20.04.05 16:53

Sorry wenn ich jetzt was doppelt poste aber hab die mittleren Seiten nicht gelesen.

Das Program Prime 95 vom Primzahlprojekt Gimps nutzt zur Primzahlberechnung Lucas-Lehmer iterations. Kenne diese zwar nicht, scheint aber der professionellste Ansatz für dieses Problem zu sein.


GTA-Place - Mi 20.04.05 17:07

Das Prog läuft grad bei mir und rechnet ^^

http://www.devalco.de/mersenne.htm


delfiphan - Mi 20.04.05 17:23

Tilo: (2^x)-1 ist natürlich nicht garantiert Prim und es dient auch nicht als Beweis, dass es unendlich viele Primzahlen gibt.
Man denke an 255 oder 65535. Die sind wie alle anderen Zahlen, die mit 5 enden, nicht Prim (ausser die 5 selbst).


Tilo - Fr 22.04.05 08:10

Aber ich habe wieder ein seite [http://prime.haugk.co.uk/] gefunden die (2^p)-1 als Prim ansieht, wenn p eine Primzahgl ist!


BenBE - Fr 22.04.05 08:42

user profile iconTilo hat folgendes geschrieben:
Aber ich habe wieder ein seite [http://prime.haugk.co.uk/] gefunden die (2^p)-1 als Prim ansieht, wenn p eine Primzahgl ist!

Die Klammersetzung ist falsch: 2^(p-1) mod p == 1
Gilt aber nur als Pseudo-Primalitätstest ...
Gibt noch einen Rabbin-Miller-Test, der den Exponenten p-1 = s * 2 ^ k darstellt und auch 2^s mod p == 1 überprüft.


Gausi - Fr 22.04.05 08:57

Kleiner Einschub, da hier aufkam, warum es unendlich viele Primzahlen gibt: Das ist ganz einfach zu sehen - durch einen Widerspruchsbeweis:

Angenommen, es gäbe nur endlich viele Primzahlen - nennen wir diese endlich vielen Primzahlen p_1,p_2,p_3,...,p_n. Dann bilden wir daraus eine neue Zahl, nämlich das Produkt aller Primzahlen, und addieren 1 hinzu: X=(p_1 * p_2 *...*p_n) +1. Jetzt kann man sich leicht überlegen, das X durch keine der Primzahlen teilbar sein kann, also wäre X auch eine Primzahl. Das steht aber im Widerspruch dazu, dass wir bereits alle Primzahlen gefunden haben. Also gibt es unendlich viele.

Hinweis: da es nicht nur die Primzahlen p_1 bis p_n gibt, ist X auch nicht notwendigerweise wirklich eine Primzahl. Auf diese Weise kann man also auch nicht einfach große Primzahlen finden.


jelleton - So 24.04.05 22:51

Also ich habe diese Diskussion verfolgt, weil ich genau das gleiche Prob habe, konnte aber teilweise nur begrenzt folgen. Ich frag deshalb einfach nochmal direkt nach: Mittels welchem Algo finde ich Primzahlen, in meinem Fall mit ungefähr 30 Stellen?


BenBE - Mo 25.04.05 09:36

Rabbin-Miller-Test, indem Du für 10 bis 20 verschiedene Primzahl-Basen die Starke Pseudo-Primalität prüfst ...

d.h.

Generierung einer 30*ld(10)=99,6 bit-->100 bit breiten Zufallszahl
Setzen von Bit 0
Setzen von Bit 99
Durchführung des SPS-Tests* für die Basen 2,3,5,7,11,13,7,19,23,...
Wenn die Zahl in einem dieser Tests durchfällt, ist sie nicht prim, fortsetzen mit Schritt 1

*SPS-Test:
Z' = Z - 1
S = Anzahl der bei Z' niederwertigen 0-Bits (bei 40 wären dies 3 [101000])
Z'' = Z' / 2^S
For N = 0 TO S DO
Basis ^ (Z'' * 2 ^ S) MOD Z == 1;

Ist diese Gleichung für eine Zahl nicht erfüllt, dann ist Z nicht Stark Pseudo-Prim zur Basis B.

Damit solltest Du eigentlich recht gute Ergebnisse von der Geschwindigkeit erzielen ...


zemy - Mo 25.04.05 16:42

Würde man aber alles auf ca. 30 Primzahlen begrenzen. Wenn es notfalls auch ne Pseudoprimzahl sein kann, einfach ne Zufallszahl in dieser größenordnung generieren und dann durchprobieren, bis ein Pseudoprimtest Ja sagt. Kannst dann auch einen richtigen nachschieben um sicherzugehen (Teiler suchen von 2 bis Wurzel Zahl).


jelleton - Mo 25.04.05 17:39
Titel: Nachfrage
user profile iconBenBE hat folgendes geschrieben:

Z'' = Z' / 2^S
For N = 0 TO S DO
Basis ^ (Z'' * 2 ^ S) MOD Z == 1;

Ist diese Gleichung für eine Zahl nicht erfüllt, dann ist Z nicht Stark Pseudo-Prim zur Basis B.

...



Da frage ich doch noch einmal dumm nach.

Basis ^ (Z'' * 2 ^ S) MOD Z == 1

Gut, aber Z'' ist doch gleich Z' / 2^S, dann ist der Exponent der variablen Basis doch Z', oder sehe ich das falsch, wofür dann das ganze Zeug da drunter?
Oder hat N noch eine weitere Bedeutung als die Häufigkeit zu limitieren?

Ansonsten schonmal danke..


BenBE - Mo 25.04.05 17:43

user profile iconzemy hat folgendes geschrieben:
Kannst dann auch einen richtigen nachschieben um sicherzugehen (Teiler suchen von 2 bis Wurzel Zahl).

LOL Eine 30-stellige Zahl besitzt eine (log (10^30))/2 :arrow: 15-stellige Wurzel ;-) :arrow: Int64 ^^

Ne, das mit den SPS-Tests hat schon seine Berechtigung, denn es gibt für n < 10^25 nur knapp eine Handvoll SPSPs und von denen besitzt keine für mehr als 5-10 Basen Starke Pseudo-Primalität. --> Jede Zahl die 10-20 Basen übersteht ist auch wirklich prim.

user profile iconjelleton hat folgendes geschrieben:
Da frage ich doch noch einmal dumm nach.

Basis ^ (Z'' * 2 ^ S) MOD Z == 1

Gut, aber Z'' ist doch gleich Z' / 2^S, dann ist der Exponent der variablen Basis doch Z', oder sehe ich das falsch, wofür dann das ganze Zeug da drunter?
Oder hat N noch eine weitere Bedeutung als die Häufigkeit zu limitieren?

Ansonsten schonmal danke..


Oops :oops:, Schreibfehler meinerseits: S ersetzen durch N.

Ergänzung, wenn ich schonmal dabei bin:
Es reicht theoretisch aus für N = 0 zu testen. Ist dieser Test erfüllt, sind auch alle weiteren erfüllt, da

(B ^ (Z'') mod Z)^2 == 1^2 == 1 ist.


jelleton - Mo 25.04.05 17:59

user profile iconBenBE hat folgendes geschrieben:


Oops :oops:, Schreibfehler meinerseits: S ersetzen durch N.

Ergänzung, wenn ich schonmal dabei bin:
Es reicht theoretisch aus für N = 0 zu testen. Ist dieser Test erfüllt, sind auch alle weiteren erfüllt, da

(B ^ (Z'') mod Z)^2 == 1^2 == 1 ist.



So macht das auch gleich viel mehr Sinn, werds gleich morgen mal ausprobieren, wenns nicht klappt meld ich mich nochmal, ihr wirkt ja recht hilfsbereit/interessiert..

Bis dahin...


hans mans - Mi 13.07.05 00:33

Also ich hätte hier ein (wenn auch sehr einfaches) recht schnelles Programm:

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:
38:
39:
40:
41:
42:
43:
44:
Program primzahlen;
{$APPTYPE CONSOLE}

uses
  SysUtils;

Const max = 1000000;
Var primzahl    : Array[1..max] Of Integer;
    ergebnis    : Boolean;
    zahln,index : Integer;
    testzahlen  : Array[1..max] Of Integer;
    
Procedure primtest(testzahl : Integer);
Var zaehler : Integer;
Begin
  ergebnis := false;
  If (testzahl<3Then exit;
  zaehler := 1;
  Repeat
    zaehler := zaehler + 1;
    If (testzahl Mod zaehler) = 0 Then ergebnis := true  
                                  Else ergebnis := false;
  Until (ergebnis) Or (zaehler >= SQRT(testzahl))
End;

Begin
  index := 1;
  For zahln := 2 To max Do
  Begin
    primtest(zahln);
    If Not(ergebnis) Then
    Begin
      primzahl[index] := zahln;
      index := index + 1
    End
  End;
  For zahln := 2 To max Do testzahlen[zahln] := zahln;
  For zahln := 1 To index-1 Do
    Begin
      Write(primzahl[zahln]:8);
      If zahln Mod 5 = 0 Then Writeln
    End;
  Readln
End.


Das wichtige ist, dass bei dem Programm nicht geprüft, ob die zu testende Zahl durch jede kleinere Zahl, sondern nur durch jede Primzahl, die kleiner ist, als die zu testende Zahl.


BenBE - Mi 13.07.05 01:02

Der Source ist zwar recht einfach, kommt aber sicherlich an die zwischenzeitlich im Thread genannten Ergebnisse nicht ran. Schätz ich mal. Kannst Du mal paar Messwerte nennen, die Du mit dem Source hattest (+HW-Config)


hans mans - Mi 13.07.05 09:29

Oh Mist... Hab irgendwie übersehen, das es mehrere Seiten gibt :oops: :oops: :oops:
Naja, ich glaub mein Programm is doch gegenüber den hier genannten anderen Lösungen sehr langsam. Genaue Messwerte hab ich aber nicht.


Tilo - Mi 13.07.05 10:20

user profile iconhans mans hat folgendes geschrieben:
Also ich hätte hier ein (wenn auch sehr einfaches) recht schnelles Programm:

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:
38:
39:
40:
41:
42:
43:
44:
Program primzahlen;
{$APPTYPE CONSOLE}

uses
  SysUtils;

Const max = 1000000;
Var primzahl    : Array[1..max] Of Integer;
    ergebnis    : Boolean;
    zahln,index : Integer;
    testzahlen  : Array[1..max] Of Integer;
    
Procedure primtest(testzahl : Integer);
Var zaehler : Integer;
Begin
  ergebnis := false;
  If (testzahl<3Then exit;
  zaehler := 1;
  Repeat
    zaehler := zaehler + 1;
    If (testzahl Mod zaehler) = 0 Then ergebnis := true  
                                  Else ergebnis := false;
  Until (ergebnis) Or (zaehler >= SQRT(testzahl))
End;

Begin
  index := 1;
  For zahln := 2 To max Do
  Begin
    primtest(zahln);
    If Not(ergebnis) Then
    Begin
      primzahl[index] := zahln;
      index := index + 1
    End
  End;
  For zahln := 2 To max Do testzahlen[zahln] := zahln;
  For zahln := 1 To index-1 Do
    Begin
      Write(primzahl[zahln]:8);
      If zahln Mod 5 = 0 Then Writeln
    End;
  Readln
End.


Das wichtige ist, dass bei dem Programm nicht geprüft, ob die zu testende Zahl durch jede kleinere Zahl, sondern nur durch jede Primzahl, die kleiner ist, als die zu testende Zahl.


zur der letzten Behauptung: Nein
Den Zähler mit dem zu testest wird in deinem Code immer 1 erhöht. Dadurch testest du die Zahl auf Teilbarkeit mit allen Zahlen bis zur Wurzel. Du muss den Zähler schon aus deinem Feld der gefundenen PRimzahlen auslesen.

Weitere Verbesserung für den Int64 Bereich (2^64) reicht es ein Feld bis zur Zahl 2^32 auf zu bauen. Alle Primzahlen, die größer als 2^32 sind werden nicht benötigt um den gesamten int 64 Bereich abzudecken, es sein denn man möchte Zahlen größer als 2^64 testen.


FinalFantasy - Mi 13.07.05 15:40

Hallo,


Ich hab mich auch schonmal mit Primzahlberechnung beschäftigt, allerdings hab ich das damals noch mit C (ohne ++) gemacht. Ich bin zu ähnlichen Ergebnissen gekommen, wie ihr hier.
ABER: Was mir gerade so durch den Kopf geschossen ist (da ich seit kurzem einen P4 mit HT habe), könnte man das ganze parallelisieren? So dass das ganze auch mehrere (logische oder physische) CPUs nutzt? Da man die gefundenen Primzahlen ja speichert und zum überprüfen wieder benötigt, ist eine Aufteilung nach dem Motto CPU1 rechnet von 0 bis 100, CPU2 rechnet von 101 bis 200 nicht mehr möglich.
Ausserdem würden mich mal Ergebnisse auf einem 64Bit System interessieren. Da ja hier massiv mit INT64 oder ähnlichem gerechnet wird, sollte doch ein 64-Bit System deutliche Geschwindigkeitsvorteile bringen.


hans mans - Mi 13.07.05 16:06

Oh, Mist... :oops: Auch noch falsches Programm gepostet...


Phantom1 - Mi 13.07.05 17:15

@hans mans: ich habe deinen Code mal getestet: für 10 mio testzahlen, braucht dein code über 94 sekunden (ohne ausgabe der primzahlen). Zum vergleich, schau mal auf Seite 6 (weiter unten auf der seite), dort findest du ein Code von mir, welcher weniger als 0,3 sekunden für 10 mio testzahlen braucht :wink:

getestet mit CPU: AthlonXP-M@2000mhz


ManuelGS - Mi 13.07.05 17:31

den code kannst du noch schneller machen, wenn du die wurzelfunktion selbst schreibst, so dass sie nur eine ganzzahl zurückliefert. bis jetzt sind es ja zig stellen nach dem komma...
das implementieren überlass ich den mathematikern...


Gekko - Mi 13.07.05 17:52

http://www.hardtware.de/products/braingrid.php
Habt ihr das schon gesehen?


enyaw_ecurb - Mi 13.07.05 21:37

8 Seiten zum Durchlesen, ganz schön lang.

Ich hab noch ein paar Ideen für die Leute, denen die Programme immer noch nicht schnell genug
sind.

Wenn man beim Sieb des Erastothenes die Vielfachen einer Zahl wegstreichen will, kann
man davon ausgehen das alle noch nicht weggestrichenen Zahlen bis zum Quadrat dieser Zahl Primzahlen sind.
Z.B.:
Alle Vielfachen von Zwei werden weggestrichen, nächste Primzahl anvisiert, die 3
und alle ungeraden Zahlen bis zur 9 sind Primzahlen.
Das bringt bei großen Zahlen einen nicht erheblichen Speed-Kick.

Außerdem kann man, was ich bei meinem Programm gemacht hab, die Primzahlen in einem Boolean
Array speichern. Man streicht die Zahlen weg indem man den Wert auf 1 oder halt eben 0 setzt.
Das Problem liegt bei der Ausgabe. Man kann zwar die Anzahl der Primzahlen sehr gut
feststellen, aber bei der Ausgabe wird die Zeit wieder rausgeholt.
Es bringt halt eben nur vom Speicher was, besonders wenn man es Bitweise packt.

Ich vermute das ihr das irgendwo in diesen 8 Seiten bestimmt schon erwähnt habt und
falls ich irgendetwas wiederhole tut es mir Leid.

PS: Mein Programm schafft die Primzahlen bis 100 Millionen in 2,7 Sekunden und hat bei
Bitweiser Speicherung einen Verbrauch von 10MB. (AthlonXP 2200+)


Tilo - Do 14.07.05 11:13

user profile iconenyaw_ecurb hat folgendes geschrieben:

Wenn man beim Sieb des Erastothenes die Vielfachen einer Zahl wegstreichen will, kann
man davon ausgehen das alle noch nicht weggestrichenen Zahlen bis zum Quadrat dieser Zahl Primzahlen sind.
Z.B.:
Alle Vielfachen von Zwei werden weggestrichen, nächste Primzahl anvisiert, die 3
und alle ungeraden Zahlen bis zur 9 sind Primzahlen.


Die bisher geposteten schnellen Programme/Routinen verwenden eine abgewandelte und schnellere Form des Siebes. Die gefundenen Primzahlen werden in ein Array gespeichert. Zutestende Zahlen werden nun auf Teilbarkeit mit Primzahlen bis zur Wurzel der zutestenden Zahl geprüft.Wenn keine teilbarkeit, dann neue Prim. Bei Teilbarkeit wird sofort abgebrochen und es kann die nchste Zahl getestet werden. Dieses Verfahren ist bis jetzt das schnellste gewesen.

BEi Vielfachen "wegstreichen" hast man ein Problem. Erst werden z.b. alle Vielfache von 7 weggestrichen, dann alle von 11. Da ergibt sich ein unnützer Aufwand durch die Vielfachen von 77.(jede 11. Zahl bei Eleminieren von x7 werden nochmals beim Eleminieren von x11 bearbeitet.)


Phantom1 - Do 14.07.05 16:49

Tilo hat folgendes geschrieben:
Die bisher geposteten schnellen Programme/Routinen verwenden eine abgewandelte und schnellere Form des Siebes. Die gefundenen Primzahlen werden in ein Array gespeichert. Zutestende Zahlen werden nun auf Teilbarkeit mit Primzahlen bis zur Wurzel der zutestenden Zahl geprüft.Wenn keine teilbarkeit, dann neue Prim. Bei Teilbarkeit wird sofort abgebrochen und es kann die nchste Zahl getestet werden. Dieses Verfahren ist bis jetzt das schnellste gewesen.

BEi Vielfachen "wegstreichen" hast man ein Problem. Erst werden z.b. alle Vielfache von 7 weggestrichen, dann alle von 11. Da ergibt sich ein unnützer Aufwand durch die Vielfachen von 77.(jede 11. Zahl bei Eleminieren von x7 werden nochmals beim Eleminieren von x11 bearbeitet.)


Welche geposteten Routinen meinst du? von GTA-Place?, er benutzt kein Sieb.

Die schnellsten bisher hier geposteten Routinen waren die nach dem Sieb des Erathotenes.

@enyaw_ecurb: nicht schlecht nur 2,7 sekunden für 100 mio testzahlen. Könntest du den Code posten? ich würde gern wissen wie du das gemacht hast. Zum vergleich: mein code braucht dafür etwa 4 sekunden.

mfg
Phantom1


ScorpionKing - Do 14.07.05 16:58

2,7 Sekunden! :shock:
Das ist doch verdammt schnell! Geht das überhaupt?


enyaw_ecurb - Do 14.07.05 17:27

Korrektur:
Ich habe einen AthlonXP 2800+ mit 2200Mhz, habs vertauscht.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
while i < bisw do           //Wurzel von bis                                          
begin                                                                 
i := i+i + 2*i*i;             //Das Quadrat von i ausrechnen, hab ich oben erklärt
 while i < bis do                                                 
 begin                                                            
  if Primfeld.bits[i] = false then
  begin
  Primfeld.bits[i] := true;  
  l := l+1;
  end;
  i := i + x;                                                 
 end;                                                             
i := z + 1;                                                           
 while Primfeld.bits[i] = true do                                 
 begin
 i := i+1;                                                           
 end;                                                             
z := i;                                                               
x := i+i+1;                                                           
end;


Der Code ist nicht wirklich optimiert, ich meine vom Syntax her, da ich kein
sonderlich guter Delphi-Programmierer bin.
Wichtig ist, dass mein Feld nur die Ungeraden enthält und mit 0 beginnt, d.h. Index No 1
ist Zahl 3. i+i+1 entspricht der wirklichen Zahl von i. l zählt die Primzahlen, bzw.
l zählt welche Zahlen keine sind und bei der Ausgabe muss subtrahiert werden.
x enthält immer die wirkliche Zahl von i.

Bei der Ausgabe der Zahlen muss natürlich mit i+i+1 umgewandelt werden und
deshalb dauert das ein wenig länger.

Zusätzlich ist mir noch eingefallen, dass man noch ein paar Stempel benutzen kann,
hat glaube ich noch niemand erwähnt.
Hier ist ziemlich gut erklärt was Stempel sind:

http://www.devalco.de/sieb_des_Ulam.htm

Bei meiner Variante wäre ein Stempel zu umständlich, da die Indexberechnung ja schon ohne
die Vielfachen von zwei kompliziert ist.

Falls ich irgendwelche Fehler oder Wiederholungen eingebaut hab tuts mir wie gesagt
Leid.

Moderiert von user profile iconraziel: Code- durch Delphi-Tags ersetzt.


Phantom1 - Do 14.07.05 18:00

@enyaw_ecurb: danke für den Code, leider bekomme ich ihn nicht zum laufen!

Hier dein Code, so wie ich ihn verwenden wollte:


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:
procedure SavePrimes1(MinPrime, MaxPrime: Cardinal; const FileName: String = '');
var                                  //
  i, l, x, z: Cardinal;
  SL: TStringList;
  Primfeld: TBits;
begin
  Primfeld:=TBits.Create;
  Primfeld.Size:=MaxPrime;
  i:=0; l:=0; x:=0; z:=0;
  while i < Trunc(Sqrt(MaxPrime)) do begin          //Wurzel von bis
    i:=i+i + 2*i*i;             //Das Quadrat von i ausrechnen, hab ich oben erklärt
    while i < MaxPrime do begin
      if Primfeld.bits[i] = false then begin
        Primfeld.bits[i] := true;
        l:=l+1;
      end;
      i:=i+x;
    end;
    i:=z+1;
    while Primfeld.bits[i] = true do
      i:=i+1;
    z:=i;
    x:=i+i+1;
  end;

  //
  // Ausgabe
  //

  Primfeld.Free;
end;


Wozu dient die Variable l ? auf diese wird in dem Code nicht mehr zugegriffen.

mfg
Phantom1


Tilo - Do 14.07.05 18:05

user profile iconPhantom1 hat folgendes geschrieben:

Welche geposteten Routinen meinst du? von GTA-Place?, er benutzt kein Sieb.

Die schnellsten bisher hier geposteten Routinen waren die nach dem Sieb des Erathotenes.


Die schnellen Codes verwenden alle ein Array, in dem die bisher gefundenen Primzahlen gespeichert werden. Nur diese Zahlen werden dann bei Primzahlentest berücksichtigt. Das ist meiner Meinung nach eine abgewandelte Form des Siebes des Erathotenes. Da werden ja auch Vielfache der Primzahlen eleminiert.


enyaw_ecurb - Do 14.07.05 18:13

Ich bin schon ein Trottel, muss ich natürlich dazusagen:

x:=drei, l:=null, i:=eins, z:=drei

dann müsste es eigentlich laufen.
Wegen der Variable l zählt einfach die Primzahlen mit.
Steht eigentlich schon oben...
Brauchst du bei der Ausgabe. Es gibt soundsoviele Primzahlen im Zahlenraum von...


Tilo - Do 14.07.05 18:18

user profile iconPhantom1 hat folgendes geschrieben:
@enyaw_ecurb: danke für den Code, leider bekomme ich ihn nicht zum laufen!

Hier dein Code, so wie ich ihn verwenden wollte:


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:
procedure SavePrimes1(MinPrime, MaxPrime: Cardinal; const FileName: String = '');
var                                  //
  i, l, x, z: Cardinal;
  SL: TStringList;
  Primfeld: TBits;
begin
  Primfeld:=TBits.Create;
  Primfeld.Size:=MaxPrime;
  i:=0; l:=0; x:=0; z:=0;
  while i < Trunc(Sqrt(MaxPrime)) do begin          //Wurzel von bis
    i:=i+i + 2*i*i;             //Das Quadrat von i ausrechnen, hab ich oben erklärt
    while i < MaxPrime do begin
      if Primfeld.bits[i] = false then begin
        Primfeld.bits[i] := true;
        l:=l+1;
      end;
      i:=i+x;
    end;
    i:=z+1;
    while Primfeld.bits[i] = true do
      i:=i+1;
    z:=i;
    x:=i+i+1;
  end;

  //
  // Ausgabe
  //

  Primfeld.Free;
end;


Wozu dient die Variable l ? auf diese wird in dem Code nicht mehr zugegriffen.

mfg
Phantom1


Durch Optimierung wird Zeile 15 übersprungen. i bleibt immer 0. Endlosschleife.


BenBE - Do 14.07.05 18:48

i wird in Zeile 19 initialisiert.
Zeile 15 ist nur für die Zählung der Primzahlen zuständig.

Insgesamt kann man an der Routine kaum noch was optimieren.

BTW: Du nutzt bereits einen 2er-Stempel.


enyaw_ecurb - Do 14.07.05 19:40

Exakt, ich benutze schon einen 2-Stempel.
Ich meinte nur, dass man noch größere Stempel benutzen kann.
Das Programm ecprime von Walisch Kim hat einen Stempel, der
die Vielfachen von 2,3,5,7,11 und 13 eliminiert und schafft die Primzahlen
bis 1 Milliarde auf meinem Rechner in 0,4 Sekunden!!


ScorpionKing - Do 14.07.05 19:57

user profile iconenyaw_ecurb hat folgendes geschrieben:
Exakt, ich benutze schon einen 2-Stempel.
Ich meinte nur, dass man noch größere Stempel benutzen kann.
Das Programm ecprime von Walisch Kim hat einen Stempel, der
die Vielfachen von 2,3,5,7,11 und 13 eliminiert und schafft die Primzahlen
bis 1 Milliarde auf meinem Rechner in 0,4 Sekunden!!


ich habe die selbse funktion ausprobiert. bei mir brauch die 4 sek!


Phantom1 - Do 14.07.05 20:00

@enyaw_ecurb: So, jetzt konnte ich deinen Code mal direkt vergleichen, dein Code braucht bei mir 5,4 sekunden für 100 mio testzahlen (ohne ausgabe). Somit ist dein Code leider doch langsamer als meiner (bei mir 3,7 sekunden für 100 mio testzahlen). Schade eigentlich, ich dachte ich könnte meinen Code noch verbessern.

EDIT: zeitwert korrektur


enyaw_ecurb - Do 14.07.05 20:19

Irgendwie komisch,
bei euch kommen ganz andere Zeiten raus.
Ich hab beides noch mal überprüft und die Zeiten von oben stimmen.
Liegt wohl an der Taktrate oder meine Uhr ist kaputt.
Eigentlich schade...


Phantom1 - Do 14.07.05 20:32

Ja, durch die verschiedenen Taktraten kommen auch unterschiedliche Zeiten raus. Wenn man aber alles auf einem Rechner testet, kann man es gut vergleichen. Für die Zeitmessung verwende ich QueryPerformanceCounter() im RealTime-Modus.


GTA-Place - So 07.08.05 10:15

Hab mal bisschen weitergeforscht und nochmal ne knapp eine Sekunde gespart, wenn ich als Rückgabewert Byte statts Boolean nehme (0 oder 1).


EDIT: Nach der Hilfe wird Booelan als Byte gespeichert und deshalb ist es ein bisschen langsamer, da das ja praktisch so aussehen würde:

Delphi-Quelltext
1:
2:
3:
4:
if Prim = True then
  Prim := 1
else
  Prim := 0;

Direkte Zuweisung ist dann ein bisschen schneller.


GTA-Place - So 07.08.05 12:36

Hab grad ne Funktion für das Sieb des Es.... geschrieben.
Wenn die Zahlen stimmen, dann ist diese Funktion über 10 Sekunden schneller!
Für 20.000.000 Zahlen statt 16 Sekunden nur noch 5 Sekunden.


Phantom1 - So 07.08.05 12:57

Zum vergleich braucht meine optimierte Version funktion (nach dem Sieb d. E.) für 20 mio testzahlen nur 0,6 sekunden :wink: Es gibt aber noch viel schnellere Algorythmen, dagegen ist unserer Code ziemlich langsam *g

mfg
Phantom1


GTA-Place - So 07.08.05 13:04

Ich hol dich auch noch ein ^^ Nur noch 3 Sekunden.


EDIT: 2 Sekunden :-)
EDIT: 1 Sekunde :P


Phantom1 - So 07.08.05 13:29

Damit du selbst besser vergleichen kannst (da wir ja unterschiedliche Rechner haben), hier mein aktueller Code:

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:
procedure SavePrimes(MinPrime, MaxPrime: Cardinal; const FileName: string = '');
var
  isPrime: array of Cardinal;
  i, j: Cardinal;
  SL: TStringList;
begin
  SetLength(isPrime, MaxPrime shr 5);
  isPrime[0]:=$00000003// bit 0 und 1 ist keine primzahl

  for i:=2 to Trunc(Sqrt(MaxPrime)) do
    if isPrime[i shr 5and (1 shl (i mod 32)) = 0 then begin
      j:=i*i;
      while j<=MaxPrime do begin
        isPrime[j shr 5]:=isPrime[j shr 5or (1 shl (j mod 32));
        Inc(j, i);
      end;
    end;

  if FileName<>'' then begin
    SL:=TStringList.Create;
    try
      for i:=MinPrime To MaxPrime do
        if isPrime[i shr 5and (1 shl (i mod 32)) = 0 then
          SL.Add(IntToStr(i));
      SL.SaveToFile(FileName);
    finally
      FreeAndNil(SL);
    end;
  end;
end;


mfg
Phantom1


GTA-Place - So 07.08.05 13:44

Meine Funktion braucht jetzt auch 0,6 Sekunden (ohne das ich deine gesehen habe).
Ich mach jetzt mal ein Test mit deiner.

EDIT:
Deine: 0,9 Sekunden
Meine: 0,6 Sekunden

Wenn man mal mit meiner letzten vergleicht: 16 Sekunden -> 0,6 Sekunden = 96% schneller.


Phantom1 - So 07.08.05 15:29

Ich würde den Code gerne mal sehen, wenn du nix dagegen hast :wink:

mfg
phantom1


GTA-Place - So 07.08.05 15:35

Klar, war bis jetzt immer OpenSource hier, allerdings versuch ich noch was zu optimieren.
Source gibts spätestens heute Abend.


GTA-Place - So 07.08.05 19:17

So hier der Source:


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:
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:
var
  Start:    Single;
  Ende:     Single;
  ZList:    Array of Byte;
  CheckSch: Integer;
  NrCount:  Integer;
  PosCount: Integer;
  Zahl:     Cardinal;
  Max:      Cardinal;
  PrimF:    TStringList;
begin
  if (not (TryStrToInt(VonEdit.Text, Von))) OR
     (not (TryStrToInt(BisEdit.Text, Bis))) OR
     (Von < 0OR (Bis < 0OR (Von > Bis) then
  begin
    ShowMessage('Ungültige Eingabe(n)!');
    Exit;
  end;

  Max := (Bis div 2) + 1;
  SetLength(ZList, Max);

  PrimLab.Caption := 'Überprüfe Zahlen...';
  Application.ProcessMessages;

// ************************************************************************** //

  Start := GetTickCount;

  PosCount := 0;
  for CheckSch := Von + 1 to Bis + 1 do
  begin
    if odd(CheckSch) then
    begin
      ZList[PosCount] := 1;
      inc(PosCount);
    end;
  end;

  PosCount := 0;
  for CheckSch := 3 to Trunc(sqrt(Bis)) do
  begin
    if odd(CheckSch) then
    begin
      if ZList[PosCount] = 1 then
      begin
        Zahl    := CheckSch;
        NrCount := ((Zahl * Zahl) - 3div 2;

        while (NrCount <= Abs(Max)) do
        begin
          if ZList[NrCount] = 1 then
            ZList[NrCount] := 0;
          inc(NrCount, Zahl);
        end;
      end;

      inc(PosCount);
    end;
  end;

  Ende := GetTickCount;
  DauerLab.Caption := 'Diese Überprüfung hat ' +
                      FloatToStr((Ende - Start) / 1000) +
                      ' Sekunden gedauert.';
  Application.ProcessMessages;

// ************************************************************************** //

  PrimF := TStringList.Create;

  if Von < 3 then
    PrimF.Add('2');

  PosCount := 0;
  for CheckSch := 3 to Bis do
  begin
    if odd(CheckSch) then
    begin
      if ZList[PosCount] = 1 then
        PrimF.Add(IntToStr(CheckSch));

      inc(PosCount);
    end;
  end;

  PrimF.SaveToFile('Paket.prim');
  PrimF.Free;

  PrimLab.Caption := 'Fertig. Warte auf Eingabe...';
end;


Statistiken:


Phantom1 - So 07.08.05 19:42

Dein Code berechnet nur die hälfte der Testzahlen :!: Also schau bitte nochmal nach.

mfg
Phantom1


GTA-Place - So 07.08.05 19:44

Oh, da ist wohl grad was schief gelaufen. Einen Moment...


EDIT: Finde den Fehler erstmal nicht und bin ab Morgen ja weg, also werde ich in einer Woche weitersuchen.
Ich bitte euch erstmal keinen Source mehr zu posten bzw. meinen zu verbessern und die Diskussion bis zu meiner Rückkehr einzustellen. Danke!
(Ich mags nicht, wenn man an so einem wichtigen Thema ohne mich diskutiert :wink: )

EDIT2: Fehler gefunden und verbessert. Source oben.


Phantom1 - So 14.08.05 22:18

@GTA: dein Code ist tatsächlich schneller ;O) allerdings solltest du beachten, das die komplette Zeit inkl dem speicher reservieren und freigeben gemessen werden sollte!

Ich konnte es so natürlich nicht lassen und hab mich nochmal rangesetzt, diesmal benutze ich einen 30er Stempel (auch Bit-komprimiert wie mein bisheriger Code) das spart schonmal eine menge RAM. Ich hab den Code jetzt zwar noch nicht auf schnelligkeit optimiert, aber das resultat ist trotzdem nicht schlecht ;O)


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:
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:
procedure SavePrimes(MaxPrime: Cardinal; const FileName: String = '');
const
  Stempel: array[0..7of Byte = (17111317192329);
var
  isPrime: array of Byte;
  i, j: Cardinal;
  f: TextFile;

  procedure PrimSieve(n, nbit: Cardinal);
  var Num, Num2, m, mbit: Cardinal;
  begin
    Num:=n*30+nbit;
    Num2:=Num*Num;

    mbit:=Num2 mod 30;
    m:=(Num2-mbit) div 30;

    for Num2:=Num to Trunc(MaxPrime/Num) do begin
      case mbit of
        1: isPrime[m]:=isPrime[m] or 1;
        7: isPrime[m]:=isPrime[m] or 2;
       11: isPrime[m]:=isPrime[m] or 4;
       13: isPrime[m]:=isPrime[m] or 8;
       17: isPrime[m]:=isPrime[m] or 16;
       19: isPrime[m]:=isPrime[m] or 32;
       23: isPrime[m]:=isPrime[m] or 64;
       29: isPrime[m]:=isPrime[m] or 128;
      end;
      Inc(m, n);
      Inc(mbit, nbit);
      if mbit>29 then begin
        Dec(mbit, 30);
        Inc(m);
      end;
    end;
  end;

begin
  SetLength(isPrime, Trunc(MaxPrime/30));

  for j:=1 to 7 do
    PrimSieve(0, Stempel[j]);

  for i:=1 to Trunc(Sqrt(MaxPrime)/30do
    for j:=0 to 7 do
      if isPrime[i] and (1 shl j) = 0 then
        PrimSieve(i, Stempel[j]);

  if FileName<>'' then begin
    AssignFile(f, FileName);
    try
      ReWrite(f);
      WriteLn(f, '2');
      WriteLn(f, '3');
      WriteLn(f, '5');
      for i:=1 to 7 do
        WriteLn(f, IntToStr(Stempel[i]));
      for i:=1 To Trunc(MaxPrime/30do
        for j:=0 to 7 do begin
          if i*30+Stempel[j]>MaxPrime then
            Break;
          if isPrime[i] and (1 shl j)=0 then
            WriteLn(f, IntToStr(i*30+Stempel[j]));
        end;
    finally
      CloseFile(f);
    end;
  end;
end;


Hier der direkte Vergleich:

bei 20 mio testzahlen:
GTA-Place Code: 526ms (RAM-Verbrauch: 9,5 MB)
mein alter Code: 583ms (RAM-Verbrauch: 2,4 MB)
mein neuer Code: 303ms (RAM-Verbrauch: 0,6 MB)

bei 100 mio testzahlen:
GTA-Place Code: 2889ms (RAM-Verbrauch: 47,7 MB)
mein alter Code: 3621ms (RAM-Verbrauch: 11,9 MB)
mein neuer Code: 2404ms (RAM-Verbrauch: 3,2 MB)

bei 500 mio testzahlen:
GTA-Place Code: 15765ms (RAM-Verbrauch: 238,4 MB)
mein alter Code: 20896ms (RAM-Verbrauch: 59,6 MB)
mein neuer Code: 14207ms (RAM-Verbrauch: 15,9 MB)

Die zeiten wurden wie immer im RealTime-Mode gemessen und mit hilfe vom RDTSC (ist noch etwas genauer als der QueryPerformanceCounter). CPU = AtlhonXP-M@2000MHz

So langsam machen wir ja fortschritte ^^ aber an ecprime kommen wir noch lange nicht ran (ecprime braucht bei 500 mio testzahlen nur lächerliche 230ms !).

PS: Ich habe spaßeshalber mal mit meinem Programm alle primzahlen bis 2^32 gespeichert, die Datei war anschließend 2,2 GB groß :shock:

mfg
Phantom1


alzaimar - Mi 17.08.05 21:51

Und ich hab die Performance nochmal um ca 60% eingedampft :mrgreen:



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:
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:
procedure AlzSavePrimes(MaxPrime: Cardinal; const FileName: String = '');
const
  Stempel: array[0..7of Byte = (17111317192329);
  P2 : Array [0..7of byte = (1,2,4,8,16,32,64,128);
var
  isPrime: array of Byte;
  i, j: Cardinal;
  f: TextFile;

  procedure PrimSieve(n, nbit: Cardinal);
  Const
    Mods : Array [0..29of Byte = (0 , 000002000,
   408000,160,320,00,64000000,128);

  var Num, Num2, m, mbit: Cardinal;
  begin
    Num:=n*30+nbit;
    Num2:=Num*Num;

    mbit:=Num2 mod 30;
    m:=(Num2-mbit) div 30;

    for Num2:=Num to MaxPrime div Num do begin
      isPrime[m]:=isPrime[m] or mods[mbit];
      Inc(m, n);
      Inc(mbit, nbit);
      if mbit>29 then begin
        Dec(mbit, 30);
        Inc(m);
      end;
    end;
  end;

begin
  SetLength(isPrime, Trunc(MaxPrime/30));

  for j:=1 to 7 do
    PrimSieve(0, Stempel[j]);

  for i:=1 to Trunc(Sqrt(MaxPrime)/30do
    for j:=0 to 7 do
      if isPrime[i] and p2[j] = 0 then
        PrimSieve(i, Stempel[j]);

  if FileName<>'' then begin
    AssignFile(f, FileName);
    try
      ReWrite(f);
      WriteLn(f, '2');
      WriteLn(f, '3');
      WriteLn(f, '5');
      for i:=1 to 7 do
        WriteLn(f, IntToStr(Stempel[i]));
      for i:=1 To Trunc(MaxPrime/30do
        for j:=0 to 7 do begin
          if i*30+Stempel[j]>MaxPrime then

            Break;
          if isPrime[i] and (1 shl j)=0 then
            WriteLn(f, IntToStr(i*30+Stempel[j]));
        end;
    finally
      CloseFile(f);
    end;
  end;
end;

[edit]den Rat von Phantom1 befolgt und 54 in 64 verbessert[/edit]


Phantom1 - Mi 17.08.05 22:01

@alzaimar: ich hab den code zwar noch nicht probiert, aber ein fehler ist vorhanden: In der P2 Array deklariation, da steht bei dir 54, es müsste aber 64 sein.

EDIT:
So hab den Code jetzt getestet: er ist leider 15% langsamer. Ist eigentlich auch logisch, da ein zugriff auf ein Array länger dauert als ein direktes "case of".


alzaimar - Mi 17.08.05 22:50

Lustig, bei kleinen N (10000000 M) ist mein Algo ca. doppelt so schnell.
Bei N=32000000 ist der Unterschied am Deutlichsten, hier ist meine Variante um 60% Schneller.
Aber dann: Bei N=80000000 sind beide Varianten gleich schnell, aber dann wird meine Variante langsamer...

Lustig, liegt das an der Sprungvorhersage (Case-Anweisungen werden als if then-Ketten codiert), oder woran? Vielleicht kann man das ja rauskriegen?

Ansonsten, wirklich Schade.

Edit: Ich hab mal ein kleines Vergleichsprogramm mit TChart geschreben, das das Laufzeizverhalten der beiden Algorithmen vergleicht. Man sieht ganz deutlich einen Knick im zeitlichen Verlauf bei etwa 32.Mio.


Phantom1 - Mi 17.08.05 23:28

In dem Mods Array sind auch noch falsche werte, so sollte es aussehen:

Delphi-Quelltext
1:
Mods: Array [0..29of Byte = (01000002000408000160320006400000128)                    

Zumdem wäre es besser dieses Array schon direkt bei SavePrimes als const zu deklarieren, weil sonst das Array bei jedem durchlauf von PrimSieve neu angelegt werden muss.

Hier mal ein paar neue ergebnisse:

bei 1 mio testzahlen:
mein Code: 11ms
von dir modifizierter Code: 7ms

bei 32 mio testzahlen:
mein Code: 631ms
von dir modifizierter Code: 600ms

bei 100 mio testzahlen:
mein Code: 2431ms
von dir modifizierter Code: 2442ms

bei 500 mio testzahlen:
mein Code: 14282ms
von dir modifizierter Code: 15177ms


alzaimar - Do 18.08.05 17:59

Ich habe mein Testprogramm auf meinem Athlon 64bit 3000+ getestet, und da ist meine Routine auch bei 500 Mio Testdaten noch schneller, und zwar auch bei 500 Mio (15800 zu 12200 ms, oder knapp 35% schneller)...

Also liegt es an der Adressierung bei den 'kleinen' CPU und nicht am Algo. Wenn man jetzt das Bitarray in jeweils ca. 1MB grosse Stücke unterteilt, dürfte diese Macke der CPU ausgehebelt werden.


F34r0fTh3D4rk - Do 18.08.05 18:39

user profile iconGTA-Place hat folgendes geschrieben:
Hab mal bisschen weitergeforscht und nochmal ne knapp eine Sekunde gespart, wenn ich als Rückgabewert Byte statts Boolean nehme (0 oder 1).


du meinst bits oder ? weil

1 bit = 1 oder 0

8 bits = 1 byte

würdest du bytes nehmen kannst es ja auch noch auf bits optmieren :wink:

eigentlich gibt es ja noch keine formel um primzahlen zu berechnen, weil es eben kein system gibt, jetzt stelle ich einfach mal die behauptung 999999999999991 sei eine primzahl und jemand beweise mir das gegenteil. :?


hallo - Do 18.08.05 19:04

Da hat es schon größere Zahlen gegeben!
Die größte bekannte Primzahl hat einige Millionen Stellen!

Was man jedoch auch beachten soll: Wenn man schon geschaut hat ob die Zahl durch 3 teilbar ist, darf man nicht mehr schauen ob sie auch durch 6 9 etc. geht!


F34r0fTh3D4rk - Do 18.08.05 19:22

mir ist nur aufgefallen, dass alle zahlen die aus Neunen bestehen und am ende eine 1 haben immer primzahlen zu sein scheinen, demnach wäre das hier auch eine:
Zitat:

99999999999999
99999999999999
99999999999999
99999999999999
99999999999999
99999999999999
99999999999999
99999999999999
99999999999999
99999999999999
99999999999999
99999999999999
99999999999999
99999999999999
99999999999999
99999999999999
1


GTA-Place - Do 18.08.05 19:30

user profile iconhallo hat folgendes geschrieben:
Da hat es schon größere Zahlen gegeben!
Die größte bekannte Primzahl hat einige Millionen Stellen!

Was man jedoch auch beachten soll: Wenn man schon geschaut hat ob die Zahl durch 3 teilbar ist, darf man nicht mehr schauen ob sie auch durch 6 9 etc. geht!


Bist aber ein ganz schlauer.

1. Wissen wir das.
2. Beim Sieb des E. kommt Prüfung durch 6 oder 9 nicht vor, da ich ja schon durch 3 geteilt habe.

user profile iconF34r0fTh3D4rk hat folgendes geschrieben:
du meinst bits oder ? weil

1 bit = 1 oder 0

8 bits = 1 byte

würdest du bytes nehmen kannst es ja auch noch auf bits optmieren :wink:

Ne ich mein schon Bytes, aber wenns auch Bits bei Delphi gibt, dann kann ich das noch etwa optimieren.


zemy - Fr 19.08.05 09:57

user profile iconF34r0fTh3D4rk hat folgendes geschrieben:
mir ist nur aufgefallen, dass alle zahlen die aus Neunen bestehen und am ende eine 1 haben immer primzahlen zu sein scheinen, demnach wäre das hier auch eine:
Zitat:

99999999999999
99999999999999
99999999999999
99999999999999
99999999999999
99999999999999
99999999999999
99999999999999
99999999999999
99999999999999
99999999999999
99999999999999
99999999999999
99999999999999
99999999999999
99999999999999
1


Wenns so einfach währe ;) Bis zur 1-Million scheints ja zu Stimmen. Und drüber hinaus? Traust du dir zu, aus 5-6 Zahlen eine allgemein-gültige Regel zu definieren?


Phantom1 - Sa 20.08.05 16:18

Hab mein Algo weiter optimiert, diesmal hab ich das Array in gleich große Stücke (64Kilobyte) unterteilt. War ziemlich knifflig und schwieriger als ich dachte, aber es geht erstmal 8) Auch hier habe ich ein 30er Stempel benutzt mit Bit-Komprimierung.

Hier der Code:

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:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
procedure SavePrimes(MaxPrime: Cardinal; const FileName: String = '');
const
  STEMPEL: array[0..7of Byte = (17111317192329);
var
  Primes: array[0..MAXWORD] of Byte;  // 64 KB
  i, j, k, PrimeLen, PrimeBits, Num, Num2, m, mbit: Cardinal;
  f: TextFile;
begin
  if FileName<>'' then begin
    AssignFile(f, FileName);
    ReWrite(f);
    WriteLn(f, '2'+#10#13+'3'+#10#13+'5');
  end;
  PrimeLen:=Length(Primes);
  PrimeBits:=PrimeLen*30;
  for k:=0 to Trunc(MaxPrime/PrimeBits) do begin
    FillChar(Primes, PrimeLen, 0);
    for i:=0 to Trunc(Sqrt((k+1)*PrimeBits)/30do
      for j:=0 to 7 do
        if not ((i=0and (j=0)) and ((Primes[i] and (1 shl j)=0or (k>0)) then begin
          Num:=i*30+STEMPEL[j];
          if k=0 then
            Num2:=Num*Num
          else
            Num2:=Trunc(k*PrimeBits/Num)*Num+Num;
          mbit:=Num2 mod 30;
          m:=(Num2-mbit) div 30-k*PrimeLen;
          while m<PrimeLen do begin
            case mbit of
              1: Primes[m]:=Primes[m] or 1;
              7: Primes[m]:=Primes[m] or 2;
             11: Primes[m]:=Primes[m] or 4;
             13: Primes[m]:=Primes[m] or 8;
             17: Primes[m]:=Primes[m] or 16;
             19: Primes[m]:=Primes[m] or 32;
             23: Primes[m]:=Primes[m] or 64;
             29: Primes[m]:=Primes[m] or 128;
            end;
            Inc(m, i);
            Inc(mbit, STEMPEL[j]);
            if mbit>29 then begin
              Dec(mbit, 30);
              Inc(m);
            end;
          end;
        end;
    if FileName<>'' then
      for i:=0 To PrimeLen-1 do
        for j:=0 to 7 do begin
          if k*PrimeBits+i*30+STEMPEL[j]>MaxPrime then
            Break;
          if not ((i=0and (j=0and (k=0)) and (Primes[i] and (1 shl j)=0then
            WriteLn(f, IntToStr(k*PrimeBits+i*30+STEMPEL[j]));
        end;
  end;
  if FileName<>'' then
    CloseFile(f);
end;


Hier die Ergebnisse:

bei 100 mio testzahlen:
alt: 2478ms (RAM-Verbrauch: 11,9 MB)
neu: 2060ms (RAM-Verbrauch: 64,0 KB)

bei 500 mio testzahlen:
alt: 14598ms (RAM-Verbrauch: 59,6 MB)
neu: 11525ms (RAM-Verbrauch: 64,0 KB)

mfg
Phantom1


alzaimar - Sa 20.08.05 18:43

Bau doch meine Zugriffe mit den Arrays 'mods' und 'p2' ein, anstatt diese übersichtlichen, aber langsamen case of - Anweisungen, dann sparst Du wieder 60%.
Ich habe es auf meinem Lappi getestet, und da ist da Verhältnis bei 500 mio: 17300 (mit CASE) zu 9000 (mit Array). Wieso sollte ein "Case of" eigentlich schneller sein, als ein Zugriff auf ein Array? Schau Dir mal den Assembler an, den Delphi aus einem "Case of" macht.

Dann noch das Trunc(k*PrimeBits/Num) in ein MulDiv (k,PrimeBits, Num), und Du sparst nochmal 1500 ms ein.

Abschliessend habe ich noch die unnötigen Zugriffe auf STEMPEL[J] aus der innersten While Schleife rausgeholt, das brachte mit ein paar Kleinigkeiten nochmal 1100 ms, sodass ich (also, eigentlich ja Du) jetzt bei 6300 ms bin, also fast 3x schneller.

Eigentlich müsstest Du noch die Zugriffe auf das Primes-Array von Primes[m] in einen Zeiger umändern (und den inkrementieren), das dürfte nochmal Einiges bringen, aber das bekomme ich nicht hin. Dein Code ist zwar kompakt, aber die ziemlich lange "If not ((i=0) or ....)" Abfrage wird ja doch oft ausgeführt und ist nicht wirklich optimal.


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:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
procedure AlzSavePrimes (MaxPrime: Cardinal; const FileName: String = '');
const
  STEMPEL: array[0..7of Byte = (17111317192329);
  P2 : Array [0..7of byte = (1,2,4,8,16,32,64,128);
  Mods: Array [0..29of Byte =
    (01000002000408,
     000160320006400000128);

var
  Primes: array[0..MAXWORD] of Byte;  // 64 KB
  s, i30, i, j, y, z,k, PrimeLen, PrimeBits, Num, Num2, m, mbit: Cardinal;
  f: TextFile;
begin
  if FileName<>'' then begin
    AssignFile(f, FileName);
    ReWrite(f);
    WriteLn(f, '2'+#10#13+'3'+#10#13+'5');
  end;
  PrimeLen:=Length(Primes);
  PrimeBits:=PrimeLen*30;
  z := 0;
  for k:=0 to Trunc(MaxPrime/PrimeBits) do begin
    FillChar(Primes, PrimeLen, 0);
    z := k*PrimeLen;
    for i:=0 to Trunc(Sqrt((k+1)*PrimeBits)/30do begin
      i30 := i*30;
      for j:=0 to 7 do
        if not ((i=0and (j=0)) and ((Primes[i] and p2[j]=0or (k>0))  Then Begin
          s := Stempel[j];
          Num := i30 + s;
          if k=0 then
            Num2 := Num * Num
          else
            Num2 := MulDiv (k, PrimeBits, Num) * Num + Num;
          mbit := Num2 mod 30;
          m := (Num2-mbit) div 30 - z;
          while m<PrimeLen do begin
            primes[m] := Primes[m] or mods [mbit];
            Inc(m, i);
            Inc(mbit, s);
            if mbit>29 then begin
              Dec(mbit, 30);
              Inc(m);
            end;
          end;
        end;
      End;

    if FileName<>'' then
      for i:=0 To PrimeLen-1 do
        for j:=0 to 7 do begin
          if k*PrimeBits+i*30+STEMPEL[j]>MaxPrime then
            Break;
          if not ((i=0and (j=0and (k=0)) and (Primes[i] and (1 shl j)=0then
            WriteLn(f, IntToStr(k*PrimeBits+i*30+STEMPEL[j]));
        end;
  end;
  if FileName<>'' then
    CloseFile(f);
end;

Nebenbei steht im ecprime-sourcecode drin, das die das im L1-Cache laufen lassen. Wie geht das denn?


AXMD - Sa 20.08.05 18:59

Bin nicht sooo der Experte, aber ist IntToStr nicht recht langsam? Da gibt's doch bestimmt andere Möglichkeiten? Z.B. den Integer direkt in die Datei schreiben ;)

AXMD


BenBE - Sa 20.08.05 19:02

Soweit ich mich erinnere führt die CPU generell aus dem L1-Cache aus. Was die Leute im ecprimes-Source wahrscheinlich meinenn ist, dass die ihren Source so programmiert haben, dass die mit möglichst wenig Daten auskommen können und die CPU dadurch jegliche benötigten Informationen bereits im L1-Cache stehen hat, also nicht aus L2 oder RAM nachladen brauch.

D.h. auf diesen Source angewendet: Jegliche Zugriffe auf Daten müssten so geartet sein, dass NIE außerhalb eines etwa 16 KB großen Blockes Daten gelesen werden. Schaut für genauere Infos einfach mal auf die Seite von Intel oder AMD. Die haben dort recht gute Informationen zum Optimieren der Programme auf die Prozessoren.

Tipp: Indirekte Adressierungen vermeiden! Also z.B. Pointer auf einen Pointer oder Bit-Operationen die Bitshiftings im Speicher benötigen.
Außerdem sollten möglichst viele Infos gleich in den Registern gehalten werden.


alzaimar - Sa 20.08.05 19:20

@Benny: Danke für die Info: Ich check den Source von ecprime mal auf deine Aussagen.
@AXMD: Das IntToStr steht ja nur im Output, das ist nicht wirklich entscheidend bei Performancemessungen: Ich messe natürlich immer mit Filename = ''...


Phantom1 - Sa 20.08.05 19:42

@alzaimar: Ich hatte den Code noch nicht optimiert, ich war grad erst fertig mit der Array-aufteilung, hatte also noch keine Zeit dazu. Die ganzen optimierungen die du aufgezählt hast sind mir selbstverständlich bekannt ;O) Die Sache mit dem Zeiger aufs Primes-Array hab ich früher schonmal probiert, jedoch wurde es etwas langsamer.

@ASMD: wie bereits gesagt wurde, wird IntToStr nur bei der Ausgabe benutzt. Für mich steht im vordergrund aber erstmal nur die Berechnung. Ich werd mich später darum kümmern.

Das was BenBE sagt stimmt, die Daten werden generell aus dem L1-Cache benutzt. Ich glaub dort werden hauptsächlich die Adressen der Array-zugriffe gespeichert, wodurch es beim nächsten Aufruf schneller geht. Soweit ich weiß hat mein Athlon-XP-M einen 128KB (64KBData/64KBInstructionen) L1-Cache und einen 512KB großen L2 Cache, aus dem Grund habe ich eine Große von 64KB gewählt.


Phantom1 - Sa 20.08.05 21:11

user profile iconalzaimar hat folgendes geschrieben:
Bau doch meine Zugriffe mit den Arrays 'mods' und 'p2' ein, anstatt diese übersichtlichen, aber langsamen case of - Anweisungen, dann sparst Du wieder 60%.

Habe ich soeben gemacht, diesmal bringts wirklich viel.

user profile iconalzaimar hat folgendes geschrieben:
Dann noch das Trunc(k*PrimeBits/Num) in ein MulDiv (k,PrimeBits, Num), und Du sparst nochmal 1500 ms ein.

Das kann man so nicht machen, es würde so ein falsches Ergebnis rauskommen.

Hier der aktuelle Code, mit allen Optimierung die was gebracht haben:

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:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
procedure SavePrimes2(MaxPrime: Cardinal; const FileName: string = '');
const
  STEMPEL: array[0..7of Byte = (17111317192329);
  Mods: array[0..29of Byte = (0100000200040800,
                                0160320006400000128);
var
  Primes: array[0..MAXWORD] of Byte;  // 64 KB
  i, j, k, PrimeLen, PrimeBits, Num, Num2, m, mbit, s: Cardinal;
  f: TextFile;
begin
  if FileName<>'' then begin
    AssignFile(f, FileName);
    ReWrite(f);
    WriteLn(f, '2'+#10#13+'3'+#10#13+'5');
  end;
  PrimeLen:=Length(Primes);
  PrimeBits:=PrimeLen*30;
  for k:=0 to Trunc(MaxPrime/PrimeBits) do begin
    FillChar(Primes, PrimeLen, 0);
    for i:=0 to Trunc(Sqrt((k+1)*PrimeBits)/30do
      for j:=0 to 7 do
        if not ((i=0and (j=0)) and ((Primes[i] and (1 shl j)=0or (k>0)) then begin
          s:=STEMPEL[j];
          Num:=i*30+s;
          if k=0 then
            Num2:=Num*Num
          else
            Num2:=Trunc(k*PrimeBits/Num)*Num+Num;
          mbit:=Num2 mod 30;
          m:=(Num2-mbit) div 30-k*PrimeLen;
          while m<PrimeLen do begin
            primes[m]:=Primes[m] or Mods[mbit];
            Inc(m, i);
            Inc(mbit, s);
            if mbit>29 then begin
              Dec(mbit, 30);
              Inc(m);
            end;
          end;
        end;
    if FileName<>'' then
      for i:=0 To PrimeLen-1 do
        for j:=0 to 7 do begin
          if k*PrimeBits+i*30+STEMPEL[j]>MaxPrime then
            Break;
          if not ((i=0and (j=0and (k=0)) and (Primes[i] and (1 shl j)=0then
            WriteLn(f, IntToStr(k*PrimeBits+i*30+STEMPEL[j]));
        end;
  end;
  if FileName<>'' then
    CloseFile(f);
end;


Man kann das ganze noch weiter Optimieren, hab heute aber keine Lust mehr dazu ^^

Hier der Vergleich:
bei 100 mio testzahlen:
ohne Optim.: 2060ms
mit Optim.: 724ms

bei 500 mio testzahlen:
ohne Optim.: 11525ms
mit Optim.: 4109ms

mfg
Phantom1


GTA-Place - So 21.08.05 09:00

4 Sekunden für 500 Mio Zahlen. Das ist wirklich schnell!

Ich hab die Funktion mal unter deinem Namen in die DP geschrieben. Ist schon OK, oder?


Phantom1 - So 21.08.05 11:07

user profile iconGTA-Place hat folgendes geschrieben:
4 Sekunden für 500 Mio Zahlen. Das ist wirklich schnell!

Ich hab die Funktion mal unter deinem Namen in die DP geschrieben. Ist schon OK, oder?

Jo kein Problem, allerdings ist diese Funktion schon wieder veraltet :wink: Hab die Funktion weiter optimiert. Für 500 Mio Zahlen braucht man jetzt nur noch 2,55 sekunden ^^

Hier der Code:

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:
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:
procedure SavePrimes(MaxPrime: Cardinal; const FileName: string = '');
const
  CACHE = 64*1024;
  STEMPEL: array[0..7of Byte = (17111317192329);
  MODS: array[0..29of Byte = (0100000200040800,
                                0160320006400000128);
var
  Primes: array[0..CACHE-1of Byte;
  PrimesLUT: array of Byte;
  i, j, k, PrimeLen, PrimeBits, Num, Num2, m, mbit, s: Cardinal;
  f: TextFile;
begin
  if FileName<>'' then begin
    AssignFile(f, FileName);
    ReWrite(f);
    WriteLn(f, '2'+#13#10+'3'+#13#10+'5');
  end;

  SetLength(PrimesLUT, Trunc(Sqrt(MaxPrime)/30)); // max 2184 Byte für 2^32 ;-)
  PrimesLUT[0]:=$01;
  PrimeLen:=Length(PrimesLUT);
  PrimeBits:=PrimeLen*30;
  for i:=0 to Trunc(Sqrt(PrimeBits)/30do
    for j:=0 to 7 do
      if PrimesLUT[i] and (1 shl j)=0 then begin
        s:=STEMPEL[j];
        Num:=i*30+s;
        Num2:=Num*Num;
        mbit:=Num2 mod 30;
        m:=(Num2-mbit) div 30;
        while m<PrimeLen do begin
          PrimesLUT[m]:=PrimesLUT[m] or MODS[mbit];
          Inc(m, i);
          Inc(mbit, s);
          if mbit>29 then begin
            Dec(mbit, 30);
            Inc(m);
          end;
        end;
      end;

  PrimeLen:=Length(Primes);
  PrimeBits:=PrimeLen*30;
  for k:=0 to MaxPrime div PrimeBits do begin
    FillChar(Primes[0], PrimeLen, 0);
    for i:=0 to Trunc(Sqrt((k+1)*PrimeBits)/30do
      for j:=0 to 7 do
        if PrimesLUT[i] and (1 shl j)=0 then begin
          s:=STEMPEL[j];
          Num:=i*30+s;
          if k=0 then
            Num2:=Num*Num
          else
            Num2:=Trunc(k*PrimeBits/Num)*Num+Num;
          mbit:=Num2 mod 30;
          m:=(Num2-mbit) div 30-k*PrimeLen;
          while m<PrimeLen do begin
            primes[m]:=Primes[m] or MODS[mbit];
            Inc(m, i);
            Inc(mbit, s);
            if mbit>29 then begin
              Dec(mbit, 30);
              Inc(m);
            end;
          end;
        end;
    if FileName<>'' then
      for i:=0 To PrimeLen-1 do
        for j:=0 to 7 do begin
          if k*PrimeBits+i*30+STEMPEL[j]>MaxPrime then
            Break;
          if not ((i=0and (j=0and (k=0)) and (Primes[i] and (1 shl j)=0then
            WriteLn(f, IntToStr(k*PrimeBits+i*30+STEMPEL[j]));
        end;
  end;
  if FileName<>'' then
    CloseFile(f);
end;


mfg
Phantom1


GTA-Place - So 21.08.05 11:15

Denn geh ich mal in der DP updaten... :P

EDIT: Äh... wie misst du? Ich bekomm 25 Sekunden raus.


Phantom1 - So 21.08.05 11:42

user profile iconGTA-Place hat folgendes geschrieben:
Denn geh ich mal in der DP updaten... :P

EDIT: Äh... wie misst du? Ich bekomm 25 Sekunden raus.


Speicherst du die Zahlen? oder warum dauert es bei dir so lange, mhhh...

Ich habe schnell mal was zusammengebastelt, du findest es im Anhang. Bei mir braucht dieses Programm 2930ms. (Die 2650ms die ich oben gemessen habe, wurde mit RealTime Priority gemessen).


GTA-Place - So 21.08.05 13:48

Die 2,5 Sekunden bekomm ich mit meinem Prog (mit deiner Funktion) nur bei 50.000.000 Zahlen hin.


Phantom1 - So 21.08.05 14:18

user profile iconGTA-Place hat folgendes geschrieben:
Die 2,5 Sekunden bekomm ich mit meinem Prog (mit deiner Funktion) nur bei 50.000.000 Zahlen hin.


Komisch. Wie lange braucht denn das Programm in meinem letzten Posting?

Evtl musst du das Programm an deinen L1 Cache anpassen. Wie groß beträgt dein L1/L2 Cache?

EDIT: Wenn ich 500 Mio zahlen berechne und auf Festplatte speichern lasse, dauert der gesamte vorgang 21,561 sekunden. Die Datei ist anschließend 270MB groß.

mfg
Phantom1


GTA-Place - So 21.08.05 14:51

Ah ja, ich habs mit speichern. Dann bekomm ich etwa das selbe raus.


GTA-Place - So 21.08.05 15:09

Ich hab deinen Source mal bisschen formatiert:

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:
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:
procedure SavePrimes(MaxPrime: Cardinal; const FileName: string = '');  
const  
  CACHE = 64*1024;  
  STEMPEL: array[0..7of Byte = (17111317192329);  
  MODS: array[0..29of Byte = (0100000200040800,  
                                0160320006400000128);  
var  
  Primes:            Array[0..CACHE-1of Byte;
  PrimesLUT:         Array of Byte;
  i, j, k, PrimeLen: Cardinal;
  PrimeBits, Num:    Cardinal;
  Num2, m, mbit, s:  Cardinal;
  f:                 TextFile;
begin
  if not (FileName = ''then
  begin
    AssignFile(f, FileName);
    ReWrite(f);
    WriteLn(f, '2'+#10#13+'3'+#10#13+'5');
  end;

  SetLength(PrimesLUT, Trunc(Sqrt(MaxPrime) / 30)); // max 2184 Byte für 2^32 ;-)
  PrimesLUT[0] := $01;
  PrimeLen     := Length(PrimesLUT);
  PrimeBits    := PrimeLen * 30;

  for i := 0 to Trunc(Sqrt(PrimeBits) / 30do
  begin
    for j := 0 to 7 do
    begin
      if PrimesLUT[i] AND (1 shl j) = 0 then
      begin
        s := STEMPEL[j];
        Num  := i * 30 + s;
        Num2 := Num * Num;
        mbit := Num2 mod 30;
        m    := (Num2 - mbit) div 30;

        while m < PrimeLen do
        begin
          PrimesLUT[m] := PrimesLUT[m] or MODS[mbit];
          m := m + i;
          mbit := mbit + s;
          if mbit > 29 then
          begin
            mbit := mbit - 30;
            Inc(m);
          end;  
        end;  
      end;
    end;
  end;

  PrimeLen := Length(Primes);
  PrimeBits := PrimeLen * 30;

  for k := 0 to MaxPrime div PrimeBits do
  begin
    FillChar(Primes[0], PrimeLen, 0);
    for i := 0 to Trunc(Sqrt((k + 1) * PrimeBits) / 30do
    begin
      for j := 0 to 7 do
      begin
        if PrimesLUT[i] AND (1 shl j) = 0 then
        begin
          s   := STEMPEL[j];
          Num := i * 30 + s;
          if k = 0 then
            Num2 := Num * Num
          else
            Num2 := Trunc(k * PrimeBits / Num) * Num + Num;

          mbit := Num2 mod 30;
          m    := (Num2 - mbit) div 30 - k * PrimeLen;
          while m < PrimeLen do
          begin
            primes[m] := Primes[m] or MODS[mbit];
            m    := m + i;
            mbit := mbit + s;
            if mbit > 29 then
            begin
              mbit := mbit - 30;
              inc(m);
            end;
          end;
        end;
      end;
    end;

    if not (FileName = ''then
    begin
      for i := 0 to PrimeLen - 1 do
      begin
        for j := 0 to 7 do
        begin
          if k * PrimeBits + i * 30 + STEMPEL[j] > MaxPrime then
            Break;
          if not ((i = 0AND (j = 0AND (k = 0)) AND
                 (Primes[i] AND (1 shl j) = 0then
            WriteLn(f, IntToStr(k * PrimeBits + i * 30 + STEMPEL[j]));
        end;
      end;
    end;
  end;

  if not (FileName = ''then
    CloseFile(f);
end;


Edit: Aktualisiert.


Phantom1 - So 21.08.05 15:22

Naja die Formatierung ist Geschmackssache :wink: Ich finde es so ehrlich gesagt unübersichtlicher. Zudem hab ich oben noch ein paar änderungen vorgenommen, die in deinem "formatiertem" Code noch nicht drin sind.

Momentan benötigt mein Code für 2^32-1 etwas über 24 sekunden. Jetzt können wir langsam auf 64bit-Bereich gehen *g

mfg
Phantom1


GTA-Place - So 21.08.05 16:55

Ich würde das noch so ändern

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:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
...
  for k := 0 to MaxPrime div PrimeBits do
  begin
    FillChar(Primes[0], PrimeLen, 0);
    for i := 0 to Trunc(Sqrt((k + 1) * PrimeBits) / 30do
    begin
      for j := 0 to 7 do
      begin
        if PrimesLUT[i] AND (1 shl j) = 0 then
        begin
          s   := STEMPEL[j];
          Num := i * 30 + s;
          if k = 0 then
            Num2 := Num * Num
          else
            Num2 := Trunc(k * PrimeBits / Num) * Num + Num;

          mbit := Num2 mod 30;
          m    := (Num2 - mbit) div 30 - k * PrimeLen;
          while m < PrimeLen do
          begin
            primes[m] := Primes[m] or MODS[mbit];
            m    := m + i;
            mbit := mbit + s;
            if mbit > 29 then
            begin
              mbit := mbit - 30;
              inc(m);
            end;
          end;
        end;
      end;
    end;
  end;

  if not (FileName = ''then
  begin
    for k := 0 to MaxPrime div PrimeBits do
    begin
      for i := 0 to PrimeLen - 1 do
      begin
        for j := 0 to 7 do
        begin
          if k * PrimeBits + i * 30 + STEMPEL[j] > MaxPrime then
            Break;
          if not ((i = 0AND (j = 0AND (k = 0)) AND
                 (Primes[i] AND (1 shl j) = 0then
           SList.Add(IntToStr(k * PrimeBits + i * 30 + STEMPEL[j]));
        end;
      end;
    end;

    SList.SaveToFile(FileName);
    SList.Free;
  end;
end;

So wird nicht jedesmal geprüft, ob FileName <> '' ist. (Hier verwende ich zu Testzwecken eine StringList. Ändert aber so nichts an der Funktion selber, sollte also nicht weiter stören.)


zemy - So 21.08.05 17:16

Will ich mich auch mal einbringen ;) Sind zwar nur kleinigkeiten, aber vieleicht bringts ein paar Millisekunden.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
 
mbit = Num2 mod 30
mbit = Num² mod 30
mbit = (30i + s)² mod 30
mbit = (900i² + 60is + s²) mod 30 [(a + km) mod m = a mod m]
mbit = s² % 30

Und da kann man ja noch ein Array basteln, was die Werte für s² mod 30 speichert. Tada, ne Modulooperation gespart.

und jetzt noch so was..

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
m = (Num2 - mbit) div 30
m = (Num2 - s² mod 30div 30
m = ( Num² - s² mod 30div 30
m = ( (30i + s)² - s² mod 30div 30
m = (900i² + 60is + s² - s² mod 30div 30
m = 30i² + 2is + (s² - s² mod 30div 30

Ob das jetzt günstiger ist, weiß ich nicht genau. (s² - s² mod 30div 30 könnte man aber wieder in nen Array schreiben. Bleiben noch ein paar Multiplikationen und ne Addition. Ne Division wird vermieden.

Vieleicht bringts ja was.. ein paar Millisekunden ;)

Mfg Zemy

Moderiert von user profile iconAXMD: Code- durch Delphi-Tags ersetzt.


GTA-Place - So 21.08.05 17:20

Kannst du mal genauer sagen, was du da ändern willst? Denn % und ² gibts in Delphi nicht.


zemy - So 21.08.05 17:42

Sollte blos Pseudocode werden ;)

% entspricht mod und s² entspricht s*s...


GTA-Place - So 21.08.05 17:47

Deswegen versteh ich immer noch net was du willst... :wink:


Phantom1 - So 21.08.05 18:19

Ich verstehe es schon, wolltes es früher schonmal probieren, bin aber nicht auf die formel gekommen und habs erstmal sein lassen.

Ich hab die mbitLUT (LookUpTable) mal eingebaut, geht leider nur in PrimesLUT-Schleife. Leider wurde der Code dadurch etwas langsamer, ich werd das mal genauer unter die Lupe nehmen.


BenBE - So 21.08.05 21:21

Was stellen S, I, M und Num darstellen? Kannst Du deine Herleitung bitte mal etwas erläutern?


Phantom1 - So 21.08.05 22:06

s,i,m,Num sind Variablen aus meinem Algorythmus.

Ich habe jetzt mal beide Vorschläge von Zemy eingebaut, bei 500mio zahlen dauerts jetzt nur noch 2,3 statt 2,55 sekunden.


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:
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:
procedure SavePrimes(MaxPrime: Cardinal; const FileName: string = '');
const
  CACHE = 64*1024;
  STEMPEL: array[0..7of Byte = (17111317192329);
  MODS: array[0..29of Byte = (0100000200040800,
                                0160320006400000128);
  mBitLUT: array[0..7of Byte = (119119191191);
  mLUT: array[0..7of Byte = (01459121728);
var
  Primes: array[0..CACHE-1of Byte;
  PrimesLUT: array of Byte;
  i, j, k, PrimeLen, PrimeBits, Num, Num2, m, mbit, s: Cardinal;
  f: TextFile;
begin
  if FileName<>'' then begin
    AssignFile(f, FileName);
    ReWrite(f);
    WriteLn(f, '2'+#13#10+'3'+#13#10+'5');
  end;

  SetLength(PrimesLUT, Trunc(Sqrt(MaxPrime)/30)); // max 2184 Byte für 2^32 ;-)
  PrimesLUT[0]:=$01;
  PrimeLen:=Length(PrimesLUT);
  PrimeBits:=PrimeLen*30;
  for i:=0 to Trunc(Sqrt(PrimeBits)/30do
    for j:=0 to 7 do
      if PrimesLUT[i] and (1 shl j)=0 then begin
        s:=STEMPEL[j];
        mbit:=mBitLUT[j];
        m:=30*(i*i) + 2*i*s + mLUT[j];
        while m<PrimeLen do begin
          PrimesLUT[m]:=PrimesLUT[m] or MODS[mbit];
          Inc(m, i);
          Inc(mbit, s);
          if mbit>29 then begin
            Dec(mbit, 30);
            Inc(m);
          end;
        end;
      end;

  PrimeLen:=Length(Primes);
  PrimeBits:=PrimeLen*30;
  for k:=0 to MaxPrime div PrimeBits do begin
    FillChar(Primes[0], PrimeLen, 0);
    for i:=0 to Trunc(Sqrt((k+1)*PrimeBits)/30do
      for j:=0 to 7 do
        if PrimesLUT[i] and (1 shl j)=0 then begin
          s:=STEMPEL[j];
          if k=0 then begin
            mbit:=mbitLUT[j];
            m:=30*(i*i) + 2*i*s + mLUT[j];
          end else begin
            Num:=i*30+s;
            Num2:=Trunc(k*PrimeBits/Num)*Num+Num;
            mbit:=Num2 mod 30;
            m:=(Num2-mbit) div 30-k*PrimeLen;
          end;
          while m<PrimeLen do begin
            primes[m]:=Primes[m] or MODS[mbit];
            Inc(m, i);
            Inc(mbit, s);
            if mbit>29 then begin
              Dec(mbit, 30);
              Inc(m);
            end;
          end;
        end;
    if FileName<>'' then
      for i:=0 To PrimeLen-1 do
        for j:=0 to 7 do begin
          if k*PrimeBits+i*30+STEMPEL[j]>MaxPrime then
            Break;
          if not ((i=0and (j=0and (k=0)) and (Primes[i] and (1 shl j)=0then
            WriteLn(f, IntToStr(k*PrimeBits+i*30+STEMPEL[j]));
        end;
  end;
  if FileName<>'' then
    CloseFile(f);
end;

mfg
Phantom1


zemy - Mo 22.08.05 00:32

cool.. 250ms. Hätte nicht gedacht, das es doch so viel bringt^^


GTA-Place - Mo 22.08.05 15:51

Da ich die Speicherfunktion rausgenommen habe aus der Schleife, ist nun auch das hier überflüssig:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
for j := 0 to 7 do
begin
  if k * PrimeBits + i * 30 + STEMPEL[j] > MaxPrime then
    Break;
  if not ((i = 0AND (j = 0AND (k = 0)) AND
         (Primes[i] AND (1 shl j) = 0then
    SList.Add(IntToStr(k * PrimeBits + i * 30 + STEMPEL[j]));
end;


-->


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
for j := 1 to 7 do
begin
  if k * PrimeBits + i * 30 + STEMPEL[j] > MaxPrime then
    Break;
  if not (Primes[i] AND (1 shl j) = 0then
    SList.Add(IntToStr(k * PrimeBits + i * 30 + STEMPEL[j]));
end;


Sollte das Speichern vielleicht etwas verschnellern.


Phantom1 - Mo 22.08.05 18:17

@GTA-Place: was machst du denn da? das geht so nicht. Du kannst die Speicherfunktion nicht aus der Hauptschleife entfernen. Die berechneten Primzahlen stehen immer nur für einen durchgang zur verfügung, als einfaches Beispiel: Durchgang1 berechnet alle primzahlen von 1 bis 100 und Durchgang2 berechnet alle primzahlen von 101 bis 200. Bei jedem Durchgang wir der Speicher wieder auf 0 zurückgesetzt. Zudem rate ich von StringListen ab, die fressen nur unnötig viel speicher und sind zudem auch noch langsam.


zemy - Mi 24.08.05 13:26

Wie währe es damit..

von

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
    if FileName<>'' then
      for i:=0 To PrimeLen-1 do
        for j:=0 to 7 do begin
          if k*PrimeBits+i*30+STEMPEL[j]>MaxPrime then
            Break;
          if not ((i=0and (j=0and (k=0)) and (Primes[i] and (1 shl j)=0then
            WriteLn(f, IntToStr(k*PrimeBits+i*30+STEMPEL[j]));
        end;
  end;

in

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
    if FileName<>'' then
      for i:=0 To PrimeLen-1 do
        for j:=0 to 7 do begin
          if k*PrimeBits+i*30+STEMPEL[j]>MaxPrime then
            Break;
          if (i or j or k or (Primes[i] and (1 shl j)) <> 0 then
            WriteLn(f, IntToStr(k*PrimeBits+i*30+STEMPEL[j]));
        end;
  end;


GTA-Place - Mi 24.08.05 15:28

Mh... dann würde ich das irgendwie ändern, weil so wird ja jedesmal geprüft ob Filename <> '' ist und kostet minimal Zeit bei 500.000.000 Zahlen.


BenBE - Mi 24.08.05 17:57

Bei den jetzigen Source-Schnipseln geht sowohl durch das WriteLn als auch durch das IntToStr wertvolle Zeit verloren, die man durch nutzen eines virtuellen dynamischen Arrays eingespart werden könnte:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
var
    PrimPtr: PInteger;
    PrimSize: Integer;

{...}

PrimPtr := VirtualAlloc(65536*1024);

{...}

          if (i or j or k or (Primes[i] and (1 shl j)) <> 0 then
          Begin
            PrimPtr^ := k*PrimeBits+i*30+STEMPEL[j];
            Inc(PrimPtr);
            //Check for Buffer Overrun and Reallocate Memory if current size is not enought
          end;

{...}


Damit sollte der Algo noch mal etwas schneller werden.


Phantom1 - Mi 24.08.05 21:11

user profile iconzemy hat folgendes geschrieben:
Wie währe es damit..

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
    if FileName<>'' then
      for i:=0 To PrimeLen-1 do
        for j:=0 to 7 do begin
          if k*PrimeBits+i*30+STEMPEL[j]>MaxPrime then
            Break;
          if (i or j or k or (Primes[i] and (1 shl j)) <> 0 then
            WriteLn(f, IntToStr(k*PrimeBits+i*30+STEMPEL[j]));
        end;
  end;

Geht so nicht, Primzahlen sind nur die Bits mit einer 0. Nebenbei: Die Sache mit der i,j,k überprüfung ist ja eigentlich nur weil sonst auch die Zahl 1 gespeichert werden würde, was aber kein Primzahl ist, daher diese Bedingung.

@GTA-Place: die Sache mit dem Filename<>"" kostet doch kaum Zeit, bzw kann man da auch nichts sinnvolles ändern. Dein Vorschlag mit dem not (Filename='') bringt keinen Unterschied.

@BenBE: wie gesagt wollte ich eigentlich die Speicherfunktion später verbessern... Ich hab mal den Speichervorgang beim schnellsten PrimzahlenProgramm das ich kenne getestet (ecprime). Das berechnen geht ja in ecprime wesentlich schneller als bei mir, aber das Speichern in ecprime ist verglichen mit meinen Code deutlich langsamer. Von daher kann doch meine Speicheralgo garnicht so langsam sein ^^

mfg


GTA-Place - Mi 24.08.05 21:18

user profile iconPhantom1 hat folgendes geschrieben:
Dein Vorschlag mit dem not (Filename='') bringt keinen Unterschied.

Das weiß ich auch, dass das nichts ändert. Hab aber in Errinerung mal gelesen zu haben, dass das besser als <> ist.


BenBE - Do 25.08.05 15:31

user profile iconPhantom1 hat folgendes geschrieben:
user profile iconzemy hat folgendes geschrieben:
Wie währe es damit..

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
    if FileName<>'' then
      for i:=0 To PrimeLen-1 do
        for j:=0 to 7 do begin
          if k*PrimeBits+i*30+STEMPEL[j]>MaxPrime then
            Break;
          if (i or j or k or (Primes[i] and (1 shl j)) <> 0 then
            WriteLn(f, IntToStr(k*PrimeBits+i*30+STEMPEL[j]));
        end;
  end;


user profile iconPhantom1 hat folgendes geschrieben:
Geht so nicht, Primzahlen sind nur die Bits mit einer 0. Nebenbei: Die Sache mit der i,j,k überprüfung ist ja eigentlich nur weil sonst auch die Zahl 1 gespeichert werden würde, was aber kein Primzahl ist, daher diese Bedingung.

Wenn diese Zeile nur zum filtern der Zahl eins verwendet wird könnte man durch k = 1 to ... sicherlich auch nochmal Zeit sparen, unter der Bedingung, dass man alle Primzahlen der ersten Reihe des Stempels voraussetzt und die Prüfung auf or j or k <> 0 entfernt. Oder sehe ich da was falsch?

user profile iconPhantom1 hat folgendes geschrieben:
@BenBE: wie gesagt wollte ich eigentlich die Speicherfunktion später verbessern... Ich hab mal den Speichervorgang beim schnellsten PrimzahlenProgramm das ich kenne getestet (ecprime). Das berechnen geht ja in ecprime wesentlich schneller als bei mir, aber das Speichern in ecprime ist verglichen mit meinen Code deutlich langsamer. Von daher kann doch meine Speicheralgo garnicht so langsam sein ^^

War ja auch nur eine Sache, die mir aufgefallen ist, wo noch relativ viel Potenzial drin stecken würde.

Naja, außerdem würd ich jegliche String-Operationen aus der Auswertungs-Prozedur entfernen; bremst nur unnötiger-Weise. Eine Binär-Speicherung reicht vollkommen aus ...


zemy - Fr 26.08.05 13:04

user profile iconPhantom1 hat folgendes geschrieben:
user profile iconzemy hat folgendes geschrieben:
Wie währe es damit..

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
    if FileName<>'' then
      for i:=0 To PrimeLen-1 do
        for j:=0 to 7 do begin
          if k*PrimeBits+i*30+STEMPEL[j]>MaxPrime then
            Break;
          if (i or j or k or (Primes[i] and (1 shl j)) <> 0 then
            WriteLn(f, IntToStr(k*PrimeBits+i*30+STEMPEL[j]));
        end;
  end;

Geht so nicht, Primzahlen sind nur die Bits mit einer 0. Nebenbei: Die Sache mit der i,j,k überprüfung ist ja eigentlich nur weil sonst auch die Zahl 1 gespeichert werden würde, was aber kein Primzahl ist, daher diese Bedingung.

@GTA-Place: die Sache mit dem Filename<>"" kostet doch kaum Zeit, bzw kann man da auch nichts sinnvolles ändern. Dein Vorschlag mit dem not (Filename='') bringt keinen Unterschied.

@BenBE: wie gesagt wollte ich eigentlich die Speicherfunktion später verbessern... Ich hab mal den Speichervorgang beim schnellsten PrimzahlenProgramm das ich kenne getestet (ecprime). Das berechnen geht ja in ecprime wesentlich schneller als bei mir, aber das Speichern in ecprime ist verglichen mit meinen Code deutlich langsamer. Von daher kann doch meine Speicheralgo garnicht so langsam sein ^^

mfg


mit  i or j or k werden die Zahlen doch einfach nur "geodert" (z.B. 11001 or 01101 = 11101) Mit  Primes[i] and (1 shl j) generierst du auch nur ne Zahl. Die kann ja dann auch mit geodert werden. Aber nen Fehler habe ich trotzdem gemacht. Müsste heißen ...s[i] and (1 shl j)) = 0 then


Phantom1 - Fr 26.08.05 13:47

user profile iconzemy hat folgendes geschrieben:
mit  i or j or k werden die Zahlen doch einfach nur "geodert" (z.B. 11001 or 01101 = 11101) Mit  Primes[i] and (1 shl j) generierst du auch nur ne Zahl. Die kann ja dann auch mit geodert werden. Aber nen Fehler habe ich trotzdem gemacht. Müsste heißen ...s[i] and (1 shl j)) = 0 then

Dein Code würde also so hier aussehen?:

Delphi-Quelltext
1:
(i or j or k or (Primes[i] and (1 shl j)) = 0)                    

In dem Fall wird nur die Zahl 1 gespeichert, alle anderem Primzahlen werden nicht gespeichert ^^

Die Bedingung wie ich sie gemacht habe, ist eigentlich schon optimal. Man könnte den ersten Durchgang (k=0) aus der schleife nehmen, aber dadurch wird nur der Code wesentlich länger, schneller wird es dadurch nicht.


Amateur - Fr 26.08.05 14:17

nur ma so weil ich auch nen eigenen algorithmus entwickeln will...
als was speichert ihr die zahlen wenn sie ziemlich groß werden? als integer geht ja irgendwann net mehr..
die größte primzahl is ja irgendwas mit 2^6000 -1 oder so...
sowas kann man ja gar net mehr als integervariable speichern...
welchen variablentyp nehmt ihr und bis zu welcher zahl geht der... oder kann man sowas irgendwie umgehn mit variant oder so?


Gausi - Fr 26.08.05 14:33

user profile iconAmateur hat folgendes geschrieben:
die größte primzahl is ja irgendwas mit 2^6000 -1 oder so...

"Oder so" ist richtig: :lol:
http://de.wikipedia.org/wiki/Mersenne-Primzahl: hat folgendes geschrieben:
Im GIMPS-Projekt wurde die 42. Mersenne-Primzahl entdeckt: 2 hoch 25.964.951 -1 hat 7.816.230 Stellen.

Eine größte Primzahl gibt es übrigens nicht - es gibt ja bekanntlich unendlich viele.


Amateur - Fr 26.08.05 14:48

user profile iconGausi hat folgendes geschrieben:
user profile iconAmateur hat folgendes geschrieben:
die größte primzahl is ja irgendwas mit 2^6000 -1 oder so...

"Oder so" ist richtig: :lol:
http://de.wikipedia.org/wiki/Mersenne-Primzahl: hat folgendes geschrieben:
Im GIMPS-Projekt wurde die 42. Mersenne-Primzahl entdeckt: 2 hoch 25.964.951 -1 hat 7.816.230 Stellen.

Eine größte Primzahl gibt es übrigens nicht - es gibt ja bekanntlich unendlich viele.


ja ok viell doch nen bisschen größer.. ich meinte natürlich die größte bekannte bzw. gefundene primzahl...sry.

naja aber wie will man sowas abspeichern? nen variablentyp in delphi der so ne zahl speichert is mir net bekannt...

kann man das also umgehn? denn sonst bringt son primzahl algorithmus ja eh nix... lässt man seinen pc die ganze laufen und spamt seine platte mit ner riesen textdatei voll voller primzahlen und im endeffekt wird man eh keine größere finden können da nen normaler hauspc gar net so große zahlen speichern kann...

bringt also im endeffekt nix außer dass man sagen kann ich hab nen primzahlenalgoritmus geschrieben...

oder?


F34r0fTh3D4rk - Fr 26.08.05 15:38

man kann das auf ganz viele interger64 aufteilen 8)


Amateur - Fr 26.08.05 18:20

user profile iconF34r0fTh3D4rk hat folgendes geschrieben:
man kann das auf ganz viele interger64 aufteilen 8)

ok und wie willste die dann wieder zusammenmachen? wie soll das gehn?
wenn du jetzt ne zahl hast die zu groß is willste die dan in zwei hälften teilen zum beispiel 100000 teilste in 100 und 000 oder wie? dann musste die ja wieder zusammenfügen und dazu brauchste ne variable die so große zahlen speichert und die gibts ja net.. denn in 50000*2 kann man ja net aufteilen da das mit primzahlen net geht..

also da seh ich grad 0 durch...


F34r0fTh3D4rk - Fr 26.08.05 18:50

nein du sollst die zwei zahlen auch nicht zusammenfügen, das ginge auch garnicht, sonst würde man sich die mühe ja gleich sparen :lol:

Normalerweise würde man die zahl in primzahlen zerlegen, was hier wohl nichts bringt, um bytes zu sparen könnte man aber auch noch das zahlensystem wechseln, ich weiß nicht ob da was zu holen ist, zb das hexasystem, dort wäre dann aber ein zeichen 1 byte groß, die zahl wäre aber trotzdem größer. bin mir nicht sicher ob das was bringen könnte, hab da nicht so die ahnung von :lol:

aufteilen auf mehrere int64 sollte aber irgendwie machbar sein, also zb eine zahl auf 2 int64 aufspalten, dann hätte man die doppelte menge an bytes. ich werde mal schauen.


GTA-Place - Fr 26.08.05 18:54

Du kannst die beiden dann in einen String umwandeln und dann wieder zusammensetzten :)


AXMD - Fr 26.08.05 18:54

user profile iconGTA-Place hat folgendes geschrieben:
Du kannst die beiden dann in einen String umwandeln und dann wieder zusammensetzten :)


Ineffetkiver gings kaum ^^

AXMD


F34r0fTh3D4rk - Fr 26.08.05 18:57

hab oben bissl was editiert 8)

Delphi-Quelltext
1:
2:
3:
4:
5:
type
  Int128 = record
    Int1: Int64;
    Int2: Int64
end;

die frage : wie damit rechnen ?


negaH - Fr 26.08.05 22:19

@Phantom1:

so es is Wochenende und ich habe mir mal deinen Algo genauer angeschaut.
Vorweg: unsere beiden Siebe unterscheiden sich an wesentlichen Punkten, sind also nicht identisch, wenn auch die mathematische Grundlage identisch scheint.

Bevor wir aber weiter versuchen deinen Algo zu optimieren, müssen wir ihr erstmal korrekt lauffähig bekommen.
D.h. als erstes habe ich überprüft ob dein Algo korrekt arbeitet. Leider ist dies nicht der Fall.

Mein Testcode dazu ist:


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:
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:
function SavePrimes(MaxPrime: Cardinal): Cardinal;
const
  CACHE = 64*1024;
  STEMPEL: array[0..7of Byte = (17111317192329);
  MODS: array[0..29of Byte = (0100000200040800,
                                0160320006400000128);
var
  Primes, PrimesLUT: array of Byte;
  Count,FailsIndex,FailsNonPrime,P,Q,Index, i, j, k, PrimeLen, PrimeBits, Num, Num2, m, mbit, s: Cardinal;
  QisPrime: Boolean;
  f: TextFile;
begin
  Index := 1;
  FailsIndex := 0;
  FailsNonPrime := 0;
  Count := 0;

  SetLength(PrimesLUT, Trunc(Sqrt(MaxPrime)/30)); // max 2184 Byte für 2^32 ;-)
  PrimesLUT[0]:=$01;
  PrimeLen:=Length(PrimesLUT);
  PrimeBits:=PrimeLen*30;
  for i:=0 to Trunc(Sqrt(PrimeBits)/30do
    for j:=0 to 7 do
      if PrimesLUT[i] and (1 shl j)=0 then
      begin
        s:=STEMPEL[j];
        Num:=i*30+s;
        Num2:=Num*Num;
        mbit:=Num2 mod 30;
        m:=(Num2-mbit) div 30;
        while m<PrimeLen do
        begin
          PrimesLUT[m]:=PrimesLUT[m] or MODS[mbit];
          Inc(m, i);
          Inc(mbit, s);
          if mbit>29 then
          begin
            Dec(mbit, 30);
            Inc(m);
          end;
        end;
      end;

  SetLength(Primes, CACHE);
  PrimeLen:=Length(Primes);
  PrimeBits:=PrimeLen*30;
  for k:=0 to MaxPrime div PrimeBits do
  begin
    FillChar(Primes[0], PrimeLen, 0);
    for i:=0 to Trunc(Sqrt((k+1)*PrimeBits)/30do
      for j:=0 to 7 do
        if PrimesLUT[i] and (1 shl j)=0 then
        begin
          s:=STEMPEL[j];
          Num:=i*30+s;
          if k=0 then Num2:=Num*Num
            else Num2:=Trunc(k*PrimeBits/Num)*Num+Num;
          mbit:=Num2 mod 30;
          m:=(Num2-mbit) div 30-k*PrimeLen;
          while m<PrimeLen do
          begin
            primes[m]:=Primes[m] or MODS[mbit];
            Inc(m, i);
            Inc(mbit, s);
            if mbit>29 then
            begin
              Dec(mbit, 30);
              Inc(m);
            end;
          end;
        end;


     for I := 0 to PrimeLen-1 do
       for J := 0 to 7 do
       begin
         Q := k * PrimeBits + I * 30 + Stempel[J];
         if Q > MaxPrime then Break;
         if not ((I = 0and (J = 0and (K = 0)) and (Primes[I] and (1 shl J) = 0then
         begin
           P := Prime.Primes[Index];
           if Q <> P then
           begin
             QisPrime := Prime.IsPrime(Q);
             Inc(FailsIndex);
             if not QisPrime then Inc(FailsNonPrime);
             WriteLn('Fails: ', FailsIndex:5'/', FailsNonPrime:5,  ', Index: ', Index:12', Q: ', Q:12', ',QisPrime:6', P: ', P:12', ', Prime.IsPrime(P):6);
             while (Q > P) and (Index < Prime.Primes.MaxIndex) do
             begin
               Inc(Index);
               P := Prime.Primes[Index];
               if P <> Q then
               begin
                 Inc(FailsIndex);
                 WriteLn('Fails: ', FailsIndex:5'/', FailsNonPrime:5,  ', Index: ', Index:12', Q: ', Q:12', ',QisPrime:6', P: ', P:12', ', Prime.IsPrime(P):6);
               end;
             end;
             WriteLn;
             if QIsPrime then Inc(Index);
           end else Inc(Index);
           Inc(Count);
         end;
       end;
  end;
  Result := count;
end;


Wie du siehtst habe ich das ganze Dateihandling rausgenommen, da es irrelevant ist. Dafür habe ich aber die Anzahl der Primzahlen integriert und später dann mein eigenes Sieb parallel arbeitend integriert, um die Resultate direkt vergleichen zu können.

Ein Ausschnitt des obigen Algo. in meiner Console sieht dann so aus:


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:
Fails:     1/    0, Index:            1, Q:            7,   TRUE, P:            2,   TRUE
Fails:     2/    0, Index:            2, Q:            7,   TRUE, P:            3,   TRUE
Fails:     3/    0, Index:            3, Q:            7,   TRUE, P:            5,   TRUE

Fails:     4/    1, Index:    203233127, Q:   4293918781,  FALSE, P:   4293918787,   TRUE
Fails:     5/    2, Index:    203233128, Q:   4293918791,  FALSE, P:   4293918793,   TRUE
Fails:     6/    3, Index:    203233131, Q:   4293918877,  FALSE, P:   4293918883,   TRUE
Fails:     7/    4, Index:    203233132, Q:   4293918887,  FALSE, P:   4293918907,   TRUE
Fails:     8/    5, Index:    203233133, Q:   4293918913,  FALSE, P:   4293918953,   TRUE
Fails:     9/    6, Index:    203233133, Q:   4293918947,  FALSE, P:   4293918953,   TRUE

....

Fails: 38283/38280, Index:    203280214, Q:   4294967101,  FALSE, P:   4294967111,   TRUE
Fails: 38284/38281, Index:    203280214, Q:   4294967107,  FALSE, P:   4294967111,   TRUE
Fails: 38285/38282, Index:    203280219, Q:   4294967213,  FALSE, P:   4294967231,   TRUE
Fails: 38286/38283, Index:    203280220, Q:   4294967273,  FALSE, P:   4294967279,   TRUE

....

Fails: 38287/38284, Index:    203280222, Q:           15,  FALSE, P:            0,  FALSE
Fails: 38288/38285, Index:    203280222, Q:           21,  FALSE, P:            0,  FALSE
Fails: 38289/38285, Index:    203280222, Q:           37,   TRUE, P:            0,  FALSE
Fails: 38290/38285, Index:    203280223, Q:           61,   TRUE, P:            0,  FALSE
Fails: 38291/38286, Index:    203280224, Q:           75,  FALSE, P:            0,  FALSE
Fails: 38292/38287, Index:    203280224, Q:           81,  FALSE, P:            0,  FALSE

....

Fails: 113112/105327, Index:    203288004, Q:       917403,  FALSE, P:            0,  FALSE
Fails: 113113/105328, Index:    203288004, Q:       917425,  FALSE, P:            0,  FALSE
Fails: 113114/105329, Index:    203288004, Q:       917433,  FALSE, P:            0,  FALSE
Fails: 113115/105330, Index:    203288004, Q:       917463,  FALSE, P:            0,  FALSE
Fails: 113116/105331, Index:    203288004, Q:       917467,  FALSE, P:            0,  FALSE
Fails: 113117/105332, Index:    203288004, Q:       917497,  FALSE, P:            0,  FALSE


Aufruf: mit SavePrimes($FFFFFFFB) -> $FFFFFFFB = 4294967291 ist höchste Primzahl < 2^32 mit Primzahlindex 203280221.

Fails: gibt ab wieviele Fehlresultat der Algo macht -> Anzahl Indexfehler/Anzahl fehlerhafter Primzahlen (sind also zusammengesetzte Zahlen die dein Algo als Primzahlen ausgibt).
Index: der Index der Primzahl in der Primzahltabelle.
Q: die Zahl die dein Algo als Primzahl ausgibt, danach FALSE/TRUE je nachdem ob Q tatsächlich eine Primzahl ist.
P: die Primzahl die mein Algo. zum Index berechnet, FALSE/TRUE jenachdem ob P eine Primzahl ist


Die ersten 3 Zeilen können wir ignorieren, da dein Algo keine direkte Berechnung zu den ersten 3 Primzahlen bietet.

Ab Primzahlindex 203233127 erzeugt dein Algo nun Zahlen die keine Primzahlen sind, er arbeitet ab da falsch.
Das geht bis Primzahlindex 203280220 was exakt der Moment ist wo der Algo im Grunde terminieren müsste.

Bis zu diesem Bereich hat dein Algo also 38287 Zahlen als Primzahlen erzeugt die aber zusammengesetzte Zahlen sind.
Der letzte Block ab dem P= 0 ist, hätte dein Algo bei der Zeile


Delphi-Quelltext
1:
2:
3:
4:
5:
     for I := 0 to PrimeLen-1 do
       for J := 0 to 7 do
       begin
         Q := k * PrimeBits + I * 30 + Stempel[J];
         if Q > MaxPrime then Break;                    // <---


schon längst terminieren müssen. Er rechnet aber weiter da wie man sieht Q nun < $FFFFFFFB ist, sprich ein Integer Überlauf dazu führt das der Algo noch mehr falsche Zahlen berechnet. Da er aber nicht terminiert beendet sich der Algo erst mit der äußersten Schleife k und erzeugt somit 113117 falsche Antworten.


Nun zur Performance:

ich habe mit nachfolgendem Source getestet. Auch hier wieder die Dateioperationen und Stringkonvertierungen entfernt, dafür aber unterschieden ob man nur die Anzahl der Primzahlen oder auch die Primzahlen wertmäßig exakt berechnen möchte. Diese Unterscheidung ist wichtig beim direkten Vergleich der unterschiedlichen Verfahren.


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:
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:
function SavePrimes1(MaxPrime: Cardinal; CalcPrimes: Boolean): Cardinal;
const
  CACHE = 64*1024;
  STEMPEL: array[0..7of Byte = (17111317192329);
  MODS: array[0..29of Byte = (0100000200040800,
                                0160320006400000128);
var
  Primes, PrimesLUT: array of Byte;
  Count, i, j, k, PrimeLen, PrimeBits, Num, Num2, m, mbit, s: Cardinal;
begin

  SetLength(PrimesLUT, Trunc(Sqrt(MaxPrime)/30)); // max 2184 Byte für 2^32 ;-)
  PrimesLUT[0]:=$01;
  PrimeLen:=Length(PrimesLUT);
  PrimeBits:=PrimeLen*30;
  for i:=0 to Trunc(Sqrt(PrimeBits)/30do
    for j:=0 to 7 do
      if PrimesLUT[i] and (1 shl j)=0 then
      begin
        s:=STEMPEL[j];
        Num:=i*30+s;
        Num2:=Num*Num;
        mbit:=Num2 mod 30;
        m:=(Num2-mbit) div 30;
        while m<PrimeLen do
        begin
          PrimesLUT[m]:=PrimesLUT[m] or MODS[mbit];
          Inc(m, i);
          Inc(mbit, s);
          if mbit>29 then
          begin
            Dec(mbit, 30);
            Inc(m);
          end;
        end;
      end;

  SetLength(Primes, CACHE);
  PrimeLen:=Length(Primes);
  PrimeBits:=PrimeLen*30;
  for k:=0 to MaxPrime div PrimeBits do
  begin
    FillChar(Primes[0], PrimeLen, 0);
    for i:=0 to Trunc(Sqrt((k+1)*PrimeBits)/30do
      for j:=0 to 7 do
        if PrimesLUT[i] and (1 shl j)=0 then
        begin
          s:=STEMPEL[j];
          Num:=i*30+s;
          if k=0 then Num2:=Num*Num
            else Num2:=Trunc(k*PrimeBits/Num)*Num+Num;
          mbit:=Num2 mod 30;
          m:=(Num2-mbit) div 30-k*PrimeLen;
          while m<PrimeLen do
          begin
            primes[m]:=Primes[m] or MODS[mbit];
            Inc(m, i);
            Inc(mbit, s);
            if mbit>29 then
            begin
              Dec(mbit, 30);
              Inc(m);
            end;
          end;
        end;

     if CalcPrimes then
       for I := 0 to PrimeLen-1 do
         for J := 0 to 7 do
         begin
           if (k * PrimeBits + I * 30 + Stempel[J]) > MaxPrime then Break;
           if not ((I = 0and (J = 0and (K = 0)) and (Primes[I] and (1 shl J) = 0then
             Inc(Count);
         end;
  end;
  Result := count;
end;

procedure TestPrimes;
var
  I,P: Cardinal;
begin
  StartTimer;
  Primes.IndexOf($7FFFFFFF);
  Writeln(Secs:10:2' sec');

  StartTimer;
  SavePrimes1($7FFFFFFF, False);
  Writeln(Secs:10:2' sec');

  I := 1;
  StartTimer;
  repeat
    P := Primes[I];
    Inc(I);
  until P >= $7FFFFFFF;
  Writeln(Secs:10:2' sec');


  StartTimer;
  SavePrimes1($7FFFFFFF, True);
  Writeln(Secs:10:2' sec');
end;


Laufzeiten:


Quelltext
1:
2:
3:
4:
5:
Primes.IndexOf($7FFFFFFF)         =  12.74 sec
SavePrimes($7FFFFFFF, False)      =  51.38 sec

loop Primes[i] until >= $7FFFFFFF =  30.64 sec
SavePrimes($7FFFFFFF, True)       =  67.98 sec


auf einem P4 1.5GHz 512Mb und Delphi 5.

Die gravierenden Performanceunterschiede zum AMD sind für mich nur durch die Anwendung der Opcodes BTS/BTC in meinen Bitarray[] Funktionen erklärbar. AMD Prozessoren sind mit diesen Operationen wesentlich langsammer als Pentium CPU's. Für AMD's müsste man diese Opcodes durch indizierte Array[] Zugriff + AND/OR Bitmasken per SHL/SHR ersetzen.

Alledings, als erstes muß man den Algorithmus sauber lauffähig bekommen, denn Speed nutzt nur dann etwas wenn die berechneten Resultate auch wirklich mathematisch korrekt sind.

Gruß Hagen


Amateur - Fr 26.08.05 22:50

user profile iconF34r0fTh3D4rk hat folgendes geschrieben:
hab oben bissl was editiert 8)

Delphi-Quelltext
1:
2:
3:
4:
5:
type
  Int128 = record
    Int1: Int64;
    Int2: Int64
end;

die frage : wie damit rechnen ?


tja wie damit rechnen? genau das is nämlich das problem... der ansatz is sicher net schlecht aber bringe ntuts aus meiner sicht nix denn um ne vollständige zahl die man auf teilbarkeit überprüfen kann zu bekommen, müsste man ja sowas schreiben:

Delphi-Quelltext
1:
2:
3:
var endzahl:string; endzahl2:integer;
endzahl:=int128.int1+int128.int2;
endzahl2:=strtoint(endzahl);


und diese endzahl2 kann man auf teilbarkeit prüfen, d.h. ob es ne primzahl is oder net...
so aber diese endzahl2 kann ja kein integer sein und kein int64 sondern muss von nem größeren variablentyp sein...

und da fängt das prob von vorne an, woher nehm ich son variablentyp...?

ich find bevor man weiter an optimierungen für die algorithmen feilt, die ja sowieso mit 2,5 sekunden für 500k zahlen schnell genug sind im moment sollte man erstma diese frage klären...
denn sonst hat das ganze ja keinen sinn...

wär genauso als wollte man das best getunte auto bekommen, hat aber nur nen vw käfer der zwar superschnell läuft aber trotzdem nie der beste wird... da kann man noch soviel geld reinstecken

you know?!?


Amateur - Sa 27.08.05 00:11

ok damit das hier net völlig vom thema abweicht hab ich hier nen extra thread eröffnet um die problematik der variablengröße zu diskutieren und lösungen zu finden:

http://www.delphi-forum.de/viewtopic.php?p=286547#286547

es sollten alle mal vorbeischauen und lösungen posten falls sie welche kennen...

und hier in dem thread heißt es back 2 topic!!! optimiert den algorithmus weiter...

und baut ne lösung für größere zahlen ein falls es eine im anderen thread gibt...


GTA-Place - Sa 27.08.05 09:00

Hab dir schon eine Lösung gepostet. Nennt sich "HugeInt" und speichert so viele Zahlen, wie der Speicher zulässt.


alzaimar - So 28.08.05 19:10

Falls es jemanden interessiert: Ich den 'Sieve of Atkins' aus der PrimeGen-Library in Delphi übersetzt. Die Version findet alle Primzahlen bis 500.000.000 in 850ms, gemessen auf einem Pentium 4 M 1.5 Ghz.

Im Anhang: Kleines Testprogramm und Verfahren, einfach mal nach "PrimeGen" googeln.


Horst_H - Mi 07.09.05 14:36

Hallo,

ich bin bei diesem Programm etwas ueber die Anzahl der Primzahlen erstaunt, da diese meiner Meinung nach zu gross ist.
800 Mhz Duron:

1: Time = 2232
2: Time = 2448
3: Time = 2458
4: Time = 2434
5: Time = 2365
6: Time = 2315
7: Time = 2240
8: Time = 2598
9: Time = 2773
10: Time = 2875
29595801 primes up to 500660190 in 2474 tics

Mein Programm von http://www.softgames.de/forum/viewtopic.php?t=20789&start=30 für Turbo Pascal stimmt mit den Daten von http://did.mat.uni-bayreuth.de/~wn/ss_01/beller/Seminar/HTML/pz5.htm ueberein.


ERATOSTHENES SIEB bis: 1000000000

Es sind 50847534 Primzahlen bis 1000000000

Es dauerte 1835 100.stel Sekunden
Fertig mit Zahl<Enter>
Aber ich erhalte viel weniger Primzahlen.
ERATOSTHENES SIEB bis: 500660190

Es sind 26388846 Primzahlen bis 500660190

Es dauerte 967 100.stel Sekunden
Fertig mit Zahl<Enter>

29595801 primes up to 500660190 in 2474 tics
Es sind 26388846 Primzahlen bis 500660190

Da liegen 3 Mio Zahlen dazwischen (mehr als 10%)!

Da bedarf es einiger Korrekturen.

Gruss Horst

EDIT Softgames hat sich verkürzt.


Phantom1 - Mi 07.09.05 17:24

Das was Horst_H sagt stimmt, ich habe das eben mal mit dem originalen ecprime überprüft -> Es sind 26388846 Primzahlen bis 500660190

mfg


Horst_H - Do 08.09.05 09:15

Hallo,

falls Du es gesehen hast, gibt es ein Wiederholungsfeld, indem alle Vielfachen von 2,3,5,7,11,13 schon ausgestrichen sind
Den Stempel gibt es auch hier [http://176.198.124.87/devalco/sieb_des_Ulam.php]
Ältere Version
http://www.qsl.net/w2gl/blackkey.html

Gruss Horst
P.S.:
Ich habe eben mal
http://www.delphi-forum.de/download.php?id=1201
PrimFinder.zip
.. Bei mir(=Phantom1) braucht dieses Programm 2930ms....
ausgeführt und der braucht auch 8,07 sec, ist also nur 19% schneller als meine Uraltversion fuer Turbo Pascal.
Ich hatte vermutet, die Kompression der Daten von 30 Byte auf 1 byte haette groessere Auswirkungen.

EDIT:
Anbei ein Programm, was qsl.net in etwa nachahmte, aber einen kompletten Bereich abklappert, statt kurze, cache-freundliche Abschnitte.
Es funktioniert bis 82e9 bei Belegung von fast 2 Gbyte ( 1300 Sekunden) , unter Auslassung aller Vielfacher von 2,3,5,7,11,13.
Wenn man 17 auch noch weglässt funktioniert es bis 86e9 wird aber noch langsamer.Da nichts mehr ( Zahlen-feld) in den Level I Cache passt

Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
    2   3   5   7  11  13
Zahlen    :60060 Byte
DiffFeld  :5760 Byte
MulFeld   :184 Byte
SuchFeld  :575424580 Byte
Maxzahl   :24000000190
 cMaxPos 799200
MaxPos 24000000000
MaxPos 23999999999 
MaxPos 4603396603 cMaxPos 23999976000
Anzahl Streichungen 6236659378

Zeit in Sekunden 276.952000
Bis 24000000000 Sind es 1050186367 Primzahlen

real  4m37.715s
user  4m36.377s
sys  0m0.343s

Edit..: devalco ist jetzt anders verlinkt


alzaimar - Do 08.09.05 21:17

Mann, seit ihr pingelig! Die paar Primzahlen extra, was wollt ihr überhaupt? Die sind umsonst! Persönliche Zugabe! :dunce:
Im Ernst: In der inneren Schleife ist das "inc (k,q)" beim Optimieren eine Zeile hochgerutscht. Jetzt stimmts.


Phantom1 - Do 08.09.05 21:33

user profile iconalzaimar hat folgendes geschrieben:
Mann, seit ihr pingelig! Die paar Primzahlen extra, was wollt ihr überhaupt? Die sind umsonst! Persönliche Zugabe! :dunce:
Im Ernst: In der inneren Schleife ist das "inc (k,q)" beim Optimieren eine Zeile hochgerutscht. Jetzt stimmts.


Durch diese "Kleinigkeit" braucht der Code jetzt 1312ms anstatt 726ms :shock:

mfg


alzaimar - Do 08.09.05 21:35

Er rechnet doch auch die doppelte Anzahl von Primzahlen aus, oder?


Horst_H - Fr 09.09.05 09:01

Hallo,

jau , und es läuft auch in nicht einmal der doppelten Zeit bei mir (ca. 4,1 sec). :-)
Jetzt fehlt nur noch eine Kleinigkeit, um bei der richtigen Zahl zu stoppen und nicht so merkwürdig krummen Zahlen.

Gruss Horst


alzaimar - Fr 09.09.05 09:47

Hi Horst,

da fehlt noch Einiges. Ich wollte eigentlich nur mal so eine Anregung geben. negaH meinte (zu Recht), es gäbe außer seiner Implementierung keine andere in Delphi. Da es auch um Performance ging, dachte ich mir, ich schnapp mir mal den PrimeGen. Und siehe da: Das richtige Verfahren ist wichtig!

Da jedoch negaH und Phantom1 (Du vermutlich auch) die Primzahleggschbedde sin, halte ich mich da raus. Es bringt nix, wenn ich mit meinen 3% Wissen über Primzahlen da mitmische...

Ich fände es unpässlich, den Primgen zu nehmen, in Delphi abzutippen und dann irgendwie meinen Namen damit in Verbindung zu bringen. Wie wäre es, wenn Du Dir den Sourcecode von PrimeGen schnappst (googel nach 'Bernstein PrimeGen') und in Delphi trommelst. Das ist ja alles Hausmannskost, man muss ja nix mehr selbst entwickeln.

Danke trotzdem für die Fehlererkennung und die Anmerkungen!

Ach, Du müsstest eigentlich nur in der Funktion "Count" an der richtigen Stelle rausspringen, so schwer ist das nicht. Wenn man das Verfahren kapiert hat. Hab ich aber nicht. Die "merkwürdig krummen Zahlen" liegen daran, das immer so ein Block knapp unter 1Mio Zahlen durchrechnet, da kommt zu Schluss eben sowas raus...

Gruss aus Berlin


Delete - Fr 30.09.05 15:00

Bin mir bewußt, dass dieses Topic bereits lange überholt ist...
Dennoch mein Senf dazu für alle, die mal einen Primzahltest programmieren wollen...
Hatte in letzter Zeit einige Vorlesungen über dieses Thema - in Mathematikprogrammen (Matlab, Derive, MathCat, Maple, ...) wird das so gehandhabt, daß bis zu einer gewissen Zahl (z.B. 1024=2^10, 1048576=2^20) alle kleineren Primzahlen verwendet, um zu schauen, ob das zu testende x ein Vielfaches davon ist.
Ist dies nicht der Fall, werden NICHT alle weiteren Primzahlen (und schon gar nicht alle ungeraden kleineren Zahlen) herangezogen, sondern es werden Primzahltests (da gibt es einige - bei Interesse einfach googlen) durchgeführt. Dabei werden zufällig Zahlen generiert, mit denen man dann einiges überprüft - z.B. ob Potenzen von x bestimmte Bedingungen erfüllen.
Durch jede solche Zahl, bei der x nicht als Nicht-Primzahl erkannt wird, halbiert sich die Wahrscheinlichkeit, daß x keine Primzahl ist. Man führt dann also z.B. mit 20 Zahlen diesen Test durch und kann dann sagen: x ist mit Wahrscheinlichkeit >99.9999 keine Primzahl.
Grüße
Stephan


alzaimar - Fr 30.09.05 15:14

Und so schliesst sich der Kreis
http://www.delphipraxis.net/topic63109,0,asc,15.html


Delete - Mo 03.10.05 18:46

Für alle, die ihre Verfahren testen wollen - hier ein paar meiner Lieblingsprimzahlen:

1248168421 - Man beachte, daß das 2-er Potenzen sind: 1-2-4-8-16-8-4-2-1
1111111111111111111 - sind 19 1-er
11111111111111111111111 - sind 23 1-er
12345678910987654321 - Erklärt sich von selbst: 1-2-3-4-5-6-7-8-9-10-9-8-7-6-5-4-3-2-1
19841407 - Mein Geburtsdatum, YYYYDDMM - 1984-14-07


Viel Spaß beim Testen ;)


-delphin- - Fr 07.10.05 16:22

Also mit dem Sieb des Erathostenes habe ich 1.000.000 Zahlen in 3:27 Minuten


GTA-Place - Fr 07.10.05 17:09

Dann haste irgendwas falsch gemacht, denn ich brauche für 200.000.000 Zahlen (200x mehr) nur 7,44 Sekunden.


Horst_H - Fr 07.10.05 17:17

Hallo,

bitte etwas mehr lesen sonst dreht man sich im Kreis.
alzaimer s Sieb nach Atkins ist erheblich schneller. Ca. 4 sec fuer Primzahlen bis 1e9 , mein Programm fuer Turbo pascal 18.75 sec. Das sind 50,8 Mio Zahlen.

Gruss Horst
P.S.: 800 Mhz Duron.


GTA-Place - Fr 07.10.05 17:22

Ich hab ja auch von meiner "Sieb d. E."-Funktion geredet.


-delphin- - Fr 07.10.05 23:28

zeigt er dir die auch alle an? @GTA


GTA-Place - Sa 08.10.05 08:30

Sieht so aus, ja.


Delete - Mi 12.10.05 18:12

Hier gabs mal die Diskussion darüber, wie viele Primzahlen es gibt:
Für eine Zahl 'n' sei p(n) die Anzahl der Primzahlen, die kleiner oder gleich 'n' sind.
Dann gilt - wens interessiert, bewiesen von Tchebyscheff (~1850) - folgende Aussage:
0.922 < (n/ln(n)) / p(n) < 1.106
Das heißt:

n
----
ln(n)

weicht von der Anzahl der Primzahlen, die kleiner als n sind, um weniger als 10% ab - umso größer die Zahl n ist, desto genauer wird übrigens die Näherung :)
zB. gibt es unter n=10^6 in etwa 72 382 Primzahlen (+/- 10%, dh. zwischen 66 736 und 80 021)
und das bereits erwähnte Beispiel mit n=500 660 190: Da gibts zwischen 23 044 211 und 27 631 808Primzahlen - dh. die gepostete 29 595 801 kann nicht ganz stimmen, die 26 388 846 hingegen schon