Primzahlen Contest

Primzahlen sind ein interessantes Thema. Mich interessiert vorallem, wie ich schnellstmöglich Primzahlen berechnen kann. Deswegen rufe ich hier mal einen kleinen Contest aus. Wer programmiert die schnellste Funktion um

primär: alle Primzahlen bis zu einem vorgegebenen Wert in einer Liste/Array zu speichern?

sekundär (1): zu überprüfen ob eine Zahl eine Primzahl ist oder nicht?
sekundär (2): die nächste Primzahl die auf eine Zahl folgt zu ermitteln?

Programmiersprache ist erstmal egal. Natürlich hab ich auch schon etwas (in Java) programmiert, könnte mir aber vorstellen, dass da durchaus noch etwas an performance zu holen ist.

Alle Primzahlen von 0 - n speichern:

		
	long time = System.currentTimeMillis();
	int[] ar = new int[max / 4];
	ar[0] = 2;
	ar[1] = 3;
	ar[2] = 5;
	int temp = 0;
	int pos = 3;
	boolean prim = true;
	for (int i = 7; i < max; i += 2) {
		prim = true;
		if (i % 3 != 0 && i % 5 != 0) {
			temp = (int)Math.sqrt(i) + 1;
			for (int j = 3; j < pos && ar[j] < temp; j++) {
				if (i % ar[j] == 0) {
					prim = false;
					break;
				}
			}
			if (prim) {
				ar[pos++] = i;
				if (pos == ar.length) {
					System.arraycopy(ar, 0, (ar = new int[ar.length + 5000]), 0, ar.length - 5001);
				}
			}
		}
	}
	System.out.println(System.currentTimeMillis() - time + "/" + pos);
	System.out.println(ar[pos - 1]);
	return ar;
}```

Testergebnisse mit einem Zugesicherten Speicher von 1024MB (DDR 400 wenn mich nicht alles täuscht) in der Virtual Machine und einem Pentium IV mit 3,8 GHz und HT Technologie (Anwendung ist Single-Threaded):

**Alle Primzahlen bis 1.000.000:**
Benötigte Millisekunden: 187
Gefundene Primzahlen: 78498
Letzte Primzahl: 999983

**Alle Primzahlen bis 5.000.000:**
Benötigte Millisekunden: 1.500
Gefundene Primzahlen: 348.513
Letzte Primzahl: 4.999.999

**Alle Primzahlen bis 10.000.000:**
Benötigte Millisekunden: 3.719
Gefundene Primzahlen: 664.579
Letzte Primzahl: 9.999.991

**Alle Primzahlen bis 50.000.000:**
Benötigte Millisekunden: 31.890
Gefundene Primzahlen: 3.001.134
Letzte Primzahl: 49.999.991

**Alle Primzahlen bis 100.000.000:**
Benötigte Millisekunden: 82.034
Gefundene Primzahlen: 5.761.455
Letzte Primzahl: 99.999.989

**Alle Primzahlen bis 500.000.000:**
Benötigte Millisekunden: 756.711
Gefundene Primzahlen: 26.355.867
Letzte Primzahl: 499.999.993

Überprüfen ob Zahl eine Primzahl ist:
```public static boolean isPrim(int numb) {
		
	if (numb == 2 || numb == 3) {
		return true;
	}
	if (numb % 2 != 0 && numb % 3 != 0) {
		int temp = (int)Math.sqrt(numb) + 1;
		for (int j = 4; j < temp; j++) {
			if (numb % j == 0) {
				return false;
			}
		}
	}
	else {
		return false;
	}
	return true;
}```

Nächste Primzahl auslesen:
```public static int getNextPrim(int numb) {
		
	if (numb % 2 == 0) {
		numb++;
	}
	while (true) {
		if (isPrim(numb)) {
			return numb;
		}
		numb += 2;
	}
}```

Ich weiß nicht ob man das so einfach „contesten“ kann. Ich habe vielleicht ne viel ältere Kiste als du, die dementsprechend hardwarebedingt für Berechnungen länger braucht. Wenn du mir mal deinen Rechner leihst mache ich vielleicht mit. :wink:

War ja auch mehr so gedacht, dass du was codest, es hier rein stellst und dann kann ich auf meinem, und du auf deinem Rechner die verschiedenen Algorithmen miteinander vergleichen.

Kannst also gern mitmachen :wink:

Für die isPrime() Abfrage:

Wie wär es mit der Java eigenen Methode isProbablePrime() aus BigInteger. Soweit ich rausfinden konnte beruht sie auf der Miller-Rabin-Methode.

Bei Bedarf könnte ich das Verfahren aber auch händisch implementieren.

Nach welchem Verfahren funzt den dein Algo? Auf Anhieb erkenn ich da nichts alt bekanntes.

Grüße

Ne händische Implementierung von dir fände ich gut, primitive Datentypen sollten ja doch schneller als BigInteger oder der gleichen sein :wink: . Außerdem soll ja nach möglichkeit keine vorgefertigte Klasse verwendet werden.

Das ist auch kein alt bekannter Algo, sondern ne Eigenentwicklung, die mir spontan eingefallen ist :wink: .

ok, dann mach ich mich mal selber ran. Ist es den erlaubt ein stabiles Verfahren, dessen Konvergenz bewiesen ist zu verwenden?

Um mal ein bekanntes zu nennen das Sieb des Eratosthenes.

wer garantiert mir die korrektheit deines algorithmus :wink: ?

http://de.wikipedia.org/wiki/Sieb_des_Eratosthenes implementieren…

niemand, evtl. logisches denken :wink: .

Ich hab ihn gegen andere, gängige Algorithmen geprüft. Dabei kam ich immer auf die selbe Anzahl an Primzahlen. Außerdem hab ich auch alle gefunden Primahlen bis zur stelle 10000 überprüft und konnte keinen Fehler entdecken.

Ist zwar ehrenhaft das du dir eigene Algorithmen ausdenkst, bei großen Zahlenbereichen ist der Sieb des Atkins aber kaum zu schlagen.
Wenn du es doch schaffst kannst du recht viel Geld verdienen.
np Probleme haben einen gewissen Wert :wink:
Die Tatsache das dieser wesentlich komplexer als der Sieb des Eratosthenes ist (für den ich eine ‚relativ‘ flotte standard Implementierung mit BitSet auf der Platte habe) lässt meine Lust diesen tatsächlich selbst zu schreiben allerdings gegen deinen Namen tendieren. :wink:

Hallo

Hab einen Sieb des Eratosthenes gemacht. Der ist schneller.

Für 500 Mio Primzahlen braucht er auf meinem 2GHZ Pentium M 69 sec. RAM braucht er 65MB für 500 Mio Primzahlen. Geht also auch.

Code ist in C# geschrieben, kannst auch relativ leicht nach Java umschreiben.

[CSharp]
BitArray ba;

    public void Primzahlentest(int bis)
    {
        ba = new BitArray(bis);

        ba.SetAll(false);

        DateTime start = DateTime.Now;
        int i = 2;
        while (i * i <= bis)
        {
            if (!ba**)
            {
                for (int j = i * i; j < bis; j += i)
                {
                    ba[j] = true;
                }
            }
            i++;
        }
        TimeSpan span = new TimeSpan(DateTime.Now.Ticks - start.Ticks);
        Console.WriteLine("Zeit: " + span.TotalSeconds + " s");

        long anzahl = 0;

        for (i = 2; i < bis; i++)
        {
            if (!ba**) anzahl++;
        }

        Console.WriteLine("Anzahl Primzahlen: " + anzahl);
    }

[/CSharp]

Da er auch 26.355.867 Primzahlen findet, kann er nicht so falsch sein :slight_smile:

Output:
Zeit: 69,452586 s
Anzahl: 26355867
Output Ende

lg Gerald

Hallo

Also, nachdem ich gestern Abend ein Sieb des Eratosthenes hier raufgestellt habe, hier noch das Sieb des Atkin.

Dieser Algorithmus ist nochmal ca. 30% schneller als mein gestriger. Ich habe ihn nicht selber erfunden, sondern nur von irgend so ner komischen Sprache übersetzt (glaube es war Pascal). Zu finden ist das Original auf der „Sieve of Atkin“- Seite der englischen Wikipedia unter Diskussion. Auf der Hauptseite gibt es „nur“ Pseudocode, der um einiges schwieriger zu übersetzen gewesen wäre.

Daten: Sieb des Atkin
Primzahlen bis: 500 Mio
Zeit: 46,078125 s
Anzahl: 26355867

Da die Anzahl wieder gleich ist, kann auch dieser nicht so falsch sein :stuck_out_tongue_winking_eye:

[CSharp]
int limit = 500000000;
double highestFactor = Math.Sqrt(limit);
BitArray isprim = new BitArray(limit);
int n = 0;

        DateTime start = DateTime.Now;
        for (int i = 1; i < highestFactor; i++)
        {
            for (int j = 1; j < highestFactor; j++)
            {
                n = 4 * i * i + j * j;
                if (n <= limit && ((n % 12 == 1) || (n % 12 == 5)))
                    isprim[n] = !isprim[n];

                n = 3 * i * i + j * j;
                if (n <= limit && (n % 12 == 7))
                    isprim[n] = !isprim[n];

                n = 3 * i * i - j * j;
                if (n <= limit && (i > j) && (n % 12 == 11))
                    isprim[n] = !isprim[n];
            }
        }

        for (int i = 5; i < highestFactor; i++)
        {
            if (isprim** == true)
            {
                for (int j = i * i; j < limit; j += i * i)
                    isprim[j] = false;
            }
        }

        isprim[2] = true;
        isprim[3] = true;
        TimeSpan span = new TimeSpan(DateTime.Now.Ticks - start.Ticks);
        Console.WriteLine("Zeit: " + span.TotalSeconds + " s");

        long count = 0;

        for (int i = 0; i < isprim.Length; i++)
        {
            if (isprim**) count++;
        }

        Console.WriteLine("Anzahl: " + count);

[/CSharp]

Ich hoffe, dass wer daran eine Freude hat.

lg Gerald

PS: Werde mich wieder meinem Matura- Lernen widmen, 7 Tage noch :slight_smile:

Schreibt die Ergebnisse sogar auf die Festplatte,
Vorsicht, bei einer Milliarde wird die Datei ca. 500MB groß :wink:

import java.io.IOException;
import java.io.PrintWriter;
import java.util.BitSet;

public final class PrimeSieve {

	private static void doIt(int SIZE) throws IOException {
		long t1, t2;
		final char newLine = '
';
		final PrintWriter writer = new PrintWriter(
				new FileWriter("results.txt"));
		BitSet set = new BitSet(SIZE);
		t1 = System.currentTimeMillis();
		set.set(0);
		set.set(1);
		writer.println(2);
		for (int i = 4; i < SIZE; i += 2) {
			set.set(i);
		}
		int end = (int) Math.sqrt(SIZE);
		for (int i = 3; i < end; i += 2) {
			if (!set.get(i)) {
				for (int j = i * i; j < SIZE; j += i) {
					set.set(j);
				}
				writer.println(i);
			}
		}
		if (end % 2 == 0)
			end++;
		for (int i = end; i < SIZE; i += 2)
			if (!set.get(i))
			{
				writer.println(i);
			}

		writer.flush();
		writer.close();
		t2 = System.currentTimeMillis();

		System.out.println((double) (t2 - t1) / 1000.0);
	}

	public final static void main(String[] args) throws IOException {

		doIt(1000000000);
	}
}```

