Autor Beitrag
Delphi-Laie
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 1600
Erhaltene Danke: 232


Delphi 2 - RAD-Studio 10.1 Berlin
BeitragVerfasst: So 17.06.12 17:02 
Eine (einfache oder komplexere) Formel zur Erzeugung der Primzahlen, optimalerweise der x. Primzahl, soll es ja bis heute nicht geben. Wohl gibt es aber inzwischen von Gerald Bühler einen siebfreien (i.Ggs.z. Eratosthenes, Atkin) Algorithmus zur Generierung derselben, der allerdings mit einem Hiflsarray arbeitet und bei größeren Zahlen auch spürbar langsamer wird. Das geht aber vom algorithmischen Niveau her immer noch um Größenordnungen über die triviale und von der Laufzeit her inakzeptable Schulmethode heraus, über Probedivisionen aller ungeraden Zahlen (und ggf. Einbezug noch weiterer Teilbarkeitsregeln) bis hin zu den Wurzeln der Zahlen zu operieren und so die Primeigenschaft nach der Versuch-Und-Irrtum-Methode festzustellen. Das wird bei wachsenden Eingabegrößen / -zahlen recht schnell ziemlich zäh.

Interessanterweise ist jener Algorithmus erweiterbar, wie ich hoffte, probierte und herausfand, sodaß man sich nicht vorher festlegen muß, bis zu welcher natürlichen Zahl n (oder alternativ bis zur Primzahl bis zur Nummer soundso) man enumerieren möchte (im Gegensatz zu den Sieben). Es sind also beliebig viele Primzahlen aufsteigend lückenlos generierbar bzw. enumerierbar.

Im Anhang befinden sich 3 Demonstrationsprogramme dazu, die ich auf die Schnelle erstellte. Primgen 1 ist das nach Delphi übersetzte Original, während Primgen 2 und 3 die nach oben offene Enumeration demonstrieren.

Edit: Noch eine kleine Unsauberkeit korrigiert.
Einloggen, um Attachments anzusehen!


Zuletzt bearbeitet von Delphi-Laie am Mo 25.06.12 09:01, insgesamt 4-mal bearbeitet

Für diesen Beitrag haben gedankt: Mathematiker
Mathematiker
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2622
Erhaltene Danke: 1447

Win 7, 8.1, 10
Delphi 5, 7, 10.1
BeitragVerfasst: So 17.06.12 19:18 
Hallo Delphi-Laie,
die drei Programme sind interessant, allerdings beim Zählen der Primzahlen nicht wirklich schnell.
Wesentlich schneller ist folgende rekursive Methode, die vor allem auf größere Datenmengen verzichtet.
ausblenden volle Höhe 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:
Function Primzahlanzahl( N:integer ):integer;
var r, p:integer;
function isprime(n:integer):Boolean;
var i,w:integer;
    teiler:boolean;
