Entwickler-Ecke

Algorithmen, Optimierung und Assembler - "Programm schneller machen"


luckyluc - Di 13.01.09 16:13
Titel: "Programm schneller machen"
Hallo
ich habe ein Programm welches wenn man eine Zahl eingibt, bis zu dieser Zahl alle Primzahlen ausgibt.
Nun nöchte ich diese Programm optimierend.h so gestalten dass das programm die primzahlen noch schneller findet. außer dass man nur bis zur wurzel der zu prüfenden zahl, den teiler hochzählen muss fällt mir nicht anderes ein.
Vielleicht habt ihr noch eine Idee?
Hier folgen zwei Quellcodes (sind im Grunde genommen das gleich, die for schleife ist jedoch schneller als die repeat)

1)

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:
var
  Form1: TForm1;
  i,teiler, eing_zahl,zeile: Integer;
  primzahl: Boolean;
  
implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
zeile:= 0;
eing_zahl:= StrToInt (Edit1.Text);


for i := 4 to eing_zahl do
begin
  primzahl := true;

  for teiler := 2 to Trunc(sqrt(i)) do
   if i mod teiler = 0 then primzahl:= false; 

   if primzahl = true then
   begin
   inc (zeile);
    StringGrid1.Cells[0,zeile] := IntToStr(i);
    StringGrid1.RowCount := StringGrid1.RowCount+1;
    end;

end;//for
end;


2)

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:
var
  Form1: TForm1;
  i,teiler, eing_zahl,zeile: Integer;
  primzahl: Boolean;
  


implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
zeile:= 0;
eing_zahl:= StrToInt (Edit1.Text);

for i := 4 to eing_zahl do
begin
  primzahl := true;

 
   teiler:= 2;
   repeat
   if i mod teiler = 0 then primzahl := false;
   inc (teiler);
   until teiler = i-1;

   if primzahl = true then
   begin
   inc (zeile);
    StringGrid1.Cells[0,zeile] := IntToStr(i);
    StringGrid1.RowCount := StringGrid1.RowCount+1;
    end;

end;//for
end;



Danke !


Moderiert von user profile iconNarses: Topic aus Sonstiges (Delphi) verschoben am Di 13.01.2009 um 15:16


Nersgatt - Di 13.01.09 16:47

Schau mal hier:
http://de.wikipedia.org/wiki/Primzahltest
Du hast bisher die Probedivision umgesetzt.


Jelly - Di 13.01.09 16:52

Merke dir jede gefundene Primzahl in einem Array, und prüfe alle weiteren Zahlen nur auf Divisionen durch bereits gefundene Primzahlen.

Bsp: Du hast 2, 3, 5, 7, 11, 13 bereits gefunden
Du testest gern 143
Nimm die Wurzel: = 11,96... Prüfe als Division aller Primzahlen bis 12. d.h. es reicht aus eine Test durch 2, 3, 5, 7, 11 und 13 durchzuführen.

Ergibt eine der Divisionen 0, so ist die Zahl 143 KEINE Primzahl.

Ausserdem: Es hilft die Schleife abzubrechen, wenn einmal eine Division 0 ergibt. Du durchläufst das Testen jedoch noch weiter. Breche ab und teste die nächste Zahl.

Ums noch schneller zu machen. Vor vorneweg ist schon mal jede 2. Zahl keine Primzahl. Also alle geraden Zahlen brauchst du erst gar nicht testen (ausser 2).


jfheins - Di 13.01.09 17:28

user profile iconJelly hat folgendes geschrieben Zum zitierten Posting springen:
Merke dir jede gefundene Primzahl in einem Array, und prüfe alle weiteren Zahlen nur auf Divisionen durch bereits gefundene Primzahlen.

Bsp: Du hast 2, 3, 5, 7, 11, 13 bereits gefunden
Du testest gern 143
Nimm die Wurzel: = 11,96... Prüfe als Division aller Primzahlen bis 12. d.h. es reicht aus eine Test durch 2, 3, 5, 7, 11 und 13 durchzuführen.

Ergibt eine der Divisionen 0, so ist die Zahl 143 KEINE Primzahl.

Ausserdem: Es hilft die Schleife abzubrechen, wenn einmal eine Division 0 ergibt. Du durchläufst das Testen jedoch noch weiter. Breche ab und teste die nächste Zahl.

