Permalänk
Medlem

c++ primtals porblem

Hej
Håller på med ett av problemen på projecteuler.net jag ska hitta summan av alla primtal under 1 million ach använder mig av ett slags
Sieve of Eratosthenes (http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes) för att hitta primtalen men det tar sjukt lång tid.
här föjer min kod:

#include <cstdlib> #include <iostream> #include <vector> #include <NTL/ZZ.h> using namespace std; using namespace NTL; int main() { int temp; ZZ summa=to_ZZ(0); vector<int> tal; vector<int> prime; vector<int>::iterator iter1; prime.push_back(2); for(int i=2; i<1000000; i++) tal.push_back(i); while(tal.size()>0){ temp=prime.back(); for(iter1=tal.begin(); iter1 != tal.end()+1; iter1++){ if(*iter1%temp==0){ tal.erase(iter1); } } prime.push_back(tal.front()); } prime.pop_back(); cout<<"hupp"<<endl; for(iter1=prime.begin(); iter1 != prime.end(); iter1++){ summa+=to_ZZ(*iter1); } cout<<"summan är: "<<summa<<endl; system("PAUSE"); return EXIT_SUCCESS; }

är det något jag kan göra för att snabba upp algoritmen?

Visa signatur

In murphy we trust, his law never fails.

Permalänk
Glömsk

Du behöver inte använda bignum här, förutom vid summeringen i slutet.

Skapa en array med en miljon bool. Använd sållet för att sätta alla primtal till true och alla icke-primtal till false. Detta kommer gå riktigt snabbt.

Sen går du igenom din array och kollar vilka x som är primtal, alltså om array[x] är true. Om du är det så adderar du x till summan.

Visa signatur

...man is not free unless government is limited. There's a clear cause and effect here that is as neat and predictable as a law of physics: As government expands, liberty contracts.

Permalänk
Medlem

tackar det verkar smartare, dock verkar det inte gå mycket fortare, vad gör jag för fel?

#include <cstdlib> #include <iostream> #include <NTL/ZZ.h> using namespace std; using namespace NTL; int main() { int temp=2; bool tal[1000000]; ZZ summa=to_ZZ(0); //tilldelning av hela vektorn for(int i=0; i<1000000; i++) tal[i]=true; //hitta primtal while(temp<1000000){ for(int k=0; k<1000000; k++){ if((k%temp==0&&k!=temp)||(k==1)) tal[k]=false; } do temp++; while(tal[temp]==false); } //summering for(int n=0; n<1000000; n++){ if(tal[n]) summa+=n; } cout<<"summan är: "<<summa<<endl; system("PAUSE"); return EXIT_SUCCESS; }

*EDIT:ändrade så den inte loopar mer än nödvändigt för temp

Visa signatur

In murphy we trust, his law never fails.

Permalänk

Du använder helt fel metod för att hitta primtalen.

Du ska börja på det lägsta primtalet (dvs 2). Du vet då att alla tal (större än 1) gånger 2 inte är primtal. Du sätter då dom talen som false i din array. Sen stegar du upp till nästa tal som fortfarande är satt till true i arrayn och gör samma sak med det. Till slut har du markerat alla tal som inte är prim som false i din array.

Permalänk
Medlem
Citat:

Ursprungligen inskrivet av jop_the_jopsan
Du använder helt fel metod för att hitta primtalen.

Du ska börja på det lägsta primtalet (dvs 2). Du vet då att alla tal (större än 1) gånger 2 inte är primtal. Du sätter då dom talen som false i din array. Sen stegar du upp till nästa tal som fortfarande är satt till true i arrayn och gör samma sak med det. Till slut har du markerat alla tal som inte är prim som false i din array.

det är väl ändå det jag gör? eller vad anser du att jag gör som är fel mer exakt?

Visa signatur

In murphy we trust, his law never fails.

Permalänk
Medlem

lite av tanken av sidor liknade project euler är väl att man ska klura på problemen själv men men...

du har krånglat till det och därför har det smygit in sig massa onödiga lösningar och lyckats försumma styrkan i algoritmen...

för det första vet vi att 0 och 1 inte är primtal, likväl som att 2 är det... eftersom första rundan (då man kontrollerar 2) är den mest krävande kan man inkludera den i loopen som förbereder arrayn. Så istället för:

for(int i=0; i<1000000; i++) tal[i]=true;