begin
   if n = 1 then begin
     isprime:=false; exit;
   end;
   if n in [2,3,5,7,11,13then begin
     isprime:=true; exit;
   end;
   if odd(n) then begin
     if n mod 3 = 0 then begin
       isprime:=false; exit;
     end;
     if n mod 5 = 0 then begin
       isprime:=false; exit;
     end;
     if n mod 7 = 0 then begin
       isprime:=false; exit;
     end;
     if n mod 11 = 0 then begin
       isprime:=false; exit;
     end;
     i:=13;
     w:=trunc(sqrt(n)+1);
     if w<13 then
     begin
       isprime:=true;
       exit;
     end;
     repeat
       teiler:=(n mod i = 0);
       inc(i,2);
     until teiler or (i>w);
     isprime:=not teiler;
   end
   else isprime:=false;
end;
Function Entfernen( n,p: integer):integer;
var nklein, p2, d: integer;
begin
    nklein := n div p;
    if nklein<p then
    begin
       Entfernen:=1;
       exit
    end;
    d := nklein;
    for p2:=2 to p-1 do
       if IsPrime(p2) then
          d := d - Entfernen(nklein,p2);
    Entfernen:=d
end;
begin
    r := N-1;
    for p:=2 to n do
       if p*p>n then
       begin
          Primzahlanzahl := r;
          exit
       end
       else
          if IsPrime(p) then
          begin
             r := r - Entfernen(n,p);
             r := r+1;
          end;
    Primzahlanzahl := r;
end;

Bei einem Test auf meinem Rechner benötigte dein Programm bis 10 Millionen 3,74 s, meine Routine 0,11 s. Beim Zählen bis 100 Millionen wurde es noch dramatischer 75,74 s zu 0,89 s.
Zu meinem Text: Die scheinbar umständliche Funktion isprime sorgt dafür, dass möglichst schnell kleine Primteiler ausgesondert werden. Wahrscheinlich kann man isprime durch eine effizientere Funktion ersetzen.
Beste Grüße
Mathematiker
Horst_H
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1652
Erhaltene Danke: 243

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: So 17.06.12 21:53 
Hallo,

es kann doch kaum einer gegen die Version des Kim Walisch code.google.com/p/primesieve/ anstinken, der jüst eine neue Version hat 3.7 ;-)
Zitat:
Bis 1e9 in 0,09 Sekunden für einen
Intel Core i7-920 4 x (2.66GHz, 32K L1 Data Cache)

Und soviel Platz braucht die auch nicht, sonst wäre sie nicht so schnell.
Ich die Version 1 jetzt mal bis 1e8 rechnen lassen und die belegt dabei maximal knapp über 50 Mb , das ist wohl nicht sehr sparsam.
Gelegenheit mal die PowerShell und Measure-Command zu testen.
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
PS ...\Primgen 1> Measure-Command {start-process Project1 -Wait}

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 45
Milliseconds      : 590
Ticks             : 455905584
TotalDays         : 0,0005276685
TotalHours        : 0,012664044
TotalMinutes      : 0,75984264
TotalSeconds      : 45,5905584
TotalMilliseconds : 45590,5584

Ich habe 1e8 per Copy und paste eingefügt und mich auch sonst beeilt.
Also 40 Sekunden werden es gewiß gewesen sein. ;-)
Das ist jetzt nicht wirklich überzeugend, wozu wurde der Algorithmus notariell bestätigt?

Gruß Horst
Selbst mein Uralt Programm ist wesentlich schneller.
0,3 Sekunden bis 1e8 bei 2,6 Mb davon 835kb privater Speicher.
SIeve of Atkin in seiner völlig unoptimierten, ursprünglichen Form, die mir Fiete zeigte, braucht über 5 Sekunden bei 100 Mb Speicherbedarf.
alzaimar hatte dieses hochoptimiert, nach einer Vorlage in C, zu Delphi übertragen
www.entwickler-ecke....ktion_33013,232.html
-> PrimeGenDelphi.zip www.entwickler-ecke....download.php?id=1364
Delphi-Laie Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 1600
Erhaltene Danke: 232


Delphi 2 - RAD-Studio 10.1 Berlin
BeitragVerfasst: So 17.06.12 22:54 
Danke für Eure Reaktionen!

Daß jener Algorithmus nicht die beste Komplexität hat, ging aus meinem ersten Beitrage ja schon hervor. Dafür ist er insofern eleganter, als daß er sich von der naheliegenden und seit der Vorantike bekannten Versuch-Und-Irrtum-Methode der Probedivisionen entfernt / unterscheidet. Die Siebungen, wenn es einem nur um Geschwindigkeit geht, sind ja noch viel schneller, doch speicherverschwendend und eben leider nicht enumerierend: Man muß sich vorher festlegen bzw. beschränken, und danach ist Schluß bzw. man muß mit einem größeren Startwert neu beginnen, während eine solche Enumeration eben beliebig wiederholt bzw. fortgesetzt werden kann. Bei großen Startmengen wird der Speicherverbrauch der Startmengen für die Siebungen sogar für GByte-Computer deutlich bis heftig (die speichersparenden Datenkomprimierungen im "Marktführer" jetzt mal außer Betracht gelassen, die Tendenz ist dennoch gegeben).

