[C++] Szukanie liczb pierwszych - optymalizacja programu

Maxime
Użytkownik
Użytkownik
Posty: 41
Rejestracja: 27 cze 2008, o 22:24
Płeć: Mężczyzna
Lokalizacja: Gliwice
Pomógł: 10 razy

[C++] Szukanie liczb pierwszych - optymalizacja programu

Post autor: Maxime »

Jakiś czas temu ponownie wziąłem się za c++, ponownie z tego samego kursu(od zera do gier kodera), i ponownie wziąłem się za program do znajdowania liczb pierwszych, tym razem z odrobinę większą wiedzą. Program sprawdza czy kolejne liczby(omijając podzielne przez 2 i 3) dzielą się przez wszystkie poprzednie liczby pierwsze. W tej chwili kod wygląda tak:

Kod: Zaznacz cały

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

typedef unsigned __int32 Lng;                 

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ą do zmiennej Prime
{
     fseek(b, ((a-1)*sizeof(Lng)), SEEK_SET);
     fread(c,sizeof(Lng),1,b);
}

int main()
{
    using namespace std;

    Lng u_Max;                                 //najwyższa sprawdzana liczba
    Lng u_Ile = 3;                             //liczba liczb pierwszych :)
    Lng w_Ile = 5;                             //najwyższa sprawdzona liczba                                  
    Lng Prime = 2;                             //bufor
    Lng x=0;                                   //Liczba wyświetlanych liczb pierwszych
    bool czy3;                                 //jeśli następna sprawdzana liczba jest podzielna przez 3 powoduje jej ominięcie
    bool przed3 = false;                       //powoduje ominięcie pierwszej liczby do sprawdzenia, jeśli jest podzielna przez 3
    bool flaga = false;
                                            
    cout<<"do jakiej liczby liczyc? - ";
    cin>>u_Max;
    cout<<endl;
    cout<<"ile wyswietlac? - ";
    cin>>x;
    cout<<endl;
    clock_t czas1 = clock();
    
    Lng* Prm = ″
    FILE *plik;
    FILE *liczba;
     
    plik=fopen("Prime.txt","rb+");                      //Odczytanie plików z listą i liczbą liczb pierwszych i sprawdzonych
    liczba=fopen("Number.txt","rb+");

    if (liczba==NULL) {czy3 = false; flaga==true;}

    if(plik==NULL){plik=fopen("Prime.txt","wb+");}      //utworzenie plików z listą i liczbą liczb pierwszych i sprawdzonych
    if(liczba==NULL){liczba=fopen("Number.txt","wb+");} 
           


    fseek(plik, 0, SEEK_END); 
    
    fseek(liczba, 0, SEEK_SET);                                        
    cout<<fread(&u_Ile,sizeof(Lng),1,liczba);
    
    fseek(liczba, sizeof(Lng), SEEK_SET);
    cout<<fread(&w_Ile,sizeof(Lng),1,liczba)<<endl;

    if ((w_Ile%3)==0&&flaga==false){czy3 = true;}           
    else if ((w_Ile%3)==2){czy3 = false;}
    else if ((w_Ile%3)==1){czy3 = true; przed3 = true;}
    
    /*Jeśli sprawdzona liczba jest podzielna przez 3, przy pierwszej iteracji czy3 zmieni wartość na false,
     przy 2 na true i przy trzeciej iteracji ominie sprawdzanie liczby(która jest podzielna przez 3).
     Jeśli sprawdzona liczba daje resztę z dzielenia przez trzy 2, jest tak jak wyżej z tym że zaczyna się na 2 iteracji.
     Jeśli sprawdzona liczba daje resztę z dzielenia przez trzy 1, wtedy pierwszą sprawdzaną liczbę trzeba ominąć(i=w_Ile+4)
     i wszystko zaczyna się jak w pierwszym przykładzie.*/

    if(w_Ile<=6)
    {
        fwrite(&Prime,sizeof(Lng),1,plik);          //wprowadzenie pierwszych trzech 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=w_Ile+2+(przed3*2); i<=u_Max; i+=(czy3+1)<<1)  /*sprawdzanie pierwszości liczb
                                                               (czy3=0 sprawdzanie liczby o 2 większej,
                                                                czy3=1 sprawdzanie liczby większej o 4)*/
    
    {

        b_Prim=true;                                         //flaga
        for(unsigned j=3; 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;
             
        }
        w_Ile=i;     
        czy3=!czy3;
        
        /*Wprowadzenie największej sprawdzonej liczby. właściwie można by było licznik zdefiniować wcześniej,
          a największą liczbę sprawdzoną zapisać po pętli... a licznik liczb pierwszych zmienić po pętli sprawdzając rozmiar pliku.*/
          
    }
    clock_t czas2 = clock();
    fseek(liczba, 0, SEEK_SET);                                        
    fwrite(&u_Ile,sizeof(Lng),1,liczba);    //zapisanie w pliku danych
    
    fseek(liczba, sizeof(Lng), SEEK_SET);
    fwrite(&w_Ile,sizeof(Lng),1,liczba);

    if (u_Ile<x){x=u_Ile;}
    for(unsigned i=0; i<=x-1; i++)  //wyświetlenie liczb pierwszych
    {
             Wczytaj(i+1,plik,Prm);
             cout<<Prime<<endl;
    }
    fclose(liczba);
    fclose(plik);
    cout<<"ilosc liczb pierwszych - "<<u_Ile<<endl;
    cout<<"gestosc - "<<static_cast<double>(u_Ile)/u_Max<<endl;

    cout<<"czas: "<<czas2-czas1<<" sekund"<<endl;
    cout<<"najwyzsza sprawdzona - "<<w_Ile<<" - liczba liczb pierwszych - "<<u_Ile<<endl;
    getch();
                 
}
poza sposobami na optymalizację wymienionymi w komentarzach można by wprowadzić odczytywanie liczb pierwszych do tablicy w celu zmniejszenia ilości odczytów, lecz nie wiem w jakim stopniu to przyśpieszy to program i czy dodatkowe komplikowanie kodu jest tego warte...
I w związku z tym piszę ten topic. Proszę aby osoby które mają jakiś sposób na przyspieszenie tego programu i ewentualnie chciało by im się zaimplementować o wypowiedź. wszelkie pomysły jak i krytyka są mile widziane
spajder
Użytkownik
Użytkownik
Posty: 735
Rejestracja: 7 lis 2005, o 23:56
Płeć: Mężczyzna
Lokalizacja: Łódź
Podziękował: 2 razy
Pomógł: 133 razy

[C++] Szukanie liczb pierwszych - optymalizacja programu

Post autor: spajder »

1. czemu 2 i 3 traktujesz spacjalnie? Możesz je traktować jak inne liczby pierwsze i umieszczać w tablicy - wtedy kod będzie czytelniejszy

2. optymalizacja tego mija się z celem - to jest naiwny algorytm - jeśli chcesz zrobić to szybko poszukaj bardziej zaawansowanych algorytmów (a jest ich niemało...)
Awatar użytkownika
miki999
Użytkownik
Użytkownik
Posty: 8691
Rejestracja: 28 lis 2007, o 18:10
Płeć: Mężczyzna
Lokalizacja: Gdańsk
Podziękował: 36 razy
Pomógł: 1001 razy

[C++] Szukanie liczb pierwszych - optymalizacja programu

Post autor: miki999 »

polecam:

Maxime
Użytkownik
Użytkownik
Posty: 41
Rejestracja: 27 cze 2008, o 22:24
Płeć: Mężczyzna
Lokalizacja: Gliwice
Pomógł: 10 razy

[C++] Szukanie liczb pierwszych - optymalizacja programu

Post autor: Maxime »

2 i 3 są umieszczone w tablicy, a traktuje specjalnie bo zaimplementowanie omijania zamiast sprawdzania wielokrotności tych liczb jest najłatwiejsze i pozwala przyśpieszyć kod. opuszczenie liczb podzielnych przez 3 przyśpieszyło kod o 1/9. Implementacja omijania liczb podzielnych przez 5 prawdopodobnie spowolni działanie kodu.
kod będzie czytelniejszy, ale wolniejszy.

Inne algorytmy - Mi się zdaje czy dają one jedynie(duże, ale jednak tylko)prawdopodobieństwo że liczba jest pierwsza? zresztą, nie wszystko co jest tam napisane rozumiem, a to jeszcze zaimplementować by trzeba...

Sito eratostenesa - znam, ale ono ogranicza do jakiejś konkretnej liczby, czego bym nie chciał. a jak już o prędzej sito atkina.

PS. o tej porze jakoś nie mam zbytniej ochoty na pisanie kodu, ale jutro przeanalizuje angielską stronkę wiki(/wiki/Primality_test) i może coś pozmieniam w kodzie.
frej

[C++] Szukanie liczb pierwszych - optymalizacja programu

Post autor: frej »

Co do sita Eratostenesa, to oczywiście trzeba wiedzieć, do jakiej liczby się sprawdza. Maxime, powiedz mi, w czym Ci to przeszkadza? Przecież i tak musisz szukać na pewnym przedziale( może być to oczywiście strasznie duży przedział ), bo chyba nie robisz tego w nieskończoność...
Dumel
Użytkownik
Użytkownik
Posty: 2000
Rejestracja: 19 lut 2008, o 17:35
Płeć: Mężczyzna
Lokalizacja: Stare Pole/Kraków
Podziękował: 60 razy
Pomógł: 202 razy

[C++] Szukanie liczb pierwszych - optymalizacja programu

Post autor: Dumel »

Maxime pisze:Inne algorytmy - Mi się zdaje czy dają one jedynie(duże, ale jednak tylko)prawdopodobieństwo że liczba jest pierwsza? zresztą, nie wszystko co jest tam napisane rozumiem, a to jeszcze zaimplementować by trzeba...
no niestety, prawdopodobieństwo wynosi tylko 100%. Sito Eratostenesa jest mimo swej prostoty bardzo dobrym algorytmem- działa w czasie \(\displaystyle{ O(n\ log \log n)}\), a ten Twój program ma złożoność czasową coś około (nie chce mi sie dokładnie analizować) \(\displaystyle{ O(n^2)}\)
luka52
Użytkownik
Użytkownik
Posty: 8601
Rejestracja: 1 maja 2006, o 20:54
Płeć: Mężczyzna
Lokalizacja: Kraków
Podziękował: 47 razy
Pomógł: 1816 razy

[C++] Szukanie liczb pierwszych - optymalizacja programu

Post autor: luka52 »

http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes#Algorithm pisze:The complexity of the algorithm is O((nlogn)(loglogn)) and has a memory requirement of O(n).
A \(\displaystyle{ \mathcal{O} ft( n \log n \log \log n \right)}\) to znacznie więcej niż \(\displaystyle{ \mathcal{O} ft( n \log \log n \right)}\) .
?odzianin

[C++] Szukanie liczb pierwszych - optymalizacja programu

Post autor: ?odzianin »

Proponuję krótki program:

Kod: Zaznacz cały

#include<iostream>
using namespace std;
int main()
{
    int max;
    cout<<"Podaj najwieksza liczbe
";
    cin>>max;
    int licznik=0;
    for(int i=2; i<max+1; i++)
    {
            for(int j=1; j<i; j++)
            {
                    if(i%j==0)
                    licznik++;
            }
            if(licznik==1)
            {
            cout<<i<<" jest liczbą pierwsza
";
            }
    licznik=0;
    }
}
Dumel
Użytkownik
Użytkownik
Posty: 2000
Rejestracja: 19 lut 2008, o 17:35
Płeć: Mężczyzna
Lokalizacja: Stare Pole/Kraków
Podziękował: 60 razy
Pomógł: 202 razy

[C++] Szukanie liczb pierwszych - optymalizacja programu

Post autor: Dumel »

1.wątpię czy autor topica jest po 6 miesiacach zainteresowany kolejnymi odpowiedziami
2. ten algorytm jest skrajnie nieefektywny, przeczytaj pare postow wyzej
Xitami

[C++] Szukanie liczb pierwszych - optymalizacja programu

Post autor: Xitami »

Dumel pisze:[...]ten algorytm jest skrajnie nieefektywny[...]
Eeee tam "skrajnie"

Kod: Zaznacz cały

Program paciorki
  var a,b,k,n:longword;
begin
  n:=1283;

  a:=n div 2;
  b:=2;
  k:=n and 1;  //n == a*b+k

  while (a>b) and (k<>0) do begin
    a -= 1;
    k += b;
    if k >= a then begin
      b += 1;
      k -= a
    end
  end;
  // n == a*b+k
  if k <> 0 then writeln(n,? jest pierwsze?)
end.
Dumel
Użytkownik
Użytkownik
Posty: 2000
Rejestracja: 19 lut 2008, o 17:35
Płeć: Mężczyzna
Lokalizacja: Stare Pole/Kraków
Podziękował: 60 razy
Pomógł: 202 razy

[C++] Szukanie liczb pierwszych - optymalizacja programu

Post autor: Dumel »

Xitami pisze:Eeee tam "skrajnie"
no fakt, moznaby np losowac liczby, sprawdzac czy dzielą n, a po n^100 sprawdzen przyjac ze mamy dosc duze prawdopodobienstwo ze n jest pierwsza
ODPOWIEDZ