Den er lavet til at finde primtalspalindromer imellem to givne tal. Den virker som den skal, men den er for langsom. Målet er at den skal kunne finde alle primtalspalindromer mellem 5 og 100000000 på under 5 sekunder. Det kører på en linux maskine på omkring 800Mhz.
Den kan du nemt gøre hurtigere du skal bruge en metode der hedder erastotenes si (jeg garanterer ikke for stavemåden)
Hvis du vil finde alle primtal imellem 1 og 100 gør du følgende.
lav et array med 100 pladser (gerne bits) sæt dem alle til 1.
Start med 2, er plads 2=1 ja det er den, godt, fjern nu alle multiplummer af 2 op til , altså nulstil plads 4,6,8,10.... Kig på næste plads altså nummer 3 er den =1 yep, fjern så alle multiplummer af 3, altså 6,9,12,15.... Er plads 4=1 nej den fjernede vi jo ved multiplum af 2. så kig på plads nummer 5, fjern så alle multiplummer af 5, altså 10,15,20,25.... dette gør du så indtil du har gjort det op til kvadratroden af antal elementer altså op til talle 10.
Det må jeg lige lege lidt med. Har du evt. noget dokumentation på det? En anden begrænsning der er, er at det ikke må brugere mere dn 16Mb hukommelse. Så hvis jeg skal have et array med 100000000 elementer, som hver kan indeholde meget store tal, bliver det så ikke problematisk?
Så med 16 MB plads kan du klare op til 16*1024*1024*8 og hvis du er smart ved du alle lige tal ikke er primtal, så gange du lige antallet med 2 igen :)
Nu har jeg næsten fået det til at køre, har dog lige en sidste flaskehals.
For at tjekke for om tallene er palindromer, så skal jeg have det over i en char[9], hvor jeg tjekker fra begge ender indtil et tal ikke passer sammen.
Problemet er konverteringen. Da det skal være Gnu++ kompatibelt, så er itoa ikke en mulighed. sprintf er alt for langsom her, jeg har testet at det er flaskehalsen. Så hvad har jeg af muligheder? (fart er altbetydende).
disky >> en bool er ikke nødvendigvis 1 byte stor, det afhænger af flere ting... Fx var en bool i tidligere versioner af VC++ samme størrelse som en int.
int prime; char dig[9]; int tmp = prime; int oldtmp; for(int i=0;i<9;i++) { oldtmp=tmp; tmp=tmp/10; dig[8-i]=oldtmp-10*tmp; }
er hurtigere.
Det allerhurtigste ville være hvis x86 instruktions-sættet havde en instruktion som lavede både mod og div i en enkelt instruktion og vi kunne kalde den med asm.
En lille tilføjelse til diskys oprindelige svar omkring Erathostenes sigte (jeg garanterer HELLER ikke for stavningen...)
Det er rigtigt, at man kun behøver at fjerne multipla af allerede fundne primtal op til kvadratroden af maksimum, i eksemplet 10. Men man behøver rent faktisk ikke at fjerne multipla af et primtal "helt nedefra", man kan nøjes med at starte med kvadratet af primtallet. Når man i diskys eksempel når til primtallet 5, er det altså ikke nødvendigt at fjerne 10, 15 og 20 (de er nemlig allerede fjernet!) man skal bare starte med 25, 30, osv...
Jeg har ikke lige nogle forslag til konverteringen til decimal... Må jeg spørge, HVAD skal du bruge dette her til, og hvorfor skal det absolut køre under 5 sekunder? Løsningen ændrer sig jo ikke fra gang til gang, så det skulle jo være nok at køre bare en enkelt gang, og hvad betyder så nogle sekunder fra eller til? ;-)
Hmm, det er jo meget godt med disse løsninger, men kan vi ikke reducere problemet med 99,995% sådan uden videre.
fx. ved tal 100000X, X er 0-9 er der jo kun 1 der dur, nemlig 1, lige så med de øvrige tal på første position, hermed er talmængden reduceret 90%, ligeledes med cifre nr. 2 og 7, 3 og 6 samt 4 og 5, hermed reduceret til 99,99% og vi behøver ikke teste lige tal så vi kan reducere med 50% igen, for en samlet reduktion på 99,995%
Palindrom ligger meget op til en rekursiv løsning...
int JegErEtPalindrom(char *dig, int len) { if (!len) { CheckPrimtal(dig); // hele dig skal være prim gør hvad man nu skal bruge det til hvis prim return noget }
for (int i = 0; i <= 9; ++i) { dig[0] = i; dig[len-1] = i; JegErEtPalindrom(&dig[1], len-2); } return noget; }
2) Din funktion erstatter jo kun switch statementen i min kode og jeg kan ikke tro at en løkke og et rekursivt kald er hurtigere end 1 jump og 1-4 int comparisons.
Jeg accepterer. Løsningen som arne_v er kommet med er dog heller ikke hurtig nok. Såvidt jeg ved er division af integers noget af det langsomte integer behandling man kan komme i nærheden af. Så jeg må nok lige kigge lidt mere på hvordan jeg kan tjekke for palindromer. Tak for alle forslagene indtil videre.
Det var løsningen arne_v! Nu klarer den det på ca. et halvt sekund på min maskine. Jeg vil gerne give dig +50 point hvis du vil have dem? :) Jeg har lige et andet problem. Programmet skal køre på en linux maskine, kompileret med Gnu++ compileren. Jeg får beskeden at programmet er quittet med besked #6 - abort med det samme. Hvad kan det være? Jeg har ikke selv forstand på linux.
Æh, tror jeg ved det nu. Der er som sagt 16Mb hukommelsesgrænse. Min kode er her: http://nopaste.php-q.net/5336. Hvis jeg fjerner alt efter linie 47, så er forbruget på omkring en halv mb. Hvis jeg lader palindromfindingsalgoritmen stå, så kommer den op omkring 43Mb. Og de sidste tre linier (79-81) får hukommelsesforbruget op på omkring 196(!!!)Mb... Hmmm... bummer.
http://nopaste.php-q.net/5338 100% samme resultat. Jeg forstår det ikke rigtig. Første bool[megetstorttal] fylder <1Mb, næste fylder 40Mb. Og til sidst kommer jeg op på næsten 200Mb når jeg vil udskrive resultatet, hvilket jeg _slet_ ikke kan forstå?
Argh, har lige fundet ud af en bug i min primtals algoritme, så mange tal der ikke var primtal blev behandlet som primtal :/. Efter at have rettet den er performance igen for lav.
Min primtalsalgoritme ser således ud:
while (intCount<intCut) { intTest=intCount*intCount; while (intTest<=intHighRange) { arrPrimes[intTest]=true; intTest+=intCount*2; } intCount+=2; for (intCount=intCount; intCount<intCut; intCount+=2) if (!arrPrimes[intCount]) break; }
Et åbenlyst problem som md7jt også pointerer, er at 3 f.eks fjerner tallene 9, 15, 21, 27, 33, 39 osv, mens at tallet 11 også fjerne tallet 33 f.eks. Sådan går det igen mange gange, og en masse tal som allerede er udelukket bliver kørt igennem igen. Jeg kan dog ikke lige se hvordan jeg kan undgå det...
Selve genereringen af *pal tager ingen tid, ligeledes tager testen af arrPrimes&&pal heller ingen tid. Altså er det ovenstående algoritme som humlen skal findes i. Først og fremmest vil jeg have performance i orden, så må jeg fikse hukommelsesproblemet bagefter.
Igen, hvis i har forslag så er i velkomne :)
Ps. md7jt, det er en opgave jeg er blevet stillet, og kravene er at den skal kunne generere alle primtalspalindromer mellem 5 og 100 mill på under 5 sekunder, og hukommelsesforbruget må ikke overstige 16mb.
Ok, E's sieve skal bruge 12,5 MB hvis der bruges 1 bit/tal og der er ca. 20000 palindromer, (pseudo bevis se min eller arne_v's kode, 4 loops med hver 10 gennemløb med 2 tal i hver).
1. optimering, hvorfor vil du lave et nyt array til palindromerne? du kan lige så godt lave checket omgående hvor du genere palindromerne.
2. Du er kan skære de lige palindromer fra i koden, hvilket efterlader ca. 10000.
3. lav primtals check på dem.
4. man kan forsøge at undgå E's sieve men det bliver lidt snævert med tid :) Der er mindre end 2000 primtal under 10000. så man skal lave mindre end 10000(palindromer)*2000(primtal<10000) divisioner for at checke dette (efter at man har lavet de første primtal) 200.000.000 divs a ca. 40 cycles = 8.000.000.000 cycles du har 800MHz *5 = 4.000.000.000
orca, din algoritme tager så vidt jeg kan se også højde for den detalje, jeg havde pointeret: intTest=intCount*intCount; Her sikrer du dig jo netop, at du først starter ved 121, når du når frem til primtallet 11.
Men du kan følge det råd, som disky gav tidligere: Lad dit array repræsentere alle ULIGE tal. Dermed halverer du memoryforbruget (hvilket dog ikke er nok...), og du kan fjerne en *2 i din primtalsløkke. Koden bliver alt andet lige lidt vanskeligere at skrive, men det er nu ikke så slemt.
segmose/arne_v -> Grunden til pal arrayet er også at jeg så får et sorteret output (krævet). Ellers skal jeg bruge kræfter på at sortere mit output bagefter, og det tror jeg bliver for langsomt.
Jeg har fået mit primtalsarray ned på størrelsen 50000001, ved at skære de lige tal fra, det hjælper på hukommelsesforbruget (49mb), så jeg må lige se om ikke også jeg kan gøre det samme med pal arrayet.
Jeg kan ikke se nogen grund til at lave primtalscheck på mit pal array mens jeg gemmer værdierne, for da jeg vil have sorteret output, så kan jeg ligeså godt gøre det hele til sidst.
Ja, der er en matematisk sammenhæng: Palindromer med et lige antal cifre kan altid divideres med 11. Man kan let overbevise sig om, at palindromer med et lige antal cifre kan opløses i summer af følgende led: 11, 1001, 100001,... altså med et lige antal 0'er i "midten". F.eks. er 36577563 = 3*10000001+60*100001+500*1001+7000*11. Derefter skal vi bare "bevise", at 11 går op i 10..01, og det er også let at "se": Beregn differencen mellem 1001 (med n nuller) og 11 (med n-2 nuller), den er 990 (altså 99 med ned n-1 nuller), det vil gælde for hele rækken. 11 går selvfølgelig op i 11 og også op i 990, 99000, osv. altså går 11 op i hele rækken, og dermed også i alle palindromer med et lige antal cifre. (Kan naturligvis formuleres pænt som et rigtigt induktionsbevis).
Så kunne man faktisk speede algoritmen yderligere op ved at skippe 4, 6 og 8 cifrede tal (ikke 2 da 11 selv lige skal med).
Synes godt om
Ny brugerNybegynder
Din løsning...
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.