Ohne etwas in den Himmel zu loben, was ohnehin im wesentlichen nicht von mir stammt: Konkret die x. Primzahl (ohne über den Umweg der Enumeration) zu generieren, ist auch dieser Algorithmus leider nicht imstande. Für Primzahlzerlegungen und -faktorisierungen hingegen liefert er in Einzelschüssen jedoch genau die saubere Startmenge, die man dafür benötigt.


Zuletzt bearbeitet von Delphi-Laie am So 17.06.12 23:42, insgesamt 1-mal bearbeitet
Horst_H
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1652
Erhaltene Danke: 243

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: So 17.06.12 23:18 
Hallo,

nichtsdestotrotz finde ich bei einer Speicherbelegung von 50 Mb um alle Zahlen bis 1e8 mit Primgen 1 zu bestimmen, völlig gegensätzlich zu der Aussage von einem platzsparendem Algorithmus.

Gruß Horst
Delphi-Laie Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 1600
Erhaltene Danke: 232


Delphi 2 - RAD-Studio 10.1 Berlin
BeitragVerfasst: Mo 18.06.12 10:05 
user profile iconHorst_H hat folgendes geschrieben Zum zitierten Posting springen:
nichtsdestotrotz finde ich bei einer Speicherbelegung von 50 Mb um alle Zahlen bis 1e8 mit Primgen 1 zu bestimmen, völlig gegensätzlich zu der Aussage von einem platzsparendem Algorithmus.


Meinst Du das beispielhafte C++-Programm? Zugegebenermaßen, als ich Eratosthenes las, war mein Interesse schon wieder erloschen.

Die Sache interessierte mich nun aber doch weiter: Also, die Summe der Länge beider Arrays (des eigentlichen, in dem die Primzahlen abgelegt werden und die des Hilfsarrays) beträgt nur einen Bruchteil dessen, als wenn man ein Array einfach mit allen natürlichen Zahlen füllen würde und Erathostenes darauf losließe. Gut, bei Eratosthenes muß man nur einen Wert, nämlich boolean, ablegen, das reicht ja als Information über die Primeigenschaft. Ob das von Delphi speichersparend umgesetzt wird, weiß ich nicht, vermute aber, daß für jeden Wert des Types "boolean" analog zu "bool" letztlich doch eine integre Variable "verschwendet" wird. Bitweiser Zugriff auf Integertypen wäre ja auch langsamer.

Dennoch ist die Tendenz eindeutig: Der Speicherbedarf (zumindest i.S. der Summe der Längen beider Arrays) bleibt gegenüber der Länge des Eratosthenes-Arrays immer mehr zurück, je mehr man enumeriert. Er beträgt nur einen (eben immer kleiner werdenden) Bruchteil.
Mathematiker
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2622
Erhaltene Danke: 1447

Win 7, 8.1, 10
Delphi 5, 7, 10.1
BeitragVerfasst: Mo 18.06.12 11:46 
Hallo Hort_H,
user profile iconHorst_H hat folgendes geschrieben Zum zitierten Posting springen:
alzaimar hatte dieses hochoptimiert, nach einer Vorlage in C, zu Delphi übertragen

Ich habe mir Deinen Hinweis auf PrimeGen angesehen und kann nur sagen: faszinierend.
Aber! Irgendwie stelle ich mich zu dämlich an. :autsch:
Ich schaffe es weder die Anzahl der Primzahlen bis zu einer genauen(!) Grenze, sagen wir 5 Millionen, berechnen zu lassen noch irgendeine Primzahl (zum Beispiel die letzte berechnete) anzeigen zu lassen.
Aus dem Quelltext werde ich einfach nicht schlau. Die Prozedur berechnet offensichtlich die Primzahlen:
ausblenden 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:
Procedure DoIt;
Var
  i,j : Integer;
  Buf : pABuffer;
  pq, Nexti : pCardinal;
  k,q,kPos,bits,data : Cardinal;