Ums noch schneller zu machen. Vor vorneweg ist schon mal jede 2. Zahl keine Primzahl. Also alle geraden Zahlen brauchst du erst gar nicht testen (ausser 2).

So ähnlich funktioniert das Sieb des Eratosthenes ;)

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:
var 
  Form1: TForm1; 
  
implementation 


{$R *.dfm} 

procedure TForm1.Button1Click(Sender: TObject); 
var
  eing_zahl, zahl, i, limit: Integer; 
  zahlen: Array of Boolean; 
begin 

eing_zahl:= StrToInt (Edit1.Text); 

limit := Trunc(sqrt(eing_zahl))

setlength(zahlen, eing_zahl + 1);

zahlen[0] := false;
zahlen[1] := false; // 0 & 1 per Def. nicht prim
zahlen[2] := true; // 2 per Def. prim

for i := 3 to eing_zahl do 
  zahlen[i] := (i mod 2) = 1;

zahl := 2;

while zahl <= limit do
begin
   inc(zahl);

   if not zahlen[zahl] then // Wenn keine Primzahl überspringen
      continue;

   i := zahl * 2;
   while i <= eing_zahl do
   begin
     zahlen[i] := false; // Vielfache einer Primzahlen sind nicht prim
     i := i + zahl;
   end;
end

for i := 3 to eing_zahl do 
  if zahlen[i] then
    Memo1.Lines.Add(inttostr(i) + ' ist prim!');

end;


Am Ende ist zahlen[x] wahr für jede Primzahl und falsch für jede nicht-Primzahl im Bereich ;)


Horst_H - Di 13.01.09 18:21

Hallo,

Gab es hier alles schon, sind aber viele Beiträge :
http://www.delphi-forum.de/viewtopic.php?t=33013&highlight=contest+primzahlen

Gruß Horst


platzwart - Di 13.01.09 20:32

Primzahlen (>3) sind immer in der Form von 6n+/-1


Boldar - Di 13.01.09 21:44

Wirklich? Ist das bewiesen?


Hidden - Di 13.01.09 21:52

Hi :)

Wenn du zu 6n 3 addierst/subtrahierst, kommst du auf eine durch drei teilbare zahl => nicht prim

+- 2 => durch 2 teilbar

und noch +-0 => durch 3 und 2. Es verbleiben also nurnoch +-1.

aber diese Einschränkung kann man doch beliebig weiterspinnen, indem man stets die Vielfachen de nächsten Primzahl wegnimmt ;) . Die Formel hier liefert ja einfach nur alle Zahlen, die weder durch 3, noch durch 2 teilbar sind.

mfG,


JayEff - Mi 14.01.09 00:23

user profile iconBoldar hat folgendes geschrieben Zum zitierten Posting springen:
Wirklich? Ist das bewiesen?
Leicht zu beweisen:
Ein Freund von mir hat folgendes geschrieben:
Also - überleg erstmal - sei p > 3 eine Primzahl. Kann sie dann gerade sein? - Natürlich nicht.
Jede ganze Zahl ist von der Form 6x + i (wobei i die Werte -1,0,1,2,3,4 durchläuft)
Als Primzahl kann sie nicht von der Form 6x + 0, 6x + 2, 6x + 4 sein, denn dann wäre sie durch zwei teilbar.
Wäre sie von der Form 6x + 3 = 3(2x + 1), dann wäre sie durch 3 teilbar.
Bleibt also nur 1 und -1 übrig.
:zustimm:


Narses - Mi 14.01.09 00:25

Moin!

