szukanie liczb pierwszych - sprawdzenie programu

0

Program ten szuka liczb pierwszych do pożądanej wartości, a Jako że wartości te mają być dość spore dane zapisywane są w osobnym pliku. Kompilator nie wykrywa błędów, ale kompilator to kompilator...
Pierwszy raz operuję na plikach(zresztą na wskaźnikach też) i chce się upewnić czy przez jakiś błąd wyprodukuje mi się plik-agresor zajmujący cały dysk(mały jak na dzisiejsze czasy) czy jakiś inny kwiatek.

oto kod:

#include <iostream>
#include <cmath>
#include <conio.h>
#include <fstream>
#include <stdio.h>

typedef unsigned __int64 Lng;                 /* notka do siebie: nie ignorować ŻADNEJ
                                                 możliwości c++. Że też ja jak debil cały
                                                 cały kod pisałem ze zwykłą nazwą a 
                                                 alias wprowadziłem ... na koniec :/        */  

void Zapisz(Lng* x, FILE* y)                               // dopisuje liczbę pierwszą na koniec pliku
{
     fseek(y, 0, SEEK_END);
     fwrite(x,sizeof(Lng),1,y);
}

bool Sprawdz(unsigned q, unsigned w, Lng* e, FILE* r)      // sprawdza czy liczba pierwsza jest                           
{                                                          // mniejsza od pierwiastka liczby sprawdzanej
     fseek(r, ((q-1)*16), SEEK_SET);
     fread(e,sizeof(Lng),1,r);
     return(*e <= ceil(pow(w,0.5)) ? true : false);
}

void Wczytaj(unsigned a, FILE* b, Lng* c)                  // wczytuje daną liczbę pierwszą
{
     fseek(b, ((a-1)*16), SEEK_SET);
     fread(c,sizeof(Lng),1,b);
}

int main()
{
    unsigned long u_Max;                                        //najwyższa sprawdzana liczba
    unsigned long u_Ile = 2;                                    //liczba liczb pierwszych :)
    Lng Prime=2;                                           //chwilowo przechowywana liczba pierwsza
    Lng* Prm = &Prime;
    std::cout<<"do jakiej liczby liczyc? - ";
    std::cin>>u_Max;
    std::cout<<std::endl;
    FILE *plik;    
    plik=fopen("Prime.txt","ab");                          //utworzenie lisry liczb pierwszych
    fwrite(&Prm,sizeof(unsigned __int64),1,plik);          //wprowadzenie pierwszych dwóch liczb pierwszych :)
    Prime=3;
    fwrite(&Prm,sizeof(unsigned __int64),1,plik);

    bool b_Prim=true;
    for(Lng i=5; i<=u_Max; i+=2)                           //sprawdzanie pierwszości liczb
    {

        b_Prim=true;                                       //flaga
        for(unsigned j=1; Sprawdz(j,i,Prm,plik); ++j)      //właściwa pętla sprawdzająca
        {
             Wczytaj(j,plik,Prm);
             if(i%Prime==0)
             {
                  b_Prim=false;                            //jeśli się dzieli nie jest pierwsza
                  break;
                          
             }                
        }
        if (b_Prim==true)
        {
             Prime=i;
             Zapisz(Prm,plik);                             // dopisuje liczbę pierwszą na koniec pliku
             ++u_Ile;
        }
    }
    for(unsigned i=0; i<=u_Ile-1; i++)
    {
             Wczytaj(i+1,plik,Prm);
             std::cout<<Prime<<std::endl;
    }
    std::cout<<"ilosc liczb pierwszych - "<<u_Ile<<std::endl;
    std::cout<<"gestosc - "<<static_cast<double>(u_Ile)/u_Max<<std::endl;
    getch();
                 
}

krytyka mile widziana :)

0

