Einmal das Consolewrite rausnehmen, aber vor allem:
Kein _ArrayDelete. Stattdessen einfach nen Wert wie -1 eintragen, der nie erreicht werden kann. Beim ArrayDelete wird im Hintergrund jedesmal ein neues Array erzeugt, mit den alten Daten beschrieben, und wieder zurückgegeben. Das dauert.
Wenn du -1 eingetragen hast: einfach nach der Größe sortieren und dann mit ReDim alle restlichen "abschneiden". Dabei wird dann nur 1x das Array neu erzeugt. Musst dann nur ne ausnahmeregel für -1 einbauen.
primzahlenrechner
-
- [ offen ]
-
GegX -
23. März 2015 um 17:33 -
Erledigt
-
-
- Offizieller Beitrag
und dann mit ReDim alle restlichen "abschneiden"
Wenn du ReDim verwendest kannst du auch gleich bei ArrayDelete bleiben, macht keinen Unterschied. Einfacher: Index führen für letztes Element.
-
Naja... ob du jeden schleifendurchlauf _arraydelete nutzt, oder ob du einmal nach der schleife sortierst und dann redim nutzt macht eigentlich nen ziemlich großen unterschied^^.
Aber nen Index macht natürlich auch sinn -
Hey ProgrammingDonkey!
Ich bin zu faul um mich hinter deinen Gedanken zu deinem Skript einzulesen, das eine oder andere Kommentar hätte sicherlich geholfen. Was mir jedoch konkret ins Auge springt ist das _ArrayDelete(). Bei jedem Aufruf wird das Array neu dimensioniert, das kostet viel Zeit. Um Optimierungen vorzunehmen muss man erst einmal genau wissen was man möchte. Bei dieser Primzahlen-Geschichte kann man bereits mathematische Gesetze ausnutzen. So wie ich das sehe tust du das nur bedingt, da kann man tatsächlich viel mehr rausholen als wie es den Anschein hat. Du hattest mich um eine Erklärung zu meinen Skript gebeten, um das allerdings zu verstehen muss der Gedanke dahinter verstanden werden. Ich versuche es mal zu erklären, eine Optimierung deines Codes kannst du anhand des gewonnenen Wissens dann selber vornehmen.Mein Algorithmus tut eigentlich nur folgendes:
1. Es wird eine Liste angelegt von 2 bis N Elementen (in Form eines 1D Arrays)
2. Es wird die kleinste Zahl aus der Liste gesucht welche noch nicht „durchgestrichen“ ist, dies muss eine Primzahl sein.
3. Alle Vielfachen der Gefunden Zahl werden „durchgestrichen“.
4. Gehe wieder zu Schritt 2 und wiederhole die Schritte bis das Ende der Liste erreicht wurde.
5. Alle nicht durchgestrichenen Zahlen sind Primzahlen.Anders gesagt arbeitet dieser nach dem Sieb des Eratosthenes. Eine einfache Implementierung würde folgendermaßen aussehen:
Spoiler anzeigen
[autoit]#include <Array.au3>
[/autoit] [autoit][/autoit] [autoit]Dim $limit = 100
[/autoit] [autoit][/autoit] [autoit]
Dim $crosslimit = Sqrt($limit) ; Erklärung folgt...
Dim $sieve[$limit +1] = [True, True] ; 0 und 1 sind keine Primzahlen; Alle geraden Zahlen werden "durchgestrichen", sind ja Vielfachen von 2.
[/autoit] [autoit][/autoit] [autoit]
For $n = 4 To $limit Step 2
$sieve[$n] = True
Next; Wir können in Zweierschritten vorgehen, da alle geraden Zahlen keine Primzahlen sein können.
[/autoit] [autoit][/autoit] [autoit]
For $n = 3 To $crosslimit Step 2
If Not $sieve[$n] Then ; Wenn die Zahl noch nicht durchgestrichen wurde...; Auch hier folgt noch eine Erklärung...
[/autoit] [autoit][/autoit] [autoit]
For $m = $n ^ 2 To $limit Step 2 * $n
$sieve[$m] = True
Next
EndIf
Next; Alle durchgestrichenen Zahlen sind mit "True" markiert, alle anderen sind demnach Primzahlen.
[/autoit]
_ArrayDisplay($sieve)Wenn wir uns mal Zeile 4 anschauen, so sehen wir folgende Rechnung:Sqrt($limit). Es ist so, dass nur alle Zahlen bis zu der Quadratwurzel der Endzahl abgeklappert werden müssen. Bei 100 Primzahlen müssen also nur alle Vielfachen von 2 bis 10 tatsächlich durchgestrichen werden. Alle anderen Zahlen zu überprüfen und deren Vielfache durchzustreichen wäre lediglich nur Zeitverschwendung. Dies liegt daran dass wir jede Zahl in ihre Primfaktoren zerlegen können. Das bedeutet, dass die Zahl eine Vielfache des Primfaktor ist. Und maximal 1 Primfaktor (und das muss nicht mal unbedingt so sein) kann größer sein als die Quadratwurzel jener Zahl. Alle anderen Primfaktoren liegen damit unter der Quadratwurzel. Ein Beispiel wird dies nochmal verdeutlichen:
Nehmen wir an wir wollen alle Primzahlen bis 1000 ermitteln. Die Quadratwurzel dieser Zahl ist 31,62. Wenn wir die Zahl 1000 in ihre Primfaktoren zerlegen erhalten wir die Faktoren 2^3 * 2^5. Das bedeutet dass 1000 die Vielfache von 2 und 5 ist. Nun zerlegen wir die 999: 3^3 * 37 Wir sehen dass ein Faktor über der Quadratwurzel von 31,62 liegt. Ist aber soweit nicht tragisch da der Faktor 3 noch darunter liegt. Streichen wir also alle Vielfachen von 3 weg, so ist auch die 999 durchgestrichen. Dies kann man jetzt für jede Zahl fortführen, das System geht auf. Deshalb brauchen wir nur alle Zahlen von 2 bis Sqrt(n) durchlaufen da sowieso alle Vielfachen weggestrichen werden. Ich hoffe es ist so verständlich.
Schauen wir uns jetzt noch Zeile 17 an: For $m = $n ^ 2 To $limit Step 2 * $n
Wir starten bei $n ^ 2. Warum ist das allerdings so? Potenzieren wir unsere Startzahl (meinetwegen die 3) so stellen wir fest dass die 6 dabei übersprungen wird. Durch das Step 2 * $n werden auch die 12, 18 und 24 übersprungen. Bei der 5 wären dies die Zahlen 10, 15, 20, 30, 40 usw. Wir sehen also, dass (beinahe) nur gerade Zahlen übersprungen werden, die Schleifendurchläufe werden weniger und so brauchen wir weniger Zeit. Was ist aber nun mit der 15? Nun ja, die 15 wird durch die 3 (da diese dort nicht übersprungen wird) durchgestrichen, dies verhält sich bei allen Zahlen so. Die kleineren Zahlen streichen jene Zahlen durch, welche die größeren überspringen. Zeit wird so aber definitiv eingespart.Nun ist der Algorithmus aber noch nicht perfekt da es noch sehr lange dauert bis alle Primzahlen ermittelt wurden. Bei 1'000'000 als Limit braucht dieses Verfahren ca. 9 Sekunden bei mir. Da geht aber noch mehr.
Eine weitere Erklärung zu dem Rest folgt noch
Gerade keine Lust mehr zu tippen -
Bevor ich es vergesse, hier mal eine andere Version des Algo in Beitrag #18.
Könnt ihr mal eure Ergebnisse posten? Bei mir braucht das mit den String Funktionen etwas unter einer Sekunde.Spoiler anzeigen
[autoit]; ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +
[/autoit] [autoit][/autoit] [autoit]#include <Array.au3>
[/autoit] [autoit][/autoit] [autoit]Global Const $hTime = TimerInit()
[/autoit] [autoit][/autoit] [autoit]
Global Const $aPrimes = SieveOfEratosthenes(1000000)
ConsoleWrite(Round(TimerDiff($hTime) / 1000, 3) & ' Sekunden' & @CRLF)
_ArrayDisplay($aPrimes); ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +
[/autoit] [autoit][/autoit] [autoit]Func SieveOfEratosthenes($limit)
[/autoit] [autoit][/autoit] [autoit]
Local $sievebound = ($limit -1) / 2
Local $sieve[$sievebound +1]
Local $crosslimit = (Sqrt($limit) -1) / 2
Local $primes = '2|'
Local $i, $jFor $i = 1 To $crosslimit
[/autoit] [autoit][/autoit] [autoit]
If Not $sieve[$i] Then
For $j = 2 * $i * ($i +1) To $sievebound Step 2 * $i +1
$sieve[$j] = True
Next
EndIf
NextFor $i = 1 To $sievebound
[/autoit] [autoit][/autoit] [autoit]
If Not $sieve[$i] Then $primes &= 2 * $i +1 & '|'
NextReturn StringSplit(StringTrimRight($primes, 1), '|', 2)
[/autoit] [autoit][/autoit] [autoit]
EndFunc; ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +
[/autoit] -
@Make-Grafik >> Mein Script macht genau das gleiche, nur das die Zahlen Liste nur aus ungeraden besteht. Erklärende Kommentare bräuchte dein Script btw auch ^^.
An die anderen >> Danke für die schnelle Hilfe, ich versuche es gleich auch ohne _ArrayDelete in der Schleife.
@Kanashius >> Das ConsoleWrite() macht zeittechnisch keinen Unterschied (habe ich getestet), deshalb habe ich es zur Darstellung drin gelassen -
Neues Script:
Spoiler anzeigen
[autoit]
[/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]
#include <Array.au3>
Const $iMax = 1000000
$hTimer = TimerInit()
$aPrimes = GetPrimes($iMax)
ConsoleWrite(@CRLF&"Time >> "&round(TimerDiff($hTimer)/1000, 4)&@CRLF)
_ArrayDisplay($aPrimes)Func GetPrimes(Const $iHead)
[/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]
Local $aPrimes[round($iHead/2-1, 0)]
$iResult = round($iHead/2-1, 0)
$x = 0
for $i = 3 to $iHead step +2
$aPrimes[$x] = $i
$x += 1
Next
$Stop = False
for $x = 0 to UBound($aPrimes)-1 step +1
$iNumber = $aPrimes[$x]
If $x > UBound($aPrimes)-1 or $Stop Then
ConsoleWrite("Last one: "&$iNumber&@CRLF)
ExitLoop
EndIf
$Stop = True;Local $hTimer = TimerInit()
[/autoit]
for $iNum = $x+1 to UBound($aPrimes)-1
If $aPrimes[$iNum] = -1 Then ExitLoop
If Mod($aPrimes[$iNum], $iNumber) = 0 Then
$Stop = False
;ConsoleWrite("Delete: "&$aPrimes[$iNum]&@CRLF)
$aPrimes[$iNum] = -1
$iResult -= 1
EndIf
Next
;ConsoleWrite($iNumber&" / "&UBound($aPrimes)-1&" Time: "&TimerDiff($hTimer)&@CRLF)
Next
Local $aResult[$iResult+1]
$x = 0
$aResult[0] = 2
for $i = 1 to $iResult
While $aPrimes[$x] = -1
$x += 1
WEnd
$aResult[$i] = $aPrimes[$x]
$x += 1
Next
ConsoleWrite($iResult +1 &" Primes found."&@CRLF)
Return $aResult
EndFunc
Stimmen die Primzahlen? Sind es 333334 bis eine Mio? -
- Offizieller Beitrag
Sind es 333334 bis eine Mio?
Überleg doch mal logisch: Demnach wäre jede 3.te Zahl eine Primzahl! Schau doch einfach mal die Zahlen 1-20 an, wie dort die Verteilung ist, dann wird dir klar, dass das nicht möglich sein kann.
-
1-20 = 8 Primzahlen, und die stimmen auch noch alle
Spoiler anzeigen
2
3
5
7
11
13
17
19
EDIT: Aber bei 30 schleicht sich die 25 ein. Mal sehen -
Das Problem liegt an der Abbruchbedingung. Siehe kommentierte Zeile:
Spoiler anzeigen
[autoit]#include <Array.au3>
[/autoit] [autoit][/autoit] [autoit]Const $iMax = 100
[/autoit] [autoit][/autoit] [autoit]
Const $hTimer = TimerInit()
Global $aPrimes = GetPrimes($iMax)
ConsoleWrite(@CRLF & "Time >> " & Round(TimerDiff($hTimer) / 1000, 4) & @CRLF)_ArrayDisplay($aPrimes)
[/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]Func GetPrimes(Const $iHead)
[/autoit] [autoit][/autoit] [autoit]
Local $aPrimes[$iHead / 2 -1]
Local $i, $jFor $i = 3 To $iHead Step +2
[/autoit] [autoit][/autoit] [autoit]
$aPrimes[$i / 2 -1] = $i
NextFor $i = 0 To $iHead / 2 -2
[/autoit] [autoit][/autoit] [autoit]
For $j = $i +1 To $iHead / 2 -2
;~ If Not $aPrimes[$j] Then $j = $iHead / 2 -2
If Not Mod($aPrimes[$j], $aPrimes[$i]) Then $aPrimes[$j] = False
Next
NextReturn $aPrimes
[/autoit]
EndFuncOkey, bei dir entspricht Zeile 25 meiner auskommentierten Zeile. Habe das Skript nur mal auf das nötigste komprimiert. Wie du siehst wird das Array abgeklappert bis wir auf -1 bzw. False treffen. Wenn wir das Skript bis 30 durchlaufen lassen, passiert folgendes:
Alles ungeraden Zahlen ab 3 bis 30 aufschreiben:
3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29Nun überprüfen welche der Zahlen eine Vielfache von der aktuellen Primzahl ist, bei 3 fangen wir an:
3 <> 5
3 <> 7
3 = 9 >> 9 wird ausradiert
usw...Folgende Liste bleibt über:
3, 5, 7, -, 11, 13, -, 17, 19, -, 23, 25, -, 29Gleiches Spiel, dieses mal mit der 5:
5 <> 7
5 <> - >> Abbruchbedingung erfüllt.Nun geht’s mit der 7 weiter:
7 <> - >> Abruchbedingung erfülltDu siehst wo das Problem ist? Ich weiß was du damit bezwecken willst, habe auch schon die Lösung. Die Abbruchbedingung gehört nicht in die zweite For-Schleife sondern muss abgefragt werden BEVOR diese überhaupt gestartet wird:
Spoiler anzeigen
[autoit]#include <Array.au3>
[/autoit] [autoit][/autoit] [autoit]Const $iMax = 30
[/autoit] [autoit][/autoit] [autoit]
Const $hTimer = TimerInit()
Global $aPrimes = GetPrimes($iMax)
ConsoleWrite(@CRLF & "Time >> " & Round(TimerDiff($hTimer) / 1000, 4) & @CRLF)_ArrayDisplay($aPrimes)
[/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]Func GetPrimes(Const $iHead)
[/autoit] [autoit][/autoit] [autoit]
Local $aPrimes[$iHead / 2 -1]
Local $i, $jFor $i = 3 To $iHead Step +2
[/autoit] [autoit][/autoit] [autoit]
$aPrimes[$i / 2 -1] = $i
NextFor $i = 0 To $iHead / 2 -2
[/autoit] [autoit][/autoit] [autoit]
If $aPrimes[$i] Then
For $j = $i +1 To $iHead / 2 -2
If Not Mod($aPrimes[$j], $aPrimes[$i]) Then $aPrimes[$j] = False
Next
EndIf
NextReturn $aPrimes
[/autoit]
EndFuncWie du siehst wird jetzt auch die 25 degradiert.
-
Danke für die Hilfe, dieses Problem hatte ich allerdings schon beseitigt. Das ganze dauerte dann aber viel länger.. Morgen Nachmittag kann ich deine Verbesserung testen, ich hoffe, dass mein neues Problem dort nicht auftritt
-
Nein - leider dauert das ganze wieder viel länger
-