Hallo

Ich hab das ganze nochmals verbessert. Erstens habe ich ein eigenes BitSet geschrieben, welches beliebig groß werden kann (begrenzt durch den RAM) - vorher war die Grenze bei int.MaxValue. Es besteht intern aus den (komprimierten) BitArrays. Zu benutzen ist es komplett gleich, außer dass nun dem Index (also bitset**) ein long übergeben werden kann. Jetzt kann man endich mehr als 2,1 Milliarden Zahlen untersuchen (auf das hat die Welt schon immer gewartet :smiley: ).

Weiters habe ich ein HDDBitSet geschrieben, welches auf Festplatte anstatt dem Arbeitsspeicher arbeitet - so kann man viele Milliarden Stellen durchgehen, ohne Arbeitsspeicherverbrauch (Programm selber hat unter .Net 15 MB). Je nach Festplatte ist dies natürlich 20 bis 40 mal langsamer, als im RAM, dafür nahezu unbegrenzt…

Dritte Neuerung ist, dass es nun möglich ist, das Sieb des Atkin (also die Version, die um 30% schneller ist, als das Sieb von Eratosthenes) auf einen bestimmten Bereich anzuwenden - also ist es möglich, die Zahlen zwischen 1 Mia bis 1,5 Mia auf Primzahlen zu untersuchen - Arbeitsspeicherverbrauch ist dabei nur 0,5 Mia bool- Werte (welche komprimiert sind), als nur der Bereich, den man wissen will.