Begin
  fillChar (a,sizeOf (a), 0);
  For i:=0 to 7 do begin
    Nexti := @next[i];
    Buf := @a[i];
    pq := @qtab[0];
    For j:=0 to 3508 do Begin
      k := nexti^;
      if (k < ccBlockLen) Then Begin
        q := pq^;
        Repeat
          kPos := k shr 5;
          buf^[kPos] := buf^[kPos] or (1 shl (k and 31));
          inc (k,q);
        Until k >= ccBlockLen;
        End;
      nexti^ := k - ccBlockLen;  inc (pq);
      inc (nexti);
      End;
    End;
End;

Aber, wo bitte stehen diese dann. Die vielen Zeiger und Adresszuweisungen sind für mich kryptisch.
Kennst Du ein Beispiel, wie man PrimeGen vernünftig einsetzen kann?
Danke.
Mathematiker
Horst_H
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1652
Erhaltene Danke: 243

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: Mo 18.06.12 12:54 
Hallo,

@Delphi-Laie:
Wie kommt es denn nun, das Dein PrimeGen 1 maximal 50 Mb=5e7 Byte belegt um die Primzahlen bis 1e8 zu zählen.Das ist so groß wie das Sieb des Eratosthenes mit ungeraden Zahlen.Sind das nur Datenleichen auf dem Weg nach oben?

@Mathematiker:
alzaimar hat damals, 2005, das Programm primeGen cr.yp.to/primegen.html nach Delphi übertragen, das ist nicht auf seinem Mist gewachsen, was er auch nie behauptet hat.Ich habe doch in dem thread auch gefragt, wie man den jetzt zu einer bestimmten Zahl zählen muss ;-)

Mal schauen, ob ich drauf komme.

Gruß Horst
Gammatester
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 328
Erhaltene Danke: 101



BeitragVerfasst: Mo 18.06.12 12:54 
Einen Algorithmus der aller Primzahlen für bis zu einer bestimment großen Zahl bestimmt braucht man wohl eher nur für theroetische Zwecke, mal abgesehen vom Speicher (Problem 50MB siehe oben), der praktisch linear anwächst (ca n / ln(n)). Zwei Aufgaben, die in der Praxis vorkommen, sind u.a.

1. Ein Primzahlgenerator bzw. -Enumerator, der fortlaufende Primzahlen erzeugt ohne diese zu speichern.

2. Ein segmentiertes Sieb, daß zB die auf eine Zahl folgenden 1000 oder 10000 Primzahlen erzeugt und speichert (zB für Primzahlen im RSA-Algorithmus)

Der Generator aus 1. könnte theoretisch ohne Speicher auskommen, ist dann aber langsam. Praktisch wird er auch eine Art segmetiertes Sieb genutzen. Für das Problem 5000000-te Primzahl liefert mein Generator aus MPArith für 32-Bit-Integer
ausblenden Quelltext
1:
2:
3:
X:\TMP>T_PS.EXE
Zeit [s]: 0.214
5000000-te Primzahl = 86028121

was man leicht mit Wolfram Alpha verifizieren kann. Der Quellcode dazu:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
uses
  mp_prime;
var
  sieve: TSieve;
  np,n,p: longint;
begin
  prime_sieve_init(sieve,2);
  np := 5000000;
  for n:=1 to np do begin
    p := prime_sieve_next(sieve);
  end;
  writeln(np,'-te Primzahl = ',p);
end.
Delphi-Laie Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 1600
Erhaltene Danke: 232


Delphi 2 - RAD-Studio 10.1 Berlin
BeitragVerfasst: Mo 18.06.12 13:38 
user profile iconHorst_H hat folgendes geschrieben Zum zitierten Posting springen:
@Delphi-Laie:
Wie kommt es denn nun, das Dein PrimeGen 1 maximal 50 Mb=5e7 Byte belegt um die Primzahlen bis 1e8 zu zählen.Das ist so groß wie das Sieb des Eratosthenes mit ungeraden Zahlen.Sind das nur Datenleichen auf dem Weg nach oben?


