• Hi!

    Leute habe mich mit den Primzahlen bechäftigt !
    Als ich diese Thema gelesen habe dachte ich mir komm schau mal was du kanns wobei hir auch schon ein Wettbewerb gab!

    08. Januar 2010: In einer Gemeinschaftsaktion haben internationale Institute einen 768-Bit-Schlüssel geknackt. Schon bald wollen sie den 1024-Bit-Schlüssel auslesen. Experten warnen vor großen Sicherheitslücken.
    Die heute gebräuchlichen Schlüssel zur Sicherung etwa von Kreditkartennummern im Internet könnten nach Erkenntnissen von Forschern schon in einigen Jahren unsicher werden. Ein internationales Team unter Bonner Beteiligung hat jetzt einen 768 Bit langen Schlüssel geknackt. Das sei eine Zahl mit 232 Stellen und damit Weltrekord, teilte die Universität Bonn am Freitag mit.
    Damit sind die Forscher dem aktuell gängigen Schlüssel von 1024 Bit schon ein Stück näher gekommen. Die Forscher nutzten ein Computernetzwerk. Auf einem herkömmlichen PC hätte das Knacken dieses Schlüssels nach ihren Angaben rund 2.000 Jahre gedauert.
    Viele Verfahren zur Verschlüsselung sensibler Daten beruhen darauf, dass es äußerst schwierig ist, große Zahlen in ihre sogenannten Primfaktoren zu zerlegen. Primfaktoren sind diejenigen Primzahlen, die multipliziert die gesuchte Zahl ergeben. So hat etwa die Zahl 21 die Primfaktoren 3 und 7 (3 mal 7 gleich 21). Drei US-Forscher entwickelten 1977 ein Verfahren zur Datenverschlüsselung und nutzten es später auch kommerziell. Ihre nach ihren Initialen "RSA" genannte Technik steckt inzwischen in jedem Internet-Browser. Ein kleines Programm verschlüsselt dort etwa Kreditkartennummern so, dass böswillige Lauscher mit ihnen nichts anfangen können.
    Die jetzt geknackte Zahl trägt die nüchterne Bezeichnung RSA-768, das heißt, sie hat 768 Bit. In Dezimalschreibweise entspricht das 232 Stellen. Damit handelt es sich um das größte Zahlenungetüm von allgemeiner Form, das bislang in seine Primfaktoren zerlegt wurde. "Die Zerlegung eines 1024-Bit-Schlüssels wäre um drei Größenordnungen schwieriger als das jetzt abgeschlossene Projekt", sagte Prof. Jens Franke vom Institut für Mathematik der Universität Bonn. Dennoch werde der erste 1024-Bit-Schlüssel vermutlich noch vor Ende des Jahrzehnts geknackt.

    So da habe ich mich halt versucht, die Funktionen von Wettbewerb habe ich nicht schlagen können! :huh:
    Aber ich habe eine Möglichkeit gefunden Primzahlen zu Prüfen!

    Func: IsPrim

    Spoiler anzeigen
    [autoit]

    ; #FUNCTION# ;=============================================================================================
    ;
    ; Name...........: IsPrim($maxZahl)
    ; Description ...: Prüft ob eine Zahl eine Primzahl ist
    ; Syntax.........: $maxZahl = eine Zahl
    ; Parameters ....:
    ; Return values .: 1/ ist 0/keine Primzahl
    ;
    ;
    ; Author ........: Kleiner27 (http://www.AutoIT.de)
    ;
    ; ;=========================================================================================================

    [/autoit] [autoit][/autoit] [autoit]

    Func IsPrim($maxZahl)
    If ($maxZahl < 2) Then Return 0
    If ($maxZahl = 2) Or ($maxZahl = 3) Then Return 1
    If (Mod($maxZahl, 2)) * (Mod($maxZahl, 3)) > 0 Then
    For $i = 6 To Round(Sqrt($maxZahl) + 1) Step 6
    If (((Mod($maxZahl, ($i - 1))) * (Mod($maxZahl, ($i + 1)))) = 0) Then Return 0
    Next
    Return 1
    EndIf
    Return 0
    EndFunc ;==>IsPrim

    [/autoit]


    und noch IsPrim_2 die bei 1000000 rund 4 sec. braucht

    Spoiler anzeigen
    [autoit]

    ; #FUNCTION# ;=============================================================================================
    ;
    ; Name...........: IsPrim_2($Prim)
    ; Description ...: Prüft ob eine Zahl eine Primzahl ist
    ; Syntax.........: $Prim = eine Zahl
    ; Parameters ....:
    ; Return values .: True /False
    ;
    ;
    ; Author ........: Kleiner27 (http://www.AutoIT.de)
    ;
    ; ;=========================================================================================================
    Func IsPrim_2($Prim)
    Dim $Qu_W = Sqrt($Prim)
    Dim $p[$Prim + 1]
    Dim $lngP = 1
    Do
    Do
    $lngP += 1
    Until Not $p[$lngP]
    For $lngI = $lngP To $Prim Step $lngP
    $p[$lngI] = 1
    Next
    Until ($lngP > $Qu_W)
    Return Not $p[$Prim]
    EndFunc ;==>IsPrime

    [/autoit]


    Und die GetPrim die bei maxZahl ( 1299710) rund 12 Optimiert auf rund 6-6.5 sec. braucht

    Spoiler anzeigen
    [autoit]

    ; #FUNCTION# ;=============================================================================================
    ;
    ; Name...........: GetPrim($MaxZ, $Delim = ' ')
    ; Description ...: Listet die Primzahlen bis Max
    ; Syntax.........: $MaxZ = eine Zahl / $Delim Trennung der ausgabezahlen
    ; Parameters ....:
    ; Return values .: Alle Primzahlen bis Max
    ;
    ;
    ; Author ........: Kleiner27 (http://www.AutoIT.de)
    ;
    ; ;=========================================================================================================

    [/autoit] [autoit][/autoit] [autoit]

    Func GetPrim($MaxZ, $Delim = ' ')
    If ($MaxZ < 2) Then Return 0
    If ($MaxZ = 3) Then Return 2
    Dim $Flag[$MaxZ + 1], $Prim = 2 & $Delim
    For $i = 1 To $MaxZ Step 2
    $Flag[$i] = 1
    Next
    For $i = 3 To $MaxZ Step 2
    If ($Flag[$i]) Then
    For $e = ($i ^ 2) To $MaxZ Step $i
    $Flag[$e] = 0
    Next
    $Prim &= $i & $Delim
    EndIf
    Next
    Return $Prim
    EndFunc ;==>GetPrim

    [/autoit]

    LG Kleiner

    Einmal editiert, zuletzt von Kleiner (1. September 2010 um 10:14)

    • Offizieller Beitrag

    Um IsPrim zu prüfen nehme ich meine Primfaktorermittlung. Ist die Ausgabe = Eingabe, dann ist es Prim.
    Die Funktion ist zudem rattenschnell ;)

    Spoiler anzeigen
    [autoit]

    $zahl = 1000003
    If _GetPrimeFactors($zahl) = $zahl Then
    MsgBox(0, '', 'Ist Primzahl')
    Else
    MsgBox(0, '', 'Keine Primzahl')
    EndIf

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _GetPrimeFactors($n)
    Local $F = ObjCreate("System.Collections.ArrayList")
    While Mod($n,2) == 0
    $F.add(2)
    $n = $n/2
    WEnd
    While Mod($n,3) == 0
    $F.add(3)
    $n = $n/3
    WEnd
    Local $t = 5
    Local $diff = 2
    While $t*$t <= $n
    While Mod($n,$t) == 0
    $F.add($t)
    $n = $n/$t
    WEnd
    $t = $t + $diff
    $diff = 6 - $diff
    WEnd
    If $n > 1 Then $F.add($n)
    Local $out = ''
    For $element In $F
    $out &= $element & ','
    Next
    Return StringTrimRight($out, 1)
    EndFunc ;==>_GetPrimeFactors

    [/autoit]
  • Meine Funktion:

    Spoiler anzeigen
    [autoit]


    Func _IsPrimAsm($zahl)
    Local $txt="0x8B442404BB02000000BA0000000031D2F7F383FA0074248B44240489C34BBA0000000031D2F7F383FA00740F8B44240483FB0375E8B801000000C3B800000000C3"
    Local $tCodebuffer=dllstructcreate("byte["&stringlen($txt)/2-1&"]")
    dllstructsetdata($tCodeBuffer,1,$txt)
    Local $ret=DllCall("user32.dll", "", "CallWindowProcW", "int", DllStructGetPtr($tCodeBuffer), "int", $zahl, "int", 0, "int", 0, "int", 0)
    Return $ret[0]
    EndFunc

    [/autoit]

    Asmcode:

    Spoiler anzeigen
    [autoit]

    FasmAdd($Fasm, "mov eax,[esp+4]");parameter speichern
    FasmAdd($Fasm, "mov ebx,2");püfen ob durch 2 teilbar
    FasmAdd($Fasm, "mov edx,0");modulo vorbereiten
    FasmAdd($Fasm, "xor edx,edx");"
    FasmAdd($Fasm, "div ebx");durchführen
    FasmAdd($Fasm, "cmp edx,0");prüfen ob es durch 2 teilbar ist
    FasmAdd($Fasm, "je ende1");wenn das so ist dann springe zum ende (keine primzahl)
    FasmAdd($Fasm, "mov eax,[esp+4]");eax wurde geändert, wieder auf parameter setzen
    FasmAdd($Fasm, "mov ebx,eax");zähler ebx (es wird durch ebx geteilt)
    FasmAdd($Fasm, "for:");schleifenlabel
    FasmAdd($Fasm, "dec ebx");zähler dekrementieren
    FasmAdd($Fasm, "mov edx,0");modulo vorbereiten
    FasmAdd($Fasm, "xor edx,edx");"
    FasmAdd($Fasm, "div ebx");durchführen
    FasmAdd($Fasm, "cmp edx,0");und auswerten
    FasmAdd($Fasm, "je ende1");keine primzahl
    FasmAdd($Fasm, "mov eax,[esp+4]");eax wieder auf den richtigen wert setzen
    FasmAdd($Fasm, "cmp ebx,3");prüfen ob zahl durch die geteilt wird 3 ist (2 hatten wir oben schon)
    FasmAdd($Fasm, "jne for");sonst schleife wiederholen

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    FasmAdd($Fasm, "mov eax,1");ende: ist primzahl
    FasmAdd($Fasm, "ret 4");ende

    [/autoit] [autoit][/autoit] [autoit]

    FasmAdd($Fasm, "ende1:");label für ende: keine primzahl
    FasmAdd($Fasm, "mov eax,0");keine primzahl
    FasmAdd($Fasm, "ret 4");ende

    [/autoit]

    Ist bei 1.000.000 0,069ms bei 1.000.001 4,382ms, keine Sekunden. Bei 2.000.000.001 sind es 5,584 Sekunden :P

    EDIT:
    ca. 2GHz

  • Hi!

    nuts
    Den Post habe ich wohl übersehen :huh:
    Mein test´s: Primzahl(9999991) * 10000 (Durchgängen)

    Spoiler anzeigen
    [autoit]

    Prüfung auf Prim _GetPrimeFactors(9999991) * 10000 Durchgänge : 9.6828 msc
    Prüfung auf Prim IsPrime2(9999991) * 10000 Durchgänge : 4.5376 msc
    Prüfung auf Prim IsPrim(9999991) * 10000 Durchgänge : 3.7693 msc

    [/autoit]

    TheShadowAE
    Wie arbeitet man mit deiner Func?

    LG Kleiner

  • @Shadow, sehr nice, aber du verschenkst extrem viel Zeit bei unnötigen Berechnungen :D
    Eigentlich musst du nur die ungeraden Zahlen bis zur Wurzel der vermeintlichen Primzahl untersuchen.
    Weiterhin müsste eine Abfrage abfangen, ob die eingegebene Zahl > 2^31 ist, denn bei deiner Funktion dürfen (siehe DIV) nur vorzeichenbehaftete Integer (int) als Primzahlen verwendet werden!

    Spoiler anzeigen
    [autoit]

    _("use32")
    _("mov esi,[esp+4]");parameter speichern -->eax
    _("mov ebx,2");püfen ob durch 2 teilbar
    _("mov eax,esi");wenn es geht, immer mit registern statt mit speicher arbeiten!
    _("xor edx,edx");modulo vorbereiten
    _("div ebx");durchführen
    _("cmp edx,0");prüfen ob es durch 2 teilbar ist
    _("je ende1");wenn das so ist dann springe zum ende (keine primzahl)

    [/autoit] [autoit][/autoit] [autoit]

    _("fild dword[esp+4]") ;integerzahl laden 32 bit in ST0
    _("fsqrt") ;wurzel aus ST0
    _("fistp dword[esp+4]") ;wurzel speichern
    _("mov ebx,[esp+4]");zähler ebx (es wird durch ebx geteilt)
    ;falls int(wurzel) =gerade zahl, dann um 1 erhöhen
    _("shr ebx,1") ;/2
    _("inc ebx") ;+1
    _("shl ebx,1") ;*2
    _("inc ebx") ;+1 immer ungerade Zahl! eins größer als wurzel(zahl)

    [/autoit] [autoit][/autoit] [autoit]

    _("for:");schleifenlabel
    _("sub ebx,2");zähler dekrementieren, nur ungerade teiler ;)
    _("xor edx,edx")
    _("div ebx");durchführen
    _("cmp edx,0");und auswerten
    _("je ende1");keine primzahl
    _("mov eax,esi");eax wieder auf den richtigen wert setzen
    _("cmp ebx,3");prüfen ob zahl durch die geteilt wird 3 ist (2 hatten wir oben schon)
    _("jne for");sonst schleife wiederholen

    [/autoit] [autoit][/autoit] [autoit]

    _("mov eax,1");ende: ist primzahl
    _("ret 4");ende

    [/autoit] [autoit][/autoit] [autoit]

    _("ende1:");label für ende: keine primzahl
    _("mov eax,ebx");keine primzahl eax gibt den teiler zurück
    _("ret 4");ende

    [/autoit] [autoit][/autoit] [autoit]

    Func _($str) ; Nur für die bequemlichkeit ^^
    Fasmadd($Fasm, $str)
    EndFunc ;==>_

    [/autoit]

    habe mal dein Programm mit Wurzel und nur den ungeraden Teilern erweitert

    ciao
    Andy


    "Schlechtes Benehmen halten die Leute doch nur deswegen für eine Art Vorrecht, weil keiner ihnen aufs Maul haut." Klaus Kinski
    "Hint: Write comments after each line. So you can (better) see what your program does and what it not does. And we can see what you're thinking what your program does and we can point to the missunderstandings." A-Jay

    Wie man Fragen richtig stellt... Tutorial: Wie man Script-Fehler findet und beseitigt...X-Y-Problem

    Einmal editiert, zuletzt von Andy (1. September 2010 um 02:29)

  • Ich hab auch mal was gemacht aber es ist ARRRSCHH lahm

    Spoiler anzeigen
    [autoit]

    #include <Array.au3>

    [/autoit] [autoit][/autoit] [autoit]

    $CalulateTo = 1000000

    [/autoit] [autoit][/autoit] [autoit]

    Dim $aPrims[$CalulateTo] = [1]

    [/autoit] [autoit][/autoit] [autoit]

    $Timer = TimerInit()
    Sieb($CalulateTo)
    ReDim $aPrims[$aPrims[0]]
    _ArraySort($aPrims, 0, 2)
    ConsoleWrite(TimerDiff($Timer) & @CRLF)
    _ArrayDisplay($aPrims)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func Sieb($CalulateTo)
    Local $Sqrt, $Match = False, $Teiler
    $Sqrt = Int(Sqrt($CalulateTo))
    $aPrims[1] = $Sqrt
    For $x = 11 To $CalulateTo
    If Not Mod($x, 2) Then ContinueLoop
    For $Teiler = 2 To $Sqrt
    If Not Mod($x, $Teiler) Then
    $Match = True
    ExitLoop
    EndIf
    Next
    If Not $Match Then
    $aPrims[0] += 1
    $aPrims[$aPrims[0]] = $x
    EndIf
    $Match = False
    Next

    [/autoit] [autoit][/autoit] [autoit]

    If $aPrims[1] > 11 Then Sieb($aPrims[1])
    EndFunc ;==>Sieb

    [/autoit]

    Die Primzahlen fangen erst bei [2] an

  • Hi Sprenger120!


    Das Sieb habe ich schon durch ist wirklich zu langsam durch IF und Mod abfragen!
    Eine IF abfrage kostet in manchen momenten bis zu 1 sec!


    LG Kleiner


  • Warum klappt das denn ohne finit, weil man keine floats benutzt? 8|

  • @Shadow, FINIT sollte man benutzen, wenn man den Prozessor und die Register in einen definierten Ausgangsstatus bringen muss. Um mal eben ein Register zu benutzen ist das nicht nötig. Wenn man viel und oft mit den Float-Registern arbeitet, dann ist es natürlich sinnvoll, vorher alles zurückzusetzen. Dann kann einem nicht "aus Versehen" eins der Register mit Werten dazwischenpfuschen, die man garnicht erwartet.

    Wie du sicher schon gemerkt hast, ist der DIV ein sehr "teurer" Befehl, er kostet so um die 70-80 Takte, also ist es immens wichtig,so viele Divisionen wie möglich einzusparen.
    Da die Sprungvorhersage "Branch Prediction" des Prozessors bei jedem Durchlauf in deiner inneren Schleife (dem "inner Loop") zu 50% falsch liegt (wird die Schleife verlassen oder nicht) macht man das sogenannte "loop unrolling". Das heisst, die Schleife geht beispielsweise nicht

    Code
    for i=1 to 100
    a=a+1
    next


    sondern

    Code
    for i=1 to 25
    a=a+1
    a=a+1
    a=a+1
    a=a+1
    next

    Berechnen muss man zwar jedes Element weiterhin, aber da der Prozessor die nächsten Befehle ALLE sicher kennt, kann er diese Befehle optimal in seine Pipelines einsortieren. Das erhöht die "Schwuppdizität" :D

  • ...eine "grafische" Lösung zur Ermittlung der Primzahlen^^
    Dauert zwar bissl, aber dafür hat man am Ende eine feine Bitmap mit allen Primzahlen.(Maus bewegen nicht vergessen^^)
    Da man (Monochrom)-Bitmaps in nahezu jeder Größe speichern kann, hat man so immer eine "Primzahltabelle" dabei.
    Ein "_isprime()" reduziert sich somit auf ein "Pixelgetcolor"

    Spoiler anzeigen
    [autoit]

    #include <WinAPI.au3>
    #include <String.au3>
    #include <Misc.au3>
    ;Sieb des Eratosthenes, grafisch
    AutoItSetOption("PixelCoordMode", 2)
    AutoItSetOption("MouseCoordMode", 2)

    [/autoit] [autoit][/autoit] [autoit]

    $gdi32 = DllOpen("gdi32.dll")
    $u32_dll = DllOpen("user32.dll")

    [/autoit] [autoit][/autoit] [autoit]

    ;maximale Fenstergrösse ermitteln und so einstellen, daß ein "Primzahlmuster" entsteht
    $VirtualDesktopWidth = DllCall($u32_dll, "int", "GetSystemMetrics", "int", 78) ;sm_virtualwidth
    $Width = ($VirtualDesktopWidth[0] - Mod($VirtualDesktopWidth[0], 210)) - 210 ;210 = 2*3*5*7 das Produkt der ersten 4 primzahlen
    $VirtualDesktopHeight = DllCall($u32_dll, "int", "GetSystemMetrics", "int", 79) ;sm_virtualheight
    $Height = $VirtualDesktopHeight[0] - 100

    [/autoit] [autoit][/autoit] [autoit]

    $maximum = $Width * $Height ;maximale Größe der Primzahl

    [/autoit] [autoit][/autoit] [autoit]

    $hgui = GUICreate("", $Width, $Height, 0, 0)
    $hwnd = WinGetHandle($hgui) ;Handle der Gui, braucht man für pixelgetcolor()
    $hdc = _WinAPI_GetDC($hwnd) ;Devicecontext, braucht man für _Pixelsetcolor()
    $pic = GUICtrlCreateGraphic(0, 0, $Width, $Height) ;die "Grafik"
    GUICtrlSetBkColor($pic, 0) ;Hintergrundfarbe schwarz
    GUISetState()

    [/autoit] [autoit][/autoit] [autoit]

    ;alle schwarzen Pixel der Grafik sind Primzahlen, also müssen alle Vielfachen der gefundenen Primzahlen
    ;mit einer Farbe markiert werden. 0 und 1 sind per Definition keine Primzahlen,
    ;also werden diese Pixel in der Grafik weiß markiert
    _pixelsetcolor($hdc, 0, 0, 0xFFFFFF) ;0 ist per Definition keine Primzahl
    _pixelsetcolor($hdc, 1, 0, 0xFFFFFF) ;1 ist per Definition keine Primzahl
    $Anzahlprim = 1 ;Anzahl der Primzahlen
    $flag = 1 ;wenn Sieb gefüllt ist, wird $flag =0, dann sind alle Primzahlen im Sieb gefunden
    $action = " Eliminieren der Vielfachen von "

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 2 To $maximum ;alle Zahlen ab 2 auf Prim testen
    $y = Int($i / $Width) ;y-koordinate des Pixels
    $x = $i - $Width * $y ;x-koordinate des Pixels
    If $flag = 0 Then ;wenn das Sieb gefüllt ist , werden die Primzahlen unter dem Mauszeiger angezeigt
    $mauspos = MouseGetPos()
    If $mauspos[0] > 0 And $mauspos[1] > 0 And $mauspos[0] < $Width And $mauspos[1] < $Height and PixelGetColor($mauspos[0] - 1, $mauspos[1] - 1, $hwnd) <> 0xFFFFFF Then
    ToolTip($mauspos[0] - 1 + $Height * ($mauspos[1] - 1)& " ist eine Primzahl")
    endif
    EndIf
    If PixelGetColor($x, $y, $hwnd) <> 0 Then ContinueLoop ;falls pixelfarbe<>0, dann ist es keine Primzahl, da als Vielfache mit einer Farbe markiert
    _pixelsetcolor($hdc, $x, $y, 0x0000FF) ;primzahlen rot markieren, um die aktuelle Position im Sieb zu zeigen
    WinSetTitle($hgui, "", "Sieb des Eratosthenes, Aktuelle Primzahl: " & $i & " Anzahl Primzahlen bisher: " & $Anzahlprim & $action & $i)
    $Anzahlprim = $Anzahlprim + 1 ;Anzahl der gefundenen Primzahlen +1
    If $i > Sqrt($maximum) And $flag Then ;wenn die Wurzel des Maximums überschritten ist, dann ist das Sieb gefüllt!
    $flag = 0 ;ab nun können unter dem Mauszeiger die Primzahlen angezeigt werden
    $action = " Primzahlen rot markieren: "
    WinSetTitle($hgui, "", "Das Sieb ist gefüllt, alle weißen Pixel sind Vielfache einer Primzahl. Alle schwarzen Pixel sind keine Vielfachen einer Primzahl, also sind sie Primzahlen! Leertaste drücken...")
    Do
    Sleep(50)
    Until _IsPressed("20") ;warten, bis Leertaste gedrückt wurde
    EndIf
    For $prim = $i * $i To $maximum Step $i ;alle vielfachen der Primzahl eliminieren
    $y = Int($prim / $Width) ;y-koordinate des Pixels
    $x = $prim - $Width * $y ;x-koordinate des Pixels
    _pixelsetcolor($hdc, $x, $y, 0xFFFFFF) ;vielfache werden markiert mit weißer Farbe
    Next
    Next

    [/autoit] [autoit][/autoit] [autoit]

    MsgBox(0, "Primzahlen", "Man könnte dieses Sieb als Bitmap speichern und als Sieb für die Ermittlung der Primzahlen bis " & $maximum ^ 2 & " verwenden!")
    Exit

    [/autoit] [autoit][/autoit] [autoit]

    Func _pixelsetcolor($dc, $x, $y, $color) ;pixel mit color an koordinaten setzen
    DllCall($gdi32, "long", "SetPixel", "long", $dc, "long", $x, "long", $y, "long", $color)
    EndFunc ;==>_pixelsetcolor

    [/autoit]


    Man könnte die Datei ja so anlegen, daß die gesetzten Bits die Primzahlen ergeben....Frage an die Gemeinde, wie ist die größte "Primzahl" bzw wie viele Primzahlen sind es, die sich so auf einer 1 Terabyte-Platte darstellen lassen würden?^^

    ciao
    Andy


    "Schlechtes Benehmen halten die Leute doch nur deswegen für eine Art Vorrecht, weil keiner ihnen aufs Maul haut." Klaus Kinski
    "Hint: Write comments after each line. So you can (better) see what your program does and what it not does. And we can see what you're thinking what your program does and we can point to the missunderstandings." A-Jay

    Wie man Fragen richtig stellt... Tutorial: Wie man Script-Fehler findet und beseitigt...X-Y-Problem

    2 Mal editiert, zuletzt von Andy (1. September 2010 um 21:14)

  • Nunja, 1 Terabyte = 10^12 Byte = 1.000.000.000.000 Byte * 8 Bit / Byte = 8*10^12 Bit.
    Im Prinzip müsste man also nur noch herausfinden, welche Primzahl in der Nähe von 8*10^12 ist. Bis 10^12 gibts eine Liste der Primzahlen übrigens hier
    Die Wurzel von 8e12 ist irgendwo bei 2,83 Millionen, die Primzahlen bis dorthin bekommt man auch schon mit AutoIt in relativ kurzer Zeit heraus...
    Und dann gibts ja noch die BIGINT.UDF, mit der man dann einfach die möglichen Primzahlen kleiner8*10^12 auf Prim prüft :D

    Im Primzahl-µIt hatte ich ein Verfahren vorgestellt, nicht jede Zahl in der Liste zu speichern, sondern die Vielfachen von 2,3 und 5 zu eliminieren. Das spart ca. 3/4 Speicherplatz! Wenn man sich die grafische Darstellung der Primzahlen im oben vorgestellten Script anschaut, wäre das die Eliminierung der "weissen senkrechten Balken". Denn das sind alles Vielfache von 2,3 oder 5.

    Spoiler anzeigen
    [autoit]

    #include <String.au3>
    #include <Array.au3>
    ;Sieb von Eratosthenes, komprimiert
    ;
    Global $z
    Global $limit = 1299709 ;alle Primzahlen bis zum Limit finden

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    $arraysize = (Int($limit / 30) + 1) * 8 ;Array zur Aufnahme der möglichen Primzahlen, alle Vielfachen von 2,3 und 5 werden nicht berücksichtigt
    Dim $a[$arraysize]
    ;der Vorteil ist, man braucht nur 1/4 des Speicherplatzes für das Sieb

    [/autoit] [autoit][/autoit] [autoit]

    ;es werden in diesem komprimierten Sieb alle Vielfachen von 2,3 und 5 nicht berücksichtigt, daraus ergibt sich eine "Matrix" mit möglichen Primzahlen
    ;Zahlen Spalte
    ; 0 1 2 3 4 5 6 7
    ;Zeile
    ;0 1 7 11 13 17 19 23 29 in dieser Zeile stehen die Spaltenindizes
    ;1 31 37 41 43 47 49 53 59
    ;2 61 67 71 73 77 79 83 89
    ;3 91 97
    ; Formel zur Berechnung der möglichen Primzahlen:
    ; Prim = Zeilenanzahl * 30 + Spaltenindex Spaltenindex(0)=1 usw...
    Dim $spaltenindex[8] ;
    $spaltenindex[0] = 1
    $spaltenindex[1] = 7
    $spaltenindex[2] = 11
    $spaltenindex[3] = 13
    $spaltenindex[4] = 17
    $spaltenindex[5] = 19
    $spaltenindex[6] = 23
    $spaltenindex[7] = 29

    [/autoit] [autoit][/autoit] [autoit]

    ;dann folgt ein Array, in dem die Spaltenindizes(+1) den Primzahlen von 0-30 zugeordnet werden
    Dim $b[31] = [0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 3, 0, 4, 0, 0, 0, 5, 0, 6, 0, 0, 0, 7, 0, 0, 0, 0, 0, 8, 0]
    ;jede Vielfache der Primzahl soll aus der Matrix eliminiert werden, in der Matrix stehen aber nur Vielfache der Primen von 7 bis 29
    ;also wird nur die Zahl aus der Matrix eliminiert, bei der gilt:
    ;$b[Rest] <> 0 wobei Rest = mod ( Vielfaches der Prim , 30 )
    ;
    ;die Vielfachen der Primzahlen werden eliminiert, indem an ihre Position in der Matrix eine Zahl (zur Verdeutlichung das Vielfache) geschrieben wird
    ;Ist das Sieb fertiggestellt, sind die Arrayelemente mit 0 die Primzahlen, alle anderen sind eliminierte Vielfache

    [/autoit] [autoit][/autoit] [autoit]

    $tt = TimerInit()

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    $a[1]=1
    $flag = 0
    $primstring = ""

    [/autoit] [autoit][/autoit] [autoit]

    For $x = 0 To $arraysize ;Anzahl aller möglichen Primzahlen in der Matrix
    For $y = 0 To 7 ;jedes Element in der Zeile
    If $a[$y + 1 + ($x * 8)]<>0 Then ContinueLoop ;nur Elemente mit 0 sind Primzahlen
    $prim = $x * 30 + $spaltenindex[$y] ;das ist die Primzahl
    $qprim = $prim ^ 2 ;alle Vielfachen ab dem Quadrat dieser Primzahl eliminieren...
    If $qprim > $limit Then ExitLoop 2 ;wenn Quadrat der Primzahl > Wurzel aus limit, dann besteht das sieb nur noch aus Primzahlen
    $r = Mod($qprim, 30) ;mod der Zahl liefert den Rest
    $d = Int($qprim / 30) ;int/30 liefert die Zeile der Zahl
    If $b[$r] <> 0 Then ;wenn Zahl ein Vielfaches eines der Spaltenindizes, dann alle weiteren Vielfachen eliminieren
    $a[$b[$r] + ($d * 8)] = $prim ;Im Arraydisplay sieht man sehr schön die eliminierten Vielfachen
    $t=timerinit()
    $w=0
    For $m = $qprim To $limit Step $prim ;alle Vielfachen aus der Matrix eliminieren
    $r = Mod($m, 30)
    $d = Int($m / 30)
    If $b[$r] <> 0 Then
    $a[$b[$r] + ($d * 8)] = $prim
    EndIf
    $w+=1 ;anzahl eliminierter Zahlen = Vielfache der Prim
    Next
    $m=timerdiff($t)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $m = ' &$prim&" "&$w&" "& $m & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console
    endif
    Next
    Next

    [/autoit] [autoit][/autoit] [autoit]

    $m = TimerDiff($tt)
    Msgbox(0,"Komprimiertes Sieb von Eratosthenes"," Primzahlen in "&int($m)&" Millisekunden!" & @CRLF &"Bitte im Anschluss kurz auf das Arraydisplay warten...."&@crlf&"Im Arraydisplay sind in der zweiten Spalte alle leeren Felder Primzahlen, ansonsten steht in der 2. Spalte ein Teiler der Zahl in der linken Spalte")
    _ArrayDisplay($a)
    $primstring = "2" & @CRLF & "3" & @CRLF & "5" & @CRLF
    $n = 3
    For $i = 1 To $arraysize - 2
    If $a[$i + 1] = 0 Then ;Primzahl im Sieb gefunden
    $r = Mod($i, 8)
    $d = Int($i / 8)
    $n += 1
    $primstring &= $d * 30 + $spaltenindex[$r] & " " & $n & @CR ;Ausgabe der Primzahlen
    EndIf
    Next
    ConsoleWrite($primstring)
    Exit

    [/autoit]

    Um also eine Zahl auf Prim zu prüfen reicht es, von der Zahl die 8 Spaltenindizes 1,7,11,13,17,19,23,29 zu subtrahieren und das Ergebnis durch 30 zu teilen. Ist dieses Ergebnis in allen Fällen <>0, dann ist diese Zahl nicht in der "Tabelle" enthalten, also definitiv nicht prim.
    Man kann aber noch weiter vereinfachen!
    Die "Tabelle" der möglichen Primzahlen sieht ja so aus:

    Code
    ;Zahlen		Spalte;			0	1	2	3	4	5	6	7;Zeile;0			1	7	11	13	17	19	23	29   in dieser Zeile stehen die Spaltenindizes;1			31	37	41	43	47	49	53	59;2			61	67	71	73	77	79	83	89;3			91	97;  Formel zur Berechnung der möglichen Primzahlen:; Prim = Zeilenanzahl * 30 + Spaltenindex    Spaltenindex(0)=1 usw...

    Die letzte Ziffer der möglichen Primzahl ist immer identisch mit der letzten Ziffer der Zahl in der Zeile 0.
    Wenn man also Zahl=1234557 auf Prim prüfen würde, müsste man nur zwei Tests durchführen, um zu ermitteln, ob diese Zahl überhaupt eine Primzahl sein kann!
    If mod(Zahl-7,30)<>0 then keine_primzahl
    If mod(Zahl-17,30)<>0 then keine_primzahl
    1234557 ist somit keine Primzahl!

    Ist die Zahl also in der "Tabelle" enthalten, könnte es eine Primzahl sein, und es muss nur noch durch die in der Tabelle nicht vielfachen (also den Primzahlen) bis zur Wurzel der Zahl geteilt werden. Dieses Verfahren reduziert die Anzahl der Berechnungen enorm!

    Da eine "Zeile" der Tabelle genau 8 Zahlen enthält, könnte man ein komprimiertes Sieb erstellen, bei dem 1 Bit in einem Byte festlegt, ob die Zahl eine Primzahl ist oder nicht ;)
    Byte1 = "00000000" ;erste Zeile alle Primzahlen 1,7,11,13,17,19,23,29
    Byte2 = "00000100" ;49 ist keine Primzahl
    Byte3 = "00001000" ;77 ist keine Primzahl
    uswusf. (die Bits kann man setzen wie man möchte, ob "regulär" von rechts nach links oder von links nach rechts(dann entspricht es der Tabelle) ist schnurz...)

    Wer jetzt Lust bekommen hat, kann ja die Tabelle in Bytes packen und sich ein komprimiertes Sieb "auf Platte" legen.
    Dann muss man, um festzustellen, ob eine Zahl eine Primzahl ist nur noch das Bit an der Position
    Byte = int(Zahl/30)
    Bit = $b[ mod(zahl,30)-1] ;$b=[0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 3, 0, 4, 0, 0, 0, 5, 0, 6, 0, 0, 0, 7, 0, 0, 0, 0, 0, 8, 0]
    testen...
    Falls Bit=-1, ist die Zahl keine Primzahl.
    Und das kostet nicht mal Speicher, denn mit FileSetPos() zieht man sich nur das eine zu testende Byte aus der Datei!
    Um z.B. eine zufällige Primzahl zu erzeugen, liest man ein zufällliges Byte aus der Datei. Ist dieses Byte<>0xFF ( 11111111 heisst ja, keine Primzahl enthalten), dann das nächste 0-Bit im Byte suchen und die zufällige Primzahl ist gefunden.