Bitte ändere den Titel des Topics, da er wenig über das eigentlich Thema verrät. Hier der entsprechende Absatz aus den Richtlinien [http://www.entwickler-ecke.de/richtlinien.html]:

1.2 Beiträge:
Bitte formuliere den Betreff Deiner Beiträge so, dass andere Mitglieder anhand dieser bereits das eigentliche Thema festmachen können. Beiträge wie etwa "Eine Anfängerfrage" oder "Weiß jemand, wie das geht?" lassen den Leser im Unklaren darüber, was das Thema der Diskussion ist.[...]

Einfach oben bei Deinem ersten Beitrag auf user defined image oder user defined image klicken und den Titel ändern. Danke Dir!

cu
Narses


Horst_H - Mi 14.01.09 02:17

Hallo,

heute habe mal eine Uraltversoin von Anfang 2001 ausgegraben:
wheel-sieving (Bei http://www.devalco.de Stempel-Verfahren ).
Ein Stempel der Länge 2x3x5 = 30 (=5# wie 5! nur mit den ersten Primzahlen bis 5 ) hat nur noch (2-1)x(3-1)x(5-1) = 8 Erhebungen(beim Rad Zähne)[1,7,11,13,17,19,23,29] [31,37,..59] , was die Zahlen, die teilerfremd zu 2,3,5 sind darstellt.
Man hat jetzt nur noch 8/30 = 0.2666 aller Zahlen zu betrachten, weil die anderen garantiert durch 2,3,5 teilbar sind.
Jetzt braucht man nur 8 Bit um die 30 abzubilden muss aber eine Umrechnung vornehmen um aus der Zahl die Position im Bitfeld zu bestimmen und umgekehrt, welche Zahl repräsentiert ein bestimmtes gesetztes Bit.

Das kann man nicht nur 2,3,5 , sondern allgemein machen. ( Das gab eine paar neue Knoten im Hirn )
Jetzt braucht man in diesem Zahlenkreis beim Ausstreichen aber auch nur ab dem Quadrat nur die ausstreichen die auch vorkommen können.

Beispiel 7 ausstreichen: 7x [7,11,13,17,19... man muss also nur noch 8/30 Zahlen ausstreichen, statt vorher 7x[7,8,9,10,11,12,..

Man kann dies aber genauso für alle Zahlen die Teilerfremd zu den ersten 5 Primzahlen 2,3,5,7,11 sind machen.
Und das kann mein Programm, keine Sorge es wird dadurch nicht viel schneller ;-) weil der Aufwand recht schnell sehr groß wird.

Anbei das Negativbeispiel BIS = 6 ( = 17#) dauert doppelt solange wie BIS= 5 = 13# bei 1e9 , welches bei mir das schnellste bei diesem Verfahren ist. Beide belegen etwa 26 Mb.
Einmal 23,?? das andere Mal 11,8 Sekunden für die Zahlen eins bis eine Milliarde (2.3 Ghz,Amd X2)

Gruß Horst
Edit:
A space-efficient fast prime number sieve (1996) als pdf:
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.31.3936&rep=rep1&type=pdf
Meine Version ist scheinbar New_SW
Aber dort http://citeseerx.ist.psu.edu/search?q=wheel+sieve&submit=Search&sort=rel
findest Du auch richtig schnelle quadratische Siebe , wie das von alzaimar genutzte aus primegen.Siehe auch unten auf http://www.primzahlen.de/files/referent/kw/sieb.htm


Th69 - Mi 14.01.09 11:35

Es gibt noch einen möglichen Pre-Check:

Quelltext
1:
2:
3:
4:
if((n * n) % 24 == 1) // wenn man die Schleife bei 5 beginnen läßt (2 und 3 sind ja eh bekannt!)
{

}

Dieser verkürzt die Primzahlsuche nochmals. Man muß nur zusehen, daß (n * n) nicht größer als der maximale Wertebereich der Variablen wird (d.h. am besten int64 benutzen).

Hier mal mein kompletter Code in C++:

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:
void PrintPrims(ostream &os, unsigned int nMaxPrim)
{
  os << "2, 3";

  for(unsigned int n=5; n<=nMaxPrim; n+=2)
  {
    unsigned __int64 n2 = n; // avoid overflow
    if((n2 * n2) % 24 == 1) // pre-check for prim
    {
      unsigned int nMax = sqrt(n);
      bool bPrim = true;

      for(unsigned int m=3; m<=nMax; m+=2)
        if(n % m == 0)
        {
          bPrim = false;
          break;
        }

      if(bPrim)
        os << ", " << n;
    }
  }

  os << endl;
}

wobei sqrt(unsignd int) selbst implementiert ist (keine Fließkommazahlenberechnung, sondern mittels Intervallverfahren).

Die Liste der Primzahlen bis 1 Mio. füge ich mal an (ca. 600KB).

Moderiert von user profile iconNarses: Anhang gezippt