Also, "mein" Primgen ist es genaugenommen nicht. Ich übertrug im wesentlichen nur den Algorithmus nach Delphi. Auch kann ich weder zum Speicherverbrauch noch über irgendwelche "Datenleichen" i.S. einer Begründung Auskunft geben. Ich bin schließlich nicht das Programm selbst. Bei mir benötigt das Programm bei n= ca. 5 Mio. nur knapp 4 MByte im Speicher - die Tendenz ist natürlich allmählich steigend. Unelegant ist in meinen Augen höchstens das ständige Anpassen der Arraylängen, doch auch nur, seitdem ich weiß, daß das die Speicherfragmentierung forciert (was aber nach Programmende wieder behoben sein dürfte).

Was ich jedoch kann, ist, die Länge der beiden Arrays (konkret: deren Summe) mit der natürlichen Zahl n, bei der der Algorithmus gerade werkelt, vergleichen und ins Verhältnis setzen. Bei größeren Werten von n, im Bereich von einigen Millionen, sinkt er auf unter 1/7 der Zahl n ab. n wäre die Länge des Eratosthenes-Arrays.

Ergänzungen:

1. Bei 100 Mio. steigt der Speicherbedarf "meines" obigen Programmes tatsächlich bis auf ca. 50 MByte an.
2. Ein bis 100 Mio. dimensionertes Cardinalarray erfodert jedoch knapp 400 MByte!
3. Ein bis 100 Mio. dimensionertes Wordarray erfodert knapp 200 MByte.
4. Ein bis 100 Mio. dimensionertes Bytearray erfodert - nunmehr nicht überraschend - ca. 100 MByte.

Also werden Integervariablen anscheinend speichersparsam abgelegt.

5. Ein bis 100 Mio. dimensionertes Booelanarray erfodert ebenfalls ca. 100 MByte. Anscheinend wird tatsächlich pro booleschen Wert ein Byte benötig.

Mithin ist "mein" obiges Programm eben doch speichersparsamer. Allerdings war mein simpler Vergleich der Arraylängen zu oberflächlich, da ein Sieb nur mit Boolean gefüllt werden muß: Es bleiben also 50 MByte zu 100 MByte, also etwa die Hälfte.

Und noch eine Ergänzung: Wenn man Eratosthenes nur mit ungeraden Zahlen füllt, ja, dann ist tatsächlich Gleichstand erreicht!


Zuletzt bearbeitet von Delphi-Laie am Mo 25.06.12 08:15, insgesamt 1-mal bearbeitet
Delphi-Laie Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 1600
Erhaltene Danke: 232


Delphi 2 - RAD-Studio 10.1 Berlin
BeitragVerfasst: So 24.06.12 18:28 
Zurecht wurde hier kritisiert, daß der Speicherverbrauch dieses hier vorgestellten Algorithmus' einem Eratoshenes-Sieb, das nur ungerade Zahlen speichert, gleicht.

Ich nahm mich der Sache an. Schon in der ersten Version fiel mir auf, daß das Hilfsarray überwiegend (in der weit überwiegend höheren und damit größeren "Hälfte") aus Nullen besteht. Tatsächlich wird es an falscher Stelle des Algorithmus' falsch dimensioniert, konkret: natürlich überdimensioniert. Jetzt müßte es jedoch stimmen. Ich lud bei den drei Demonstrationsprogrammen die jeweils verbesserte Version oben hoch.

Nach wie vor bin ich der Meinung, daß dieser Algorithmus ein höheres (algorithmisches) Nivaus als jedes noch so ausgeklügeltes Sieb darstellt, weil er enumeriert und damit - ohne Neuanfang - "nach oben offen" ist.

Viele Grüße