1' ciekawy komentarz dla siebie:)
2' return(*e <= ceil(pow(w,0.5)) ? true : false);
operator ?: jest totalnie zbedny, przeciez wyrazenie x<=y samo z siebie zwraca true/false i to w dodatku we wlasciwej kolejnosci
3' w main przy fwrite zapomniales o swoim typedef
4' zamiast w main pisac nonstop std:: mozesz uzyc lokalnego using'a

int main()
{   using namespace std;
    cout << "hi" << endl;
}

zasieg takiego usinga to wnetrze tej funkcji, wiec nie zasmiecasz sobie calej przestrzeni
5' jaki sens maja paraemtry funkcji sprawdz? q,w? skad ((q-1)*16)?

0

2' return(*e <= ceil(pow(w,0.5)) ? true : false);
operator ?: jest totalnie zbedny, przeciez wyrazenie x<=y samo z siebie zwraca true/false i to w dodatku we wlasciwej kolejnosci
A no tak, jakoś nie zauważyłem... będę musiał zwracać większą uwagę na takie rzeczy...

3' w main przy fwrite zapomniales o swoim typedef
O tej godzinie przeoczenie nawet tak długiego, pogrubionego tekstu jest możliwe

4' zamiast w main pisac nonstop std:: mozesz uzyc lokalnego using'a
int main()
{ using namespace std;
cout << "hi" << endl;
}
zasieg takiego usinga to wnetrze tej funkcji, wiec nie zasmiecasz sobie calej przestrzeni
główna część kodu(zamiast pliku wykorzystywała tablice) była pisana jakiś czas temu gdy o tym nie wiedziałem, a teraz nie chciało mi się tego zmieniać... Do poprawy.

5' jaki sens maja paraemtry funkcji sprawdz? q,w? skad ((q-1)*16)?
kolejne litery Qwerty, nie wiem skąd taki pomysł... ((q-1)*16) - wskazuje na pozycje odpowiedniej liczby pierwszej. to właściwie miało być ((q-1)*sizeof(Lng)), ale coś sobie ubzdurałem że sizeof(Lng) wyniesie 16... :/ Do poprawy.

Poza tym poprawiłem błąd przy wprowadzaniu pierwszych zmiennych - zamiast adresu wskaźnika teraz poprawnie przekazuje adres zmiennej, oraz dodałem 5 do liczb pierwszych(zaokrąglony pierwiastek z 5 dawał 3, który był równy najwyższej liczbie pierwszej w pliku, przez co program sprawdzał podzielność przez następne liczby których nie było w pliku - i pętla się zamyka...)

w tej chwili kod jest sprawny. Może i dużo wolniejszy od wersji z tablicą, ale pozwala na uzyskiwanie większych liczb(mam nadzieje że nie przegrzeje mi twardziela).
Przydało by się przyśpieszyć trochę działanie programu(Może ładowanie liczb z pliku po kilka/kilkanaście na raz do jakiejś małej tablicy...). Do czasu optymalizacji zmieniłem unsigned __int64 na 32(zupełnie wystarcza). na sprawdzenie 2^20 liczb potrzebuje ~115 sekund.

Oto kod:

#include <iostream>
#include <cmath>
#include <conio.h>
#include <fstream>
#include <stdio.h>

typedef unsigned __int32 Lng;                 /* notka do siebie: nie ignorować ŻADNEJ
                                                 możliwości c++. Że też ja jak debil cały
                                                 cały kod pisałem ze zwykłą nazwą a na 
                                                 alias zamieniłem ... na koniec :/        */  

void Zapisz(Lng* x, FILE* y)                               // dopisuje liczbę pierwszą na koniec pliku
{
     fseek(y, 0, SEEK_END);
     fwrite(x,sizeof(Lng),1,y);
}

bool Sprawdz(unsigned q, unsigned w, Lng* e, FILE* r)      // sprawdza czy liczba pierwsza jest                           
{                                                          // mniejsza od pierwiastka liczby sprawdzanej
     fseek(r, ((q-1)*sizeof(Lng)), SEEK_SET);
     fread(e,sizeof(Lng),1,r);
     return(*e <= ceil(pow(w,0.5)));
}

