Algorithmensammlung: Zahlentheorie: Sieb des Eratosthenes

Aus Wikibooks
Zur Navigation springen Zur Suche springen

Algorithmensammlung: Zahlentheorie

Sieb des Eratosthenes[Bearbeiten]

Die Spezifikation des Algorithmus in Pseudocode ist in der Wikipedia zu finden.

Prinzip[Bearbeiten]

Das Sieb des Eratosthenes ist ein Verfahren, um alle natürlichen Zahlen (ferner nur mit „Zahlen“ bezeichnet) bis zu einer vorgegebenen Zahl n, einschließlich n selbst, auf Primalität zu testen (zu Primzahlen siehe auch die Seite Primfaktorisierung und, sofern sie prim sind, auszugeben. Das Sieb des Eratosthenes "zäumt das Pferd von hinten auf": Es werden alle Zahlen bis n (inklusive) größer als 1 hintereinander aufgeschrieben. Dann werden die Zahlen der Reihe nach durchgegangen. Die echten Vielfachen der aktuellen Zahl (für 3 z. B. 6, 9, 12, ...) werden durchgestrichen. Übrig bleiben die Zahlen , die keine echten Vielfachen einer Zahl echt zwischen 1 und sind. Dies sind sämtliche Primzahlen bis zur vorgegebenen Grenze.

Beispiel[Bearbeiten]

Sei . Hintereinander werden die Zahlen 2, 3, 4, 5, 6, 7, 8, 9, 10 aufgeschrieben. Die 2 wird betrachtet und ihre echten Vielfachen weggestrichen: 2, 3, 4, 5, 6, 7, 8, 9, 10. Dasselbe für die 3: 2, 3, 4, 5, 6, 7, 8, 9, 10. Übrig sind noch 2, 3, 5 und 7. Dies sind, wie wir überprüfen können, alle Primzahlen im betrachteten Intervall, sodass hier abgebrochen werden kann. Schritte, um das Verfahren zu optimieren, damit der Algorithmus wirklich schon so früh endet, werden im nächsten Absatz besprochen.

Optimierungen eines simplen Durchgehens aller Zahlen und ihrer echten Vielfachen[Bearbeiten]

Es müssen nur die Zahlen bis einschließlich betrachtet werden, wobei die Abrundungsfunktion symbolisiert. Denn sei (Betrachte gerade und ungerade Zahlen zum Nachvollziehen). Dann ist kein echtes Vielfaches von x, das kleiner als n ist und somit für das weitere Vorgehen irrelevant.

Bereits gestrichener Zahlen müssen nicht mehr betrachtet werden, da ihre echten Vielfachen garantiert durch einen echten Teiler der gestrichenen Zahl teilbar sind.

Weiter optimieren kann man unter anderem, indem man durch ein Wegstreichen der echten Vielfachen einer Zahl p erst ab und einschließlich beginnt. Denn alle Vielfachen sind von der Form mit . Damit wurden sie aber als echte Vielfachen einer kleineren Zahl schon überprüft.

Komplexitätstheoretische Beschreibung[Bearbeiten]

Durch diese und weitere Optimierungen lässt sich die Laufzeit auf reduzieren. Beim Vergleich verschiedener Implementierungen und Varianten des Algorithmus ist die Ordnung der Laufzeit aber nur bedingt von Nutzen, da sie Konstanten außer Acht lässt. Bei diesem speziellen Problem wird der Sachverhalt bei einem Vergleich verschiedener Varianten auch bei großen n offensichtlich.

Die Speicherplatzkomplexität lässt sich auf drücken.


Implementierungen[Bearbeiten]

Haskell[Bearbeiten]

Ein einfaches Primzahlsieb in der Programmiersprache Haskell implementiert. Der Speicherbedarf des Algorithmus ist in Bits, wobei die Anzahl der auszugebenen Primzahlen und die größte auszugebene Primzahl bezeichnet.

> module Sieve
> where 

> sieb :: [Int] -> [Int]
> sieb (l:ls) = l:sieb[x | x <- ls, mod x l /= 0]

> take_primes n = take n $ sieb [2..]

Ausführen via

# hugs Sieve
oder für eine schnellere Version
# ghc -O2 --make Sieve.lhs
# ghci Sieve

Im Haskell-Interpeter zur Ausgabe der beispielsweise ersten 100 Primzahlen:

Prelude Sieve> take_primes 100

C#[Bearbeiten]

Folgende eigenständig lauffähige Konsolenanwendung (.NET 2.0) gibt alle Primzahlen bis n nach dem Sieb des Eratosthenes aus:

using System;
using System.Collections.Generic;
using System.Text;

namespace eratosthenes
{
    class Program
    {
        static void Main(string[] args)
        {
            int n = 100;
            bool[] prime = new bool[n];
            for (int i = 2; i < n; i++)
                prime[i] = true;
            
            {
                int i = 2;
                for (; i*i < n; i++)
                    if (prime[i])
                    {
                        Console.WriteLine(i);
                        for (int j = i * i; j < n; j = j + i)
                            prime[j] = false;
                    }
                for (; i < n; i++)
                    if (prime[i])
                    {
                        Console.WriteLine(i);
                    }
            }
            
            Console.ReadLine();
        }
    }
}

Delphi[Bearbeiten]

Folgende Konsolenanwendung gibt mit Hilfe der Funktion isPrim() von einer bestimmten Zahlenmenge die Primzahlen aus.

program Primzahl;

{$APPTYPE CONSOLE}

uses
  SysUtils;

function IsPrim(AInteger: Integer): boolean;
var
  i: Integer;
begin
  result := true;
  if (AInteger <= 1) then  //Wenn Zahl kleiner ist als 2: keine Primzahl
    begin
      result := false;
      exit;               //Funktion beenden
    end;
  for i := 2 to Round(Sqrt(AInteger)) do //Wenn eine Zahl n von 2 bis Wurzel(n)
    begin                                // nicht teilbar ist, ist sie eine Primzahl
      if AInteger mod i = 0 then         //mod: Modulo = Rest
        begin
          result := false;
          exit;
        end;
    end;
end;

var
  a, b: Integer;
  i: Integer;
begin
  Write('von: ');
  Readln(a);       //a abfragen
  Write('bis: ');
  Readln(b);       //b abfragen
  for i := a to b do
      if IsPrim (i) then Writeln(i);    //wenn i eine Primzahl ist: i ausgeben
  Readln;
end.

Python[Bearbeiten]

Theoretisch ein endloser Primzahl-Generator – nur durch die Hardware begrenzt –, aber langsamer als Siebe. Er nutzt den Satz von Wilson in umgestellter Form. Der Programmlauf wird durch Strg-C gestoppt.

#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys, time

def pgen():
  ''' ... generates the sequence of all primes: 2,3,5,7,11 ... 
  '''

  p=2
  f=1

  try:

    ts2=time.time()

    while(True):

      if f%p%2: sys.stdout.write(str(p)+"\n")
      p+=1
      f*=(p-2)

  except:

#   raise KeyboardInterrupt .. Ctrl-C

    ts2-=time.time()
    sys.stderr.write("time: "+str(round(-ts2,3))+" sec\n")

def main():
  pgen()
  sys.exit()

if __name__=="__main__":
  main()

Python 2[Bearbeiten]

Dieses Script bittet den Benutzer zuerst nach einer Obergrenze und gibt danach alle Primzahlen bis zu dieser Grenze aus.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

def main():
    
    # Frage den Benutzer nach einer Obergrenze bis zu der gesucht werden soll
    while True:
        try:
            obergrenze = int(raw_input('Bitte geben Sie eine Obergrenze ein: '))
            break
            
        except ValueError:
            print 'Dies ist keine gültige Obergrenze. Bitte verwenden Sie eine Ganzzahl!'
    
    # Jede Zahl zwischen 1 und obergrenze wird zuerst als prim angenommen
    zahlen = [True]*(obergrenze+1)
    # Da 0 und 1 keine Primzahlen sind, werden sie auf False gesetzt
    zahlen[0] = False
    zahlen[1] = False
    
    i = 2
    
    while i*i <= obergrenze:
        if zahlen[i] == True: # Die Zahl i ist als prim markiert
            # Streiche alle Vielfachen von i
            for k in range(i*i,obergrenze+1,i):
                zahlen[k] = False
        
        i = i+1
    
    # Ausgabe aller gefundenen Zahlen
    for i, v in enumerate(zahlen):
        if v == True:
            print i, 'ist prim.'
    
    return 0

if __name__ == '__main__':
    main()

C++[Bearbeiten]

Das Programm fragt zunächst nach einer Obergrenze und gibt dann alle Primzahlen bis zu dieser aus.

#include<iostream>
#include<cmath>
using namespace std;
 
int main(void){
    unsigned limit;
 
    cout << "Please insert upper limit.\n";
    cin >> limit;
 
    bool *Stroke = new bool[limit+1];
    Stroke[0]=1;
    Stroke[1]=1;
    for(unsigned i=2; i<=limit; ++i) Stroke[i] = 0;
 
    unsigned prime=2;
    while(pow((double)prime,2.0)<=limit){
        for(unsigned i = (int)pow((double)prime,2.0); i<=limit; i+=prime){
            Stroke[i]=1;
        }
 
        do ++prime; while(Stroke[prime]);
    }
 
    cout << "\nPrimes less or equal " << limit << " are:\n";
    for(unsigned i=0; i<=limit; ++i){
        if(!Stroke[i]) cout << i << endl;
    }
    delete[] Stroke;

    return 0;
}

R[Bearbeiten]

Limit heißt die Obergrenze, bis zu der alle Primzahlen ausgegeben werden.

sieb<-function(limit){
 (vek<-2:limit)[1==apply(0==outer(vek, vek, "%%"), 1, sum)]
   # Primzahlen bis limit
   # %% Division mit Rest oder modulo
}

Go[Bearbeiten]

Hier ein möglicher Quelltext eines Go-Programms zur Ausgabe der Primzahlen von 1-1000 (Der Bereich kann natürlich beliebig erweitert werden). Die Primzahlen werden dann in der Console ausgegeben. Dieses Programm kann auf der offiziellen Golang-Seite ausprobiert werden. Die Erklärungen sind identisch mit denen vom Java-Quellcode.

package main

import "math"

func main () {
	var limit uint = 1000
	var zahl, zaehler uint
	var primzahl bool
	
	for zahl = 2; zahl <= limit; zahl++ {
		
		primzahl = true
		
		for zaehler = 2; zaehler <= zahl/2; zaehler++ {
			if math.Mod(float64(zahl), float64(zaehler)) == 0 {
				primzahl = false
				break
			}
		}
		
		if primzahl == true {
			println(zahl, " ist eine Primzahl")
		}
	}
	
	
}