Delphi-Laie

Edit: Nach dieser Korrektur wächst die Länge des Hilfsarrays anscheinend nur noch logarithmisch mit der Anzahl der ermittelten Primzahlen, fällt also bei wachsender Primzahlmenge bwz. wachsendem Primzahlarray immer weniger bis kaum noch ins Gewicht.
Gammatester
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 328
Erhaltene Danke: 101



BeitragVerfasst: Mo 09.07.12 10:11 
user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
... Bei einem Test auf meinem Rechner benötigte dein Programm bis 10 Millionen 3,74 s, meine Routine 0,11 s. Beim Zählen bis 100 Millionen wurde es noch dramatischer 75,74 s zu 0,89 s. Zu meinem Text: Die scheinbar umständliche Funktion isprime sorgt dafür, dass möglichst schnell kleine Primteiler ausgesondert werden. Wahrscheinlich kann man isprime durch eine effizientere Funktion ersetzen.

Hallo Mathematiker,
entschuldige, daß ich nochmal auf diesen Beitrag zurückkomme. Ich habe mal Deine Prozedur ein wenig optimiert, Zeiten auf meinem Rechner:
ausblenden Quelltext
1:
2:
bis  100 Millionen: Dein Original 0.318s,   meine V3: 0.048
bis 1000 Millionen: Dein Original 3.437s,   meine V3: 0.422
Also ein relativ schneller Code für die Funktion PrimePi(n).

Frage: Hast Du den Entfernen-Algorithmus selbstentwickelt oder gibt es dazu Quellen, was genau macht Entfernen(n,p)?

Gruß Gammatester
Delphi-Laie Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 1600
Erhaltene Danke: 232


Delphi 2 - RAD-Studio 10.1 Berlin
BeitragVerfasst: Mo 09.07.12 10:16 
Ich beschäftigte mich mit dem Algorihtmus noch einmal näher.

Also, das "Rumgemache" mit der Quadratwurzel kann ebensogut entfallen. Und dann lassen sich die inkrementierte Variablen (c und i) auch um 2 inkrementieren, um gleich alle geraden Zahlen auszuschließen. Die 2 als erste bzw. kleinste und einzige gerade Primzahl muß dann besonders berücksichtigt werden.


Immerhin, und das ist ja das Ziel des originalen Programmautors, entfallen die Probedivisionen. Inwieweit das wirklich einen Geschwindigkeitsvorteil bringt, ist mir nicht bekannt. Div und mod bei den Integern sind ja auch recht schnell. Die Geschwindigkeit dieses Algorithmus' mit der eines Siebes zu vergleichen, bringt ihn unweigerlich ins Hintertreffen. Der Vorteil liegt gegenüber dem der Probedivisionen und hat ihm gemein, eine nach oben offene Enumerierbarkeit zu ermöglichen, die bei Sieben nur "pseuso" möglich ist, indem die "voreingestellte" Siebgröße erhöht wird und dann erneut gesiebt wird. Außerdem entfällt der hohe Speicherbedarf, den die Siebe mit sich bringen.


Zuletzt bearbeitet von Delphi-Laie am Mo 09.07.12 14:04, insgesamt 1-mal bearbeitet
Mathematiker
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2622
Erhaltene Danke: 1447

Win 7, 8.1, 10
Delphi 5, 7, 10.1
BeitragVerfasst: Mo 09.07.12 10:47 
Hallo Gammatester,
user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:
Ich habe mal Deine Prozedur ein wenig optimiert, Zeiten auf meinem Rechner:
ausblenden Quelltext
1:
2:
bis  100 Millionen: Dein Original 0.318s,   meine V3: 0.048
bis 1000 Millionen: Dein Original 3.437s,   meine V3: 0.422
Also ein relativ schneller Code für die Funktion PrimePi(n).

Klingt sehr gut. Es wäre nett, wenn Du die Änderungen veröffentlichen würdest.
user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:
Hast Du den Entfernen-Algorithmus selbstentwickelt oder gibt es dazu Quellen, was genau macht Entfernen(n,p)?