Features:

  • Auswahl des Algorithmus: Sieb von Eratosthenes, Sieb von Atkin, BigInteger Prime (für jede Zahl wird ein BigInteger angelegt und durch die isPrime()- Methode gecheckt, ob es eine Primzahl ist - sehr langsam)
  • asynchrone Abarbeitung mit Anzeige der Zeit
  • danach Speicherung in beliebige Datei
  • Möglichkeit der berechnung eines best. Bereichs (z.b. von 1Mia bis 2Mia)

Noch etwas zu dem Java- Code: So weit ich weiß, ist das BitSet in Java ein reiner Vektor aus boolean- Werten.
In C# ist das BitArray komprimiert, sodass der echte Arbeitsspeicherverbrauch nur ca. 1/8 tel von dem unter Java ist - also mit gleichem Arbeitsspeicher 8 mal so viele Zahlen :smiley: . Falls ich mich irre und das BitSet in Java auch komprimiert ist, lasst es mich wissen.

Da mein Primzahlenprogramm bereits sehr angewachsen ist (einige Klassen), macht es keinen Sinn, jede Klasse hier zu posten.

Schreibt mir ein Email an: gerald.l AT gmx.at, wenn ihr es haben wollt (C# - .Net 2.0 - VS.Net 2005).

schönen Tag noch,
lg Gerald

gerry du kannst einem Post auch Ahnung hinzufügen wenn du sowas machen willst (als kleiner Hinweis ;))