som sätter alla värden i arrayen till true så är

for(int i = 3; i < 1000000; i++){ tal[i] = i%2==1; }

bättre då man som sagt inkluderar första körningen i förberedelserna... kom ihåg att summa-variabeln ska inledas på 2 istället för 0 om man kör med denna metod...

vidare så är själva körningen väldigt konstig och som sagt så utnyttjar den inte styrkan i algoritmen... när du hittar ett primtal så vet du att alla tal som har det talet som primtalsfaktor inte är ett primtal, därmed är if((k%temp==0&&k!=temp)||(k==1)) onödigt då den testar något som du redan vet...

Det du ska göra är att gå igenom hela arrayn, om du finner ett primtal vet du att alla efterföljande tal som är i det talets "gångertabell" inte är primtal... så om du hittar 5 som primtal vet du att 10, 15, 20, 25, 30 osv inte är det, det faktumet utnyttjar du inte...

hur man vet att ett tal är ett primtal är genom att stega genom de tidigare talen i arrayn och om inget tidigare har "eliminerat" talet så är det ett primtal, det är ganska enkelt att förstå varför... Om man gått igenom alla mindre primtal och dessa inte utgör en primtalsfaktor i talet så betyder det att det måste finnas två eller fler större primtal som multiplicerat med varandra blir talet (alltså x*y=z när z<y<x ), men detta är ju självklart omöjligt... alltså kan inte talet ha några primtalsfaktorer (annat än sig själv)...

Vi börjar därför på minsta okontrollerade talet, alltså 3 eftersom vi avklarade 2 i förberedelserna... vi ser att 3 är "oeliminerat" alltså ett primtal... Vi börjar stega och eliminierar alla 3*n upp till 1000000, går till nästa okontrollerade talet, alltså 4. vi ser att det är eliminerat, alltså inte ett primtal och går vidare...

(man kan förkorta det ytterligare lite då man kan börja elimineringsstegningen först vid kvadraten av primtalet eftersom alla tidigare garanterat har blivit eliminerade av tidigare primtal, t.ex. vid 5 kan man börja på 25 eftersom 10 blivit eliminerat av 2*5 (när man hittade primtalet 2), 15 av 3*5 och 20 av 2*10. av samma anledning behöver man bara köra till roten av 1000000 för att garanterat ha eliminerat alla komposittal...)

till sist har vi din summering som är lite överföldig, varför inte summera allt eftersom du hittar primtalen...

om vi sätter det i kod blir det nått så här:

ulong summa = 2; bool tal[1000000]; for(int n = 3; n < 1000000; n++){ tal[n] = n%2==1; } for(int n = 3; n < 1000000; n++){ if(tal[n]){ //n är ett primtal summa += n; for(int m = n*n; m < 1000000; m+=n){ //stegar igenom alla tal som har n som primtalsfaktor tal[m] = false; } } } cout<<"summan är: "<<summa<<endl; system("PAUSE"); return EXIT_SUCCESS;

edit: kom på att det antagligen blir overload på int m = n*n när n börjar närma sig miljonen så borde byta ut den forloopen med:

if(n < 1000){ //kontrollerar om "elimination" är nödvändigt for(int m = n*n; m < 1000000; m+=n){ //stegar igenom alla tal som har n som primtalsfaktor tal[m] = false; } }

Permalänk
Medlem

tackar savje, listade ut det med att gå från kvadraten av talet när jag skulle somna igår men fick det aldrig att funlka så jag la aldrig in det.

En riktig aha upplevelse där med m+=n i for loopen.
Det är sånt som får en att börja bygga smartare algoritmer

*Edit: som sagt bör man lösa grejer själv, och faktum är att jag skrev en brute force metod som läste problemet åt mig, hittade sen detta sieve of eratosthenes och tyckte det verkade bra men lyckades inte på egen hand

//Aendy

Visa signatur

In murphy we trust, his law never fails.

Permalänk
Medlem
Citat:

Ursprungligen inskrivet av Aendy
tackar savje

Varsågod! alltid kul att vara till hjälp

project euler är en riktigt bra sida, jag har lärt mig mycket där...

btw, (alltså x*y=z när z<y<x ) som jag tidigare sa var omöjligt gällde självklart bara positiva heltal, annars skulle t.ex. 0.2*0.1=0.02 och 6*-5=-30 stämma... men ja kanske lite pedantiskt att påpeka