Nein, der ist nicht von mir. Vor einigen Jahren, etwa 1995-2000, habe ich den Algorithmus in einer Zeitschrift (oder im Netz?) gesehen. Ich weiß nicht mehr wo und kann Dir auch nicht genau sagen, von wem er stammt. Ich glaube mich zu errinnern, dass er von einem der Borweins war. :nixweiss: Auf jeden Fall war er in C geschrieben.
Verstanden habe ich Ihn auch nicht so richtig. Aber er funktioniert erstaunlicher Weise genau.
Beste Grüße
Mathematiker

_________________
Töten im Krieg ist nach meiner Auffassung um nichts besser als gewöhnlicher Mord. Albert Einstein
Gammatester
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 328
Erhaltene Danke: 101



BeitragVerfasst: Mo 09.07.12 10:50 
@Delphi-Laie,

schon klar, meine Frage bezog sich wirklich nur konkret auf des Mathematikers Funktion Entfernen. Hier meine Implementation der PrimePi-Funktion (setzt MPArith voraus):
ausblenden volle Höhe 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:
program t_psv3;

{$i STD.INC}
{$i mp_conf.inc}

{$x+}  {pchar I/O}
{$i+}  {RTE on I/O error}

{$ifdef APPCONS}
  {$apptype console}
{$endif}

{$ifndef FPC}
{$N+}
{$endif}

uses
  HRTimer,
  mp_prime;


{---------------------------------------------------------------------------}
function remove(n,p: longint):longint;
var
  d: longint;
  k: integer;
  w: word;