Willst du damit sagen, er hatte keine Ahnung als er das geschrieben hat? 0o

ups ich meinte einen Anhang :smiley:
wie bin ich da auf Ahnung gekommen :smiley:
ich brauch URRRRRLLLLAAAAUUUUB und Erholung :frowning:

[QUOTE=gerry3]
Noch etwas zu dem Java- Code: So weit ich weiß, ist das BitSet in Java ein reiner Vektor aus boolean- Werten.
In C# ist das BitArray komprimiert, sodass der echte Arbeitsspeicherverbrauch nur ca. 1/8 tel von dem unter Java ist - also mit gleichem Arbeitsspeicher 8 mal so viele Zahlen :smiley: . Falls ich mich irre und das BitSet in Java auch komprimiert ist, lasst es mich wissen.[/QUOTE]
Ist natürlich totaler Blödsinn…
Bei Sun sind nicht nur Hobbyprogrammierer beschäftigt :twisted:
BitSet verwendet zur Speicherung der Bits ein long[]. Jedes long repräsentiert dabei ein word.
Die Manipulation der Werte erfolgt dann mittels Bitshift und binären logischen Verknüpfungen.
Aber ich will niemanden davon abhalten das Rad neu zu erfinden…

Hallo

gg, nein von dem mit dem Anhang hatte ich keine Ahnung. Aber andere Möglichkeit: Habs sowieso online gestellt.

Auf Primzahlen ist diese zu finden. Es gibt unter Downloads den Source als Visual Studio C# Projekt zum downloaden.

Ach ja: zu Wildcard: Ist schon klar, dass da jedes Bit ausgenutzt ist (hab ich ja bei meinem HDDBitArray auch gemacht). Dachte vorher, es wäre auch noch komprimiert. Hab mir das im Sourcecode bei Mono angeschaut, ist nicht der Fall…

Edit:
Wär aber ne Idee für weitere Schritte - durch z.B. durch den GZipStream in System.IO.Compression - damit kann man ca. doppelt so viel Zahlen bei gleich großem Arbeitsspeicher untersuchen. Mach ich nach der Matura ab Freitag :wink:

