Re: Project Euler - Aufgabe 586 (Wettbewerb)
Verfasst: 06.02.2017 22:13
oh verdammt. ja auf r=40.....
Das deutsche PureBasic-Forum
https://www.purebasic.fr/german/
Code: Alles auswählen
f(n, 1) = 87505
f(n, 2) = 48727
f(n, 3) = 2149
f(n, 4) = 5306
f(n, 5) = 6
f(n, 6) = 230
f(n, 7) = 0
f(n, 8) = 55Code: Alles auswählen
f(10000000, 1) = 724319
f(10000000, 2) = 496337
f(10000000, 3) = 18750
f(10000000, 4) = 81624
f(10000000, 5) = 108
f(10000000, 6) = 4472
f(10000000, 7) = 1
f(10000000, 8) = 2676
f(10000000, 9) = 53
f(10000000, 10) = 2
f(10000000, 12) = 59
f(10000000, 16) = 1Code: Alles auswählen
EnableExplicit
Macro func(a, b)
((a) * (a) + 3 * (a) * (b) + (b) * (b))
EndMacro
; Wenn #True, werden für alle 0 < r <= #maxR alle zutreffenden Zahlen gespeichert und am Ende gelistet
; VORSICHT: Für hohe n könnte sehr viel Speicher beansprucht werden!
#doFullStatistics = #False
; Wenn #True, wird der Algorithmus automatisch für alle 0 < r < #maxR ausgeführt
#doSimpleStatistics = #True
; Obergrenze für r für die Statistik
#maxR = 100
; Millisekunden, die vergehen müssen, bevor ein Statusupdate ausgegeben wird.
; 0 zum Deaktivieren
#progress = 1000
CompilerIf #doFullStatistics
Structure Sequence
List number.i()
EndStructure
Dim Sequences.Sequence(#maxR)
CompilerEndIf
CompilerIf #doSimpleStatistics
Define innerLoops.q = 0
Dim rStat.i(#maxR)
CompilerEndIf
OpenConsole()
Define n.q, r.i
n = 10000000
r = 6
; Zeit
Define time.i, nextPrintTime.i
time = ElapsedMilliseconds()
nextPrintTime = time + #progress
; Ergebnis für f(n, r)
Define.i result = 0
; interne Variablen
Define k.q, maxA.q, maxAIncAt.q, minA.q, minAIncAt.q
k = 11
maxA = 2 ; Obergrenze für a für aktuelles k
maxAIncAt = func(maxA, 1) + 1 ; das k, ab dem maxA inkrementiert wird
minA = 2 ; Untergrenz für a für aktuelles k
minAIncAt = func(minA + 1, minA) ; das k, ab dem minA inkrementiert wird
Define matches.i, a.q, a3.q, b.q, minB.q, kA.q
While k <= n
matches = 0
; Falls a² + 3a + 2 = k mit a = maxA, erhöhe maxA
If maxAIncAt = k
maxA + 1
; Anstatt maxAIncAt immer komplett neu auszurechnen, addiere einfach die Differenz
maxAIncAt + maxA + maxA + 2
EndIf
; Falls (a+1)² + 3(a+1)a + a² = k mit a = minA, erhöhe minA
If minAIncAt = k
; Anstatt minAIncAt immer komplett neu auszurechnen, addiere einfach die Differenz
minAIncAt + 10 * minA
minA + 1
EndIf
;PrintN("k=" + k + ", a=" + minA + ".." + maxA)
a = maxA
a3 = 3 * a
; Berechne das kleinste b, sodass a² + 3ab + b² <= k ist
;TODO Weg damit!!!
minB = IntQ(Sqr(5 * a * a + 4 * k) - 3 * a) / 2
b = minB
; Berechne den Initialwert einmalig pro k
;TODO Ist nicht so schlimm wie das Sqr() bzw. allgemein Floats
kA = func(a, b)
Repeat
CompilerIf #doSimpleStatistics
innerLoops + 1
CompilerEndIf
; Haben wir einen Treffen, zähle ihn, und fahre fort mit dem nächst kleineren a
If kA = k
matches + 1
a - 1
If a < minA
Break
EndIf
a3 - 3
kA - (a + a + 1 + 3 * b)
; Ist a² + 3ab + b² zu groß geworden, dekrementiere a
ElseIf kA > k
;
a - 1
If a < minA
Break
EndIf
a3 - 3
kA - (a + a + 1 + 3 * b)
; Ist a² + 3ab + b² zu klein geworden, inkrementiere b
ElseIf kA < k
kA + a3 + b + b + 1
b + 1
EndIf
; Sind a = b geworden, gilt die Regeln a > b > 0 nicht mehr, also beenden wir die Schleife
Until a = b
CompilerIf #doFullStatistics Or #doSimpleStatistics
If matches > 0 And matches <= #maxR
CompilerIf #doFullStatistics
If AddElement(Sequences(matches)\number())
Sequences(matches)\number() = k
EndIf
CompilerEndIf
CompilerIf #doSimpleStatistics
rStat(matches) + 1
CompilerEndIf
EndIf
CompilerEndIf
If matches = r
result + 1
EndIf
CompilerIf #progress > 0
If ElapsedMilliseconds() > nextPrintTime
nextPrintTime + #progress
PrintN("k=" + k + ", a=" + minA + ".." + maxA + ", f(n, r) >= " + result)
EndIf
CompilerEndIf
k + 1
Wend
time = ElapsedMilliseconds() - time
PrintN(~"\nResult:\nn = " + n + ~"\nr = " + r + ~"\nf(n, r) = " + result + ~"\nTime: " + time + ~" ms\n")
CompilerIf #doSimpleStatistics
Print("Enter for simple statistics...")
Input()
PrintN("Inner loops: " + innerLoops + " (avg. per k: " + Str(innerLoops / n) + ")")
For r = 1 To #maxR
If rStat(r)
PrintN("f(" + n + ", " + r + ") = " + rStat(r))
EndIf
Next
CompilerEndIf
CompilerIf #doFullStatistics
Print("Enter for full statistics...")
Input()
For r = 1 To #maxR
With Sequences(r)
If ListSize(\number()) > 0
Print("r=" + r + ":")
ForEach \number()
Print(" " + \number())
Next
PrintN("")
EndIf
EndWith
Next
CompilerEndIf
Print(~"\nEnter to quit...")
Input()
CloseConsole()Code: Alles auswählen
EnableExplicit
Macro func(a, b)
((a) * (a) + 3 * (a) * (b) + (b) * (b))
EndMacro
; Wenn #True, wird der Algorithmus automatisch für alle 0 < r < #maxR ausgeführt
#doSimpleStatistics = #True
; Obergrenze für r für die Statistik
#maxR = 100
; Millisekunden, die vergehen müssen, bevor ein Statusupdate ausgegeben wird.
; 0 zum Deaktivieren
#progress = 1000
CompilerIf #doSimpleStatistics
Dim rStat.i(#maxR)
CompilerEndIf
CompilerIf #progress > 0
Define lastInnerLoops.q = 0
CompilerEndIf
CompilerIf #progress > 0 Or #doSimpleStatistics
Define innerLoops.q = 0
CompilerEndIf
OpenConsole()
Define n.q, r.i
#threads = 8
n = 10000000
r = 6
; Zeit
Define startTime.i, nextPrintTime.i
startTime = ElapsedMilliseconds()
nextPrintTime = startTime + #progress
Structure ThreadData
result.i
kOffset.q
kEnd.q
skip.i
r.i
k.q
mutex.i
refreshStat.i
started.i
CompilerIf #doSimpleStatistics
rStat.i[#maxR + 1]
CompilerEndIf
CompilerIf #progress > 0 Or #doSimpleStatistics
innerLoops.q
CompilerEndIf
EndStructure
; Ergebnis für f(n, r)
Define.i result = 0
Procedure Thread(*threadData.ThreadData)
With *threadData
LockMutex(\mutex)
PrintN("Thread started. kOffset=" + \kOffset)
UnlockMutex(\mutex)
SignalSemaphore(\started)
\result = 0
CompilerIf #doSimpleStatistics
Protected i.i
For i = 0 To #maxR
\rStat[i] = 0
Next
CompilerEndIf
CompilerIf #progress > 0 Or #doSimpleStatistics
\innerLoops = 0
CompilerEndIf
; interne Variablen
Protected k.q, maxA.q, maxAIncAt.q, minA.q, minAIncAt.q
k = 11 + \kOffset
;TODO maxA und minA berechnen
maxA = 2 ; Obergrenze für a für aktuelles k
maxAIncAt = func(maxA, 1) + 1 ; das k, ab dem maxA inkrementiert wird
minA = 2 ; Untergrenz für a für aktuelles k
minAIncAt = func(minA + 1, minA) ; das k, ab dem minA inkrementiert wird
Protected matches.i, a.q, a3.q, b.q, minB.q, kA.q
While k <= \kEnd
\k = k
matches = 0
; Falls a² + 3a + 2 = k mit a = maxA, erhöhe maxA
While maxAIncAt <= k
maxA + 1
; Anstatt maxAIncAt immer komplett neu auszurechnen, addiere einfach die Differenz
maxAIncAt + maxA + maxA + 2
Wend
; Falls (a+1)² + 3(a+1)a + a² = k mit a = minA, erhöhe minA
While minAIncAt <= k
; Anstatt minAIncAt immer komplett neu auszurechnen, addiere einfach die Differenz
minAIncAt + 10 * minA
minA + 1
Wend
;PrintN("k=" + k + ", a=" + minA + ".." + maxA)
a = maxA
a3 = 3 * a
; Berechne das kleinste b, sodass a² + 3ab + b² <= k ist
;TODO Weg damit!!!
minB = IntQ(Sqr(5 * a * a + 4 * k) - 3 * a) / 2
b = minB
; Berechne den Initialwert einmalig pro k
;TODO Ist nicht so schlimm wie das Sqr() bzw. allgemein Floats
kA = func(a, b)
Repeat
CompilerIf #doSimpleStatistics Or #progress > 0
\innerLoops + 1
CompilerEndIf
; Haben wir einen Treffen, zähle ihn, und fahre fort mit dem nächst kleineren a
If kA = k
matches + 1
a - 1
If a < minA
Break
EndIf
a3 - 3
kA - (a + a + 1 + 3 * b)
; Ist a² + 3ab + b² zu groß geworden, dekrementiere a
ElseIf kA > k
;
a - 1
If a < minA
Break
EndIf
a3 - 3
kA - (a + a + 1 + 3 * b)
; Ist a² + 3ab + b² zu klein geworden, inkrementiere b
ElseIf kA < k
kA + a3 + b + b + 1
b + 1
EndIf
; Sind a = b geworden, gilt die Regeln a > b > 0 nicht mehr, also beenden wir die Schleife
Until a = b
CompilerIf #doSimpleStatistics
If matches > 0 And matches <= #maxR
\rStat[matches] + 1
EndIf
CompilerEndIf
If matches = \r
\result + 1
EndIf
k + \skip
Wend
LockMutex(\mutex)
PrintN("Thread done. kOffset=" + \kOffset)
UnlockMutex(\mutex)
EndWith
EndProcedure
Structure Thread
handle.i
threadData.ThreadData
EndStructure
Dim threads.Thread(#threads - 1)
Define mutex.i = CreateMutex()
; Initialisiere ThreadData
Define i.i
For i = 0 To #threads - 1
With threads(i)\threadData
\kOffset = i
\kEnd = n
\skip = #threads
\r = r
\mutex = mutex
\refreshStat = #False
\started = CreateSemaphore(0)
EndWith
Next
; Starte Threads
For i = 0 To #threads - 1
With threads(i)
\handle = CreateThread(@Thread(), @\threadData)
EndWith
Next
; Warte bis alle laufen
For i = 0 To #threads - 1
With threads(i)\threadData
WaitSemaphore(\started)
EndWith
Next
CompilerIf #progress > 0
Define kLast.q = 0, threadsRunning.i
Repeat
Define currentTime = ElapsedMilliseconds()
threadsRunning.i = #threads
If currentTime > nextPrintTime
nextPrintTime = currentTime
result = 0
Define.i kMin = n
Define.i kMax = 0
Define.q innerLoops = 0
For i = 0 To #threads - 1
With threads(i)\threadData
result + \result
innerLoops + \innerLoops
If \k < kMin
kMin = \k
EndIf
If \k > kMax
kMax = \k
EndIf
EndWith
If Not IsThread(threads(i)\handle)
threadsRunning - 1
EndIf
Next
PrintN("k=" + kMin + ".." + kMax +
", f(n, r) >= " + result +
", k/sec=" + StrD((kMin - kLast) * 1000 / #progress, 2) +
", loops/sec=" + StrD((innerLoops - lastInnerLoops) * 1000 / #progress, 2) +
", time=" + Str(currentTime - startTime))
kLast = kMin
lastInnerLoops = innerLoops
EndIf
Delay(#progress)
Until threadsRunning = 0
CompilerElse
; Warte auf Thread-Ende
For i = 0 To #threads - 1
With threads(i)
WaitThread(\handle)
EndWith
Next
CompilerEndIf
; Akkumuliere Statistik
CompilerIf #doSimpleStatistics
Define j.i
For i = 0 To #threads - 1
With threads(i)\threadData
For j = 1 To #maxR
rStat(j) + \rStat[j]
Next
EndWith
Next
CompilerEndIf
result = 0
For i = 0 To #threads - 1
With threads(i)\threadData
result + \result
EndWith
Next
startTime = ElapsedMilliseconds() - startTime
PrintN(~"\nResult:\nn = " + n + ~"\nr = " + r + ~"\nf(n, r) = " + result + ~"\nTime: " + startTime + ~" ms\n")
CompilerIf #doSimpleStatistics
Print("Enter for simple statistics...")
Input()
PrintN("Inner loops: " + innerLoops + " (avg. per k: " + Str(innerLoops / n) + ")")
For r = 1 To #maxR
If rStat(r)
PrintN("f(" + n + ", " + r + ") = " + rStat(r))
EndIf
Next
CompilerEndIf
Print(~"\nEnter to quit...")
Input()
CloseConsole()Code: Alles auswählen
Result:
n = 10000000
r = 6
f(n, r) = 4472
Time: 24207 ms
Enter for simple statistics...
Inner loops: 21066879501 (avg. per k: 2106)
f(10000000, 1) = 724319
f(10000000, 2) = 496337
f(10000000, 3) = 18750
f(10000000, 4) = 81624
f(10000000, 5) = 108
f(10000000, 6) = 4472
f(10000000, 7) = 1
f(10000000, 8) = 2676
f(10000000, 9) = 53
f(10000000, 10) = 2
f(10000000, 12) = 59
f(10000000, 16) = 1Code: Alles auswählen
Inner loops: 666516755726 (avg. per k: 6665)
f(100000000, 1) = 6164497
f(100000000, 2) = 4877564
f(100000000, 3) = 155038
f(100000000, 4) = 1051883
f(100000000, 5) = 1074
f(100000000, 6) = 59517
f(100000000, 7) = 25
f(100000000, 8) = 63124
f(100000000, 9) = 970
f(100000000, 10) = 117
f(100000000, 12) = 2936
f(100000000, 13) = 4
f(100000000, 16) = 492
f(100000000, 18) = 13
f(100000000, 24) = 1