begin
  {Basiert auf function Entfernen aus Mathematiker-Beitrag in}
  {<http://www.entwickler-ecke.de/viewtopic.php?t=109633>}
  n := n div p;
  if n<p then remove := 1
  else begin
    d := n;
    for k:=1 to NumPrimes16 do begin
      w := Primes16[k];
      if w+1 > p then break;
      d := d - remove(n,w);
    end;
    remove := d;
  end;
end;

{---------------------------------------------------------------------------}
function primepi(n: longint): longint;
  {-Returns the prime counting function pi(n) = #{p <= n, p prime}
var
  r: longint;
  k: integer;
  p: word;
  w: word;
begin
  r := n-1;
  if n > 2 then begin
    w := trunc(sqrt(n));
    for k:=1 to NumPrimes16 do begin
      p := Primes16[k];
      if p > w then break;
      r := succ(r) - remove(n,p);
    end;
  end;
  primepi := r;
end;

var
  np,n: longint;
  HR: THRTimer;
begin
  writeln('-----------------------');
  np := 2100000000;
  StartTimer(HR);
  n := primepi(np);
  writeln('Zeit [s]: ',ReadSeconds(HR):1:3);
  writeln('V3 PrimePi(',np,') = ',n);
end.

Die Geschwindigkeit [PrimePi(2100000000) = 102886526 in 0.855s] gefällt mir so gut, daß ich beabsichtigte, sie in die nächste MPArith aufzunehmen, selbstverständlich mit Referenz an Mathematiker und Entwicklerecke wie im Source.

Dazu wäre es natürlich angebracht, genau zu beschreiben, was Entfernen/Remove macht.

Gruß Gammatester
Horst_H
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1652
Erhaltene Danke: 243

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: Mo 09.07.12 11:10 
Hallo,

was remove macht, ist doch klar.
n div p ->Anzahl aller Vielfachen von p (Faltoren k = 1,2,3..n div p)
Das sind die theoretischen Ausstreichungen durch p und davon zieht ( remove ) man die ab, die durch vorangegangenen Primzahlen schon gestrichen wurden.

Gruß Horst
Mathematiker
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2622
Erhaltene Danke: 1447

Win 7, 8.1, 10
Delphi 5, 7, 10.1
BeitragVerfasst: Mo 09.07.12 12:05 
Hallo,
ich habe die Quelle gefunden.
Irgendwie werde ich alt, dass ich mir nicht mehr merken kann, von wem etwas war. Vielleicht ist es auch BSE? :lol:
Beste Grüße
Mathematiker

Nachtrag: Original-Datei entfernt!

_________________
Töten im Krieg ist nach meiner Auffassung um nichts besser als gewöhnlicher Mord. Albert Einstein


Zuletzt bearbeitet von Mathematiker am So 09.09.12 23:21, insgesamt 1-mal bearbeitet
Gammatester
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 328
Erhaltene Danke: 101



BeitragVerfasst: Mo 09.07.12 12:28 
user profile iconHorst_H hat folgendes geschrieben Zum zitierten Posting springen:
..was remove macht, ist doch klar.
n div p ->Anzahl aller Vielfachen von p (Faltoren k = 1,2,3..n div p)
Das sind die theoretischen Ausstreichungen durch p und davon zieht ( remove ) man die ab, die durch vorangegangenen Primzahlen schon gestrichen wurden.
Soweit klar. Nur das 'vorangegangen' durch die Implementation festgelegt wurde, man könnte die Schleife in PrimePi auch rückwärts laufen lassen. Hältst Du diese Charakterisierung für richtig?

"Remove(n,p) mit p prim <= n zählt die Vielfachen k*p <= n, die nicht Vielfache der Primzahlen p_j < p sind."

Gruß Gammatester
Gammatester
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 328
Erhaltene Danke: 101



BeitragVerfasst: Mo 09.07.12 12:44 
user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Hallo,
ich habe die Quelle gefunden.
Irgendwie werde ich alt, dass ich mir nicht mehr merken kann, von wem etwas war. Vielleicht ist es auch BSE? :lol:
Danke, sieht ja so aus, als wenn es eine Eigenentwicklung von Albert van der Horst in FORTH :wink: wäre, und das Pascalprogram zu Dokumentation gedacht war.
Horst_H
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1652
Erhaltene Danke: 243

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: Mo 09.07.12 15:14 
Hallo,

Zitat:
Hältst Du diese Charakterisierung für richtig?

"Remove(n,p) mit p prim <= n zählt die Anzahl der Vielfachen k*p <= n, die nicht Vielfache der Primzahlen p_j < p sind."


Im Endeffekt ist es genau das.
Man kann es auch anschaulich so formulieren,
Primzahl P sagt ich streiche nsmall (n DIV P ) Zahlen und fragt jetzt die alle kleineren, von klein zu groß hin, wieviele sie davon schon gestrichen haben und zieht diese von seiner Zahl ab.
Und die fragen ihrerseits genauso.

remove selber gibt durch Rekursion die Anzahl, der nur durch die Primzahl p_j zuerst ausgestrichen Zahlen an ( also ist p_j ist der kleinste Faktor der gestrichenen Zahl ).
In pi.pas ist das ja als dismissed bezeichnet, wenn google translate recht hat, soll man diese Zahlen dann als die durch p_j verworfen ( nicht prim ) betrachten.

Ich dachte zuerst, wie kann das je fertig werden wenn Anzahl(Primzahlen bis p)*Anzahl (Primzahlen bis p)/2 Aufrufe getätigt werden, aber das sind für Dword auch nur 6543*6543/2 ~ 21 Milliionen , zudem landet man ja schnell bei extrem kleinen Zahlen nsmall, sodass die Rekursionstiefe nicht 6542 ist.
Da 2 die kleinste Primzahl, löst aber keine Rekursion mehr aus, also ist und 65521? ( ~3^10) für DWord die Größte, ist maximale die Rekursionstiefe 10 oder 11.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
  nsmall := n div p;
  if nsmall<p then  begin
    DismissedBy :=1;
    exit
    end;
  d := nsmall; 
  for p2:=2 to p-1 do
      if IsPrime(p2) then
          d := d - DismissedBy( nsmall, p2 );


Gruß Horst