Viel Spaß damit.

lg Gerald

PS: Gebt mir mal Rückmeldung zum Programm, wenn es euch freut…

ist das nicht der passende Artikel für euch :wink: http://www.heise.de/newsticker/meldung/89996/from/atom10

Moin Moin,

ich hab ein Problem mit einer Programmieraufgabe. So wie ich das sehe habt Ihr das ziemlich drauf mit Programmieren und mein Prof meinte, diese Aufgaben kann man in 5 Minuten erledigen, naja, ich jedenfalls nicht.

Es soll ein Programm sein, das mir entweder sagt, ob die eingegebene Zahl eine Primzahl ist, oder es gibt mir alle Primzahlen in einem vorgegebenen Bereich aus.

Ich hab schon mal das Gerüst gebaut, aber weiter weiß ich nicht. Das Programm soll in dem Rahmen laufen. Im unteren Fenster sollen immer die Hilfsanweisungen in der ersten Zeile stehen. Wenn man z drückt kann man in der zweiten Zeile eine beliebige Zahl angeben, mit Enter bestätigen und in großen Hauptfenster steht dann “xyz ist eine Primzahl” oder eben auch nicht. Wenn man b drückt, soll man einen Bereich angeben können und alle Primzahlen in diesem Bereich erscheinen dann in tabellenform im Hauptfenster.
Wenn man h drückt, soll dieser Hilfstext erscheinen:
Programm zur Berchnung von Primzahlen

Primzahlen sind durch 1 und durch sich selbst teilbar!

Wollen Sie prüfen lassen, ob eine Zahl eine Primzahl ist,
dann drücken Sie “Z” und geben eine beliebige Zahl ein.
Bestätigen Sie mit “Enter” und das Ergebnis erscheint auf
dem Bildschirm.
Wollen Sie alle Primzahlen angezeigt bekommen die in einem
bestimmten Bereich sind, dann drücken Sie “B” und geben den
Bereich ein aus dem die Primzahlen angezeigt werden sollen
(z.B. x 50 bis y 500).
Zum Löschen des Bildschirms drücken Sie “L”

Es wäre toll, wenn Ihr mir helfen könntet.


uses crt;

const ESC = #27;
      FKT = #0;                
      F1  = #59;               
      F2  = #60;
      F3  = #61;

var eingabe: char;
    i,y : integer;


<
<
<
     PROZEDUREN????
>
>
>





begin
  clrscr;

  write ('╔');                               {alt 201}
  for i := 1 to 78 do write ('═');           {alt 205}
  write ('╗');                               {alt 187}
  for i := 2 to 45 do
    begin
      gotoxy(y*1+80,i);
      write ('║');                           {alt 186}
      gotoxy(y*79+1,i);
      write ('║');                           {alt 186}
    end;
  gotoxy (1,46);
  write('╚');                                {alt 200}
  for i := 1 to 78 do write ('═');           {alt 205}
  write ('╝');                               {alt 188}

  gotoxy (1,43);
  write('╠');                                {alt 204}
  for i := 1 to 78 do write ('═');           {alt 205}
  write ('╣');                               {alt 185}

  gotoxy (1,3);
  write('╠');                                {alt 204}
  for i := 1 to 78 do write ('═');           {alt 205}
  write ('╣');                               {alt 185}

  gotoxy(2,2);
  write('NAME');

  gotoxy(35,2);
  write('Übung 5');

  gotoxy(74,2);
  write('2007');

  window(2,44,77,45);

  gotoxy(2,44);
  write('h = Hilfstext, z = Primzahl, b = Bereich, ESC = Ende');



  clrscr;                         
  repeat
    eingabe := upcase(readkey);   
    case eingabe of               
      FKT : begin
              eingabe:=readkey;   
              case eingabe of
                F1:;
                F2:;
                F3:;
                else writeln('falsche Funktionstaste CODE',ord(eingabe));
              end;
            end;
      'A' : write('Das ist eine Primzahl');         {Zahl eingeben}
      'B' : write(eingabe);                         {Bereich von x bis Y}
      'L' : clrscr;
      else writeln(eingabe,' ',ord(eingabe));
    end;
  until eingabe = ESC;
end.