void Wczytaj(unsigned a, FILE* b, Lng* c)                  // wczytuje daną liczbę pierwszą
{
     fseek(b, ((a-1)*sizeof(Lng)), SEEK_SET);
     fread(c,sizeof(Lng),1,b);
}

int main()
{
    time_t czas1 = time(NULL);
    using namespace std;
    unsigned long u_Max;                                        //najwyższa sprawdzana liczba
    unsigned long u_Ile = 3;                                    //liczba liczb pierwszych :)
    Lng Prime=2;                                           //chwilowo przechowywana liczba pierwsza
    Lng* Prm = &Prime;
    cout<<"do jakiej liczby liczyc? - ";
    cin>>u_Max;
    cout<<endl;
    FILE *plik;    
    plik=fopen("Prime.txt","wb+");                          //utworzenie lisry liczb pierwszych
    fwrite(&Prime,sizeof(Lng),1,plik);          //wprowadzenie pierwszych dwóch liczb pierwszych :)
    Prime=3;
    fseek(plik, 0, SEEK_END);
    fwrite(&Prime,sizeof(Lng),1,plik);
    Prime=5;
    fseek(plik, 0, SEEK_END);
    fwrite(&Prime,sizeof(Lng),1,plik);
    bool b_Prim=true;
    for(Lng i=7; i<=u_Max; i+=2)                           //sprawdzanie pierwszości liczb
    {

        b_Prim=true;                                       //flaga
        for(unsigned j=1; Sprawdz(j,i,Prm,plik); ++j)      //właściwa pętla sprawdzająca
        {

             Wczytaj(j,plik,Prm);
             if(i%Prime==0)
             {

                  b_Prim=false;                            //jeśli się dzieli nie jest pierwsza
                  break;
                          
             }                
        }

        if (b_Prim==true)
        {
             Prime=i;
             Zapisz(Prm,plik);                             // dopisuje liczbę pierwszą na koniec pliku
             ++u_Ile;
        }
    }
    for(unsigned i=0; i<=u_Ile-1; i++)
    {
             Wczytaj(i+1,plik,Prm);
             cout<<Prime<<endl;
    }
    cout<<"ilosc liczb pierwszych - "<<u_Ile<<endl;
    cout<<"gestosc - "<<static_cast<double>(u_Ile)/u_Max<<endl;
    time_t czas2 = time(NULL);
    cout<<"czas: "<<czas2-czas1<<" sekund";
    getch();
                 
}
0

a teraz troche o sensownosci uzycia wlasnego kodu:)

    fwrite(&Prime,sizeof(Lng),1,plik);          //wprowadzenie pierwszych dwóch liczb pierwszych :)
    Prime=3;
    fseek(plik, 0, SEEK_END);
    fwrite(&Prime,sizeof(Lng),1,plik);
    Prime=5;
    fseek(plik, 0, SEEK_END);
    fwrite(&Prime,sizeof(Lng),1,plik);

czemu nie skorzystasz trzy razy z Zapisz(&Prime) ?

bool Sprawdz(unsigned q, unsigned w, Lng* e, FILE* r)      // sprawdza czy liczba pierwsza jest                           
{                                                          // mniejsza od pierwiastka liczby sprawdzanej
     fseek(r, ((q-1)*sizeof(Lng)), SEEK_SET);
     fread(e,sizeof(Lng),1,r);
     return(*e <= ceil(pow(w,0.5)));
}

void Wczytaj(unsigned a, FILE* b, Lng* c)                  // wczytuje daną liczbę pierwszą
{
     fseek(b, ((a-1)*sizeof(Lng)), SEEK_SET);
     fread(c,sizeof(Lng),1,b);
}

czemu w Sprawdz nie skorzystasz z Wczytaj(q)?

        for(unsigned j=1; Sprawdz(j,i,Prm,plik); ++j)      //właściwa pętla sprawdzająca
        {
             Wczytaj(j,plik,Prm);
             if(i%Prime==0)
             {
                  b_Prim=false;                            //jeśli się dzieli nie jest pierwsza
                  break;
             }                
        }

warunek petli wykonuje Sprawdz ktore wewnetrznie wola Wczytaj(j => Prm) [dosl.: wykonuje odpowienik], a potem wnetrze petli ponownie Wczytaj(j => Prm). strata czasu. proponuje niech Sprawdz zaklada ze liczba jest juz wczytana, i wywolywac Wczytaj przed sprawdz:

bool Sprawdz(unsigned w, Lng* e)
{
     return(*e <= ceil(pow(w,0.5)));
}

void Wczytaj(unsigned a, FILE* b, Lng* c)
{
     fseek(b, ((a-1)*sizeof(Lng)), SEEK_SET);
     fread(c,sizeof(Lng),1,b);
}
...
        for(unsigned j=1; Wczytaj(j,plik,Prm), Sprawdz(i, Prm); ++j)
             if(i%Prime==0)
             {
                  b_Prim=false;
                  break;
             }                

wodoslowie, przeciez b_Prim juz jest testowalne samo z siebie

if (b_Prim==true)

i kolejny przyczepek do typedefow i uzywanych typow:

bool Sprawdz(unsigned q, unsigned w, Lng* e, FILE* r)

parametr 'w' jesli liczba do porownania z 'e'. jesli 'e' przekazujesz przez Lng* to czemu w przez zwyklego unsigned? jesli chcesz potem moc przetypedefowac Lng na np. int64 to musisz WSZYSTKIE liczby na ktorych operujesz trzymac w takim sam sposob, inaczej to sie mija z celem. ta sama uwaga oczywiscie tyczy sie tez linii:

    unsigned long u_Max;                                        //najwyższa sprawdzana liczba
..
        for(unsigned j=1; Sprawdz(j,i,Prm,plik); ++j)      //właściwa pętla sprawdzająca

po zastosowaniu w/w Twoj program powinien sie troche hm.. skompresowac optycznie. kolejne uwagi moga sie tyczyc juz samego sposobu liczenia albo optymalizacji.. np.

  • wrzucasz na start pare pierwszych liczb - ale po co ? wyliczenie tych kilku to ulamek sekundy. jesli chcesz aby program umozliwial zaczecie 'od pewnego momentu', tzn. startowac z pewna baza juz wyliczonych wczesniej liczb, to ani sie waz pisac do pliku na starcie! wtedy trzeba zalozyc, ze plik jest, ze ma zawartosc, trzeba sprawdzic ile tam juz jest liczb, odczytac ostatnia, i startowac od niej
  • kazda jedna liczbe do ktorej porownujesz wczytujesz z pliku. przeciez te liczby sa male. to jest caly czas int32/int64 mieszczacy sie latwo w rejestrach. nie ma sensu zonglowac ciagle miedzy pamiecia a plikiem na dysku. nie wiem ilu liczb pierwszych spodziewac sie w zakresie int32/64, moze byc tego bardzo wiele, ale moze w pamieci RAM typowego komputera sie zmieszcza? i w ogole pliku nie trzeba uzywac w trakcie SPRAWDZANIA, a jedynie zapisywac do niego wyniki? a nawet jesli z oszacowan wyjdzie ze sie nie zmieszcza w pamieci, to warto trzymac w niej np. pierwsze pare tysiecy najmniejszych - przeciez wiekszosc liczb testowanych odpadnie na 2,3,5,7.. wlasnie na tych malych. trzymaj najmniejsze w pamieci, bo one sa najczesciej uzywane. te DUZE do ktorych rzadko sie dochodzi, to mozna trzymac w pliku

1 użytkowników online, w tym zalogowanych: 0, gości: 1