logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

Obliczenia na b. dużych liczbach (do 2mld miejsc znaczacych)

tzok 06 Mar 2005 16:47 2613 10
REKLAMA
  • #1 1292084
    tzok
    VIP Zasłużony dla elektroda
    Posty: 38685
    Pomógł: 3162
    Ocena: 6451
    Witam, mam taki fajny temat projektu, może komuś przyda się to co już napisałem a może ktoś znajdzie tam jakieś błędy lub zechce dopisać dzielenie.

    Założenia są takie:
    *liczba jest przechowywana w klasie jako tablica bajtów, po jednym bajcie na pozycję
    *klasa zawiera podstawowy interfejs do operacji na liczbie
    *działania są realizowane przez zewnętrzne funkcje (nie metody klasy)
    *reprezentacja liczb jest stałoprzecinkowa bez znaku
    //---------------------------------------------------------------------------
    
    #include <stdlib>
    #include <string>
    #include <stdio>
    
    //---------------------------------------------------------------------------
    class liczba
    {
    private:
            char* tbl;
            int dl;
    public:
            liczba(const char*);
            liczba(const int=0);
            ~liczba();
            void set(const char*);
            void setpos(const char, const int);
            char getpos(const int)const;
            char* get()const;
            int getlen()const;
            void print()const;
    
    };
    
    liczba::liczba(const char* _tbl)
    {
            dl=strlen(_tbl);
            int i=0, lz=0;
            while(_tbl[i]=='0'&&i<dl) {lz++; i++;};
            if((dl-lz)==0) lz--;
            tbl=new char[dl-lz];
            for(int i=0; i<dl-lz; i++)
                    tbl[i]=_tbl[i+lz]-'0';
            dl-=lz;
    }
    
    liczba::liczba(const int _dl): dl(_dl)
    {
            tbl=new char[dl];
    }
    
    liczba::~liczba()
    {
            delete [] tbl;
    }
    
    void liczba::set(const char* _tbl)
    {
            delete [] tbl;
            dl=strlen(_tbl);
            int i=0, lz=0;
            while(_tbl[i]=='0'&&i<dl) {lz++; i++;};
            if((dl-lz)==0) lz--;
            tbl=new char[dl-lz];
            for(int i=0; i<dl-lz; i++)
                    tbl[i]=_tbl[i+lz]-'0';
            dl-=lz;
    }
    
    void liczba::setpos(const char x, const int i)
    {
            tbl[i]=x;
    }
    
    char liczba::getpos(const int i)const
    {
            if(i<dl&&i>=0) return tbl[i];
            return 0;
    }
    
    char* liczba::get()const
    {
            return tbl;
    }
    
    int liczba::getlen()const
    {
            return dl;
    }
    
    void liczba::print()const
    {
            for(int i=0; i<dl; i++)
                    printf("%d", tbl[i]);
    }
    //---------------------------------------------------------------------------
    char* suma(const liczba& a, const liczba& b)
    {
            int dlmax;
            if(a.getlen()>=b.getlen()) dlmax=a.getlen();
            else dlmax=b.getlen(); // ustalenie dlugosci dluzszego czynnika
            char* c=new char[dlmax+2]; // rezerwacja pamieci na wynik z uwzgletnieniem znaku terminujacego
    //-------------------zerowanie i terminowanie zmiennej, bedzie przekazana jako c-string--
            for(int i=0; i<dlmax+1; i++)
                    c[i]='0';
            c[dlmax+1]='\0';
    //---------------------------------------------------------------------------------------
            char x=0, cr=0;
    //-------------------petla sumowania cyfr------------------------------------------------
            for(int i=0; i<dlmax; i++){
                    x=a.getpos(a.getlen()-1-i)+b.getpos(b.getlen()-1-i)+cr;
                    //--------obsluga przeniesienia--------
                    if(x>9){
                            x-=10;
                            cr=1;}
                    else cr=0;
                    //-------------------------------------
                    c[dlmax-i]=x+'0';}
    //--------------------------------------------------------------------------------------
            c[0]=cr+'0'; // dopisanie ewentualnego przeniesienia
            return c;
    }
    
    char* roznica(const liczba& a, const liczba& b)
    {
            int dlmax;
            if(a.getlen()>=b.getlen()) dlmax=a.getlen();
            else dlmax=b.getlen(); // ustalenie dlugosci najdluzszego czynnika
            char* c=new char[dlmax+1]; // rezerwacja pamieci na wynik
    //-------------------zerowanie i terminowanie zmiennej, bedzie przekazana jako c-string--
            for(int i=0; i<dlmax; i++)
                    c[i]='0';
            c[dlmax]='\0';
    //---------------------------------------------------------------------------------------
            signed char x=0, cr=0;
    //-------------------petla odejmowania cyfr------------------------------------------------
            for(int i=0; i<dlmax; i++){
                    x=a.getpos(a.getlen()-1-i)-b.getpos(b.getlen()-1-i)-cr;
                    //-------------------obsluga pozyczki------------------
                    if(x<0){
                            x+=10;
                            cr=1;}
                    else cr=0;
                    //-----------------------------------------------------
                    c[dlmax-1-i]=x+'0';}
            return c;
    }
    //------------------------funkcja treningowa, brak komentarzy-----------------------------
    char* ilzcyfr(const liczba& a, const char& b)
    {
            int dlmax=a.getlen();
    
            char* c=new char[dlmax+2];
    
            for(int i=0; i<dlmax+1; i++)
                    c[i]='0';
            c[dlmax+1]='\0';
    
            char x=0, cr=0;
            for(int i=0; i<dlmax; i++){
                    x=a.getpos(a.getlen()-1-i)*b+cr;
                    if(x>9){
                            cr=x/10;
                            x=x%10;}
                    else cr=0;
                    c[dlmax-i]=x+'0';}
            c[0]=cr+'0';
            return c;
    }
    //----------------------------------------------------------------------------------------
    char* iloczyn(const liczba& a, const liczba& b)
    {
            int dla=a.getlen();
            int dlb=b.getlen();
    
            char* c=new char[dla+dlb+2]; // wynik jest niedluzszy niz suma dlugosci obydwu cyfr
            for(int i=0; i<dla+dlb+2; i++)
                    c[i]=0;
            c[dla+dlb+1]='\0';
    
            char* temp=new char[dla+dlb+1]; // zmienna pomocnicz przechowyjaca wynik mnozenia przez pojedyncza cyfre
            for(int i=0; i<dla+dlb; i++)
                    temp[i]=0;
    
            char x=0, cr=0; // zmienne pomocnicze
    //--------------------------------petla cyfr drugiego czynnika---------------------------
            for(int j=0; j<dlb; j++){
            //-----------------------petla cyfr pierwszego czynnika--------------------------
                    for(int i=0; i<dla; i++){
                            x=a.getpos(a.getlen()-1-i)*b.getpos(b.getlen()-1-j)+cr;
                            //-------obsluga przeniesienia-----------------------------------
                            if(x>9){
                                    cr=x/10;
                                    x=x%10;}
                            else cr=0;
                            //---------------------------------------------------------------
                            temp[dla+dlb-j-i-1]=x;}
                    temp[dlb-1-j]=cr; // dopisanie ewentualnego przeniesienia
                    x=0, cr=0; // zerowanie wykorzystanych zmiennych pomocniczych
    //-------------------------------petla sumowania wynikow mnozen przez kolejne cyfry------
                    for(int k=0; k<dla+dlb; k++){
                            x=temp[dla+dlb-1-k]+c[dla+dlb-k]+cr;
                            temp[dla+dlb-1-k]=0;
                            //-------obsluga przeniesienia-----------------------------------
                            if(x>9){
                                    x-=10;
                                    cr=1;}
                            else cr=0;
                            //---------------------------------------------------------------
                            c[dla+dlb-k]=x;}
                    c[0]=cr; // dopisanie przeniesienia
                    x=0, cr=0; // zerowanie wykorzystanych zmiennych pomocniczych
                    }
    //-------------------------------petla konwersji wartosci wyniku na string---------------
            for(int i=0; i<dla+dlb+1; i++)
                    c[i]+='0';
    //---------------------------------------------------------------------------------------
            delete [] temp; // usuniecie zmiennej pomocniczej
            return c;
    }
    //---------------------------------------------------------------------------
    int main(int argc, char* argv[])
    {
            char* wpis=new char[255];
            char* wynik;
            liczba c;
    
            //string wpis;
    
            printf("a=");
            scanf("%s", wpis);
            liczba a(wpis);
            printf("b=");
            scanf("%s", wpis);
            liczba b(wpis);
            delete [] wpis;
    
                    printf("\n");
            a.print();
            printf(" + ");
            b.print();
            printf(" = ");
            wynik=suma(a, b);
            c.set(wynik);
            delete [] wynik;
            c.print();
                    printf("\n");
            a.print();
            printf(" - ");
            b.print();
            printf(" = ");
            wynik=roznica(a, b);
            c.set(wynik);
            delete [] wynik;
            c.print();
                    printf("\n");
            a.print();
            printf(" * ");
            b.print();
            printf(" = ");
            wynik=iloczyn(a, b);
            c.set(wynik);
            delete [] wynik;
            c.print();
                    printf("\n");
    
            system("pause");
    
            return 0;
    }
    //---------------------------------------------------------------------------
  • REKLAMA
  • #2 1298120
    Xitami
    Poziom 29  
    Posty: 1130
    Pomógł: 118
    Ocena: 31
    Wg pewnej książki klasyczne mnożenie dwóch liczb po milionie cyfr na maszynie o dzielności 10MIPS (mało ale niech tam) zajmie 24 godziny! Istnieją inne algorytmy, wg. tego samego źródła algorytm Karatsuby 10 minut, FFT 10 sekund (doba to 86 400 sekund) na tej maszynie pomnożenie miliarda cyfr przez miliard zajmie tylko 2700 lat (FFT 3 miesiące)
  • #3 1298709
    tzok
    VIP Zasłużony dla elektroda
    Posty: 38685
    Pomógł: 3162
    Ocena: 6451
    Mnożenie na pewno nie jest optymalne... ale działa, nadal brakuje mi natomiast dzielenia. Oczywiście mógłbym wykorzystać jakiś gotowy kod, problem w tym że chciałbym go przynajmniej rozumieć.
  • REKLAMA
  • #4 1299398
    shg
    Poziom 35  
    Posty: 2289
    Pomógł: 339
    Ocena: 134
    Przypominam, że komputery liczą w systemie binarnym!
    Implementacja tego była by tylko trochę trudniejsza (chyba, że ktoś myśli jak maszyna, do czego się konsekwentnie zbliżam :D ) niż systemu dziesiętnego, a wydajność wielokrotnie wyższa.
    Można by wtedy w prosty sposób zrobić mnożenie i dzielenie (binarne jest prostsze!), gdzieś na elektrodzi był chyba temat o dzieleniu, a jakby co to tu można coś znaleźć:
    http://www.8052.com/
    Jest tam gdzieś opisany sposób dzielenia binarnego, poza tym dość sporo takich procedur jest w książce "Hacker's delight", -> priv.

    Problemem była by tylko konwersja ascii<->liczba, trochę długo by to trwało :D
  • Pomocny post
    #5 1302277
    eihs
    Poziom 13  
    Posty: 33
    Pomógł: 6
    shg napisał:
    Problemem była by tylko konwersja ascii<->liczba, trochę długo by to trwało :D


    Wcale nie bylaby takim problemem :)

    zalozmy ze mamy liczbe 357, chcemy ja rozpisac na jedynki i zera. robimy sobie zatem kolumny, w kazdej kolumnie dokladnie jedna cyferka zapisana w czterech bitach


    0011 || 0101 || 0111, sa ladne trzy kolumny, teraz przesuwamy o jeden w prawo
    0001 || 1010 || 1011 -> 1 te wychodzaca cyferke zapamietujemy, jesli po przesunieciu, w ktorejs kolumnie jest wiecej niz 7 to odejmujemy od tego 3 i petlimy dalej (przesuniecie, ewentualne odejmowanie, az do zera)


    a wiec od poczatku

    
       3    5    7 
    ---- ---- ---- 
    0011 0101 0111 
    0001 1010 1011 -> 1 po przesunieciu 
        -0011-0011 
    0001 0111 1000      po odejmowaniu 
    
    0000 1011 1100 -> 0 po przesunieciu 
        -0011-0011 
    0000 1000 1001      po odejmowaniu 
    
    0000 0100 0100 -> 1 po przesunieciu 
    
    0000 0010 0010 -> 0 po przesunieciu 
    
    0000 0001 0001 -> 0 po przesunieciu 
    
    0000 0000 1000 -> 1 po przesunieciu 
             -0011 
    0000 0000 0101      po odejmowaniu 
    
    0000 0000 0010 -> 1 po przesunieciu 
    
    0000 0000 0001 -> 0 po przesunieciu 
    
    0000 0000 0000 -> 1 po przesunieciu (zero wiec koniec) 
    
    teraz czytamy od konca te bity co nam wychodzily z przesuniecia, czyli 101100101b = 357 
    


    mam nadzieje ze latwo zrozumiec :)

    greets
  • #6 1302846
    shg
    Poziom 35  
    Posty: 2289
    Pomógł: 339
    Ocena: 134
    Tak, faktycznie, już problemu nie ma, wczoraj wydumałem algorytm szybkiej konwersji między systemami liczb, wystarczy na dziesiętny zamienić, a potem na ASCII (albo odwrotnie), przy czym można to dość mocno zoptymalizować :D dla tego jednego konkretnego zadania
    https://www.elektroda.pl/rtvforum/topic262306.html#1302839
  • REKLAMA
  • #7 1303619
    tzok
    VIP Zasłużony dla elektroda
    Posty: 38685
    Pomógł: 3162
    Ocena: 6451
    Jak na razie dzielenie jest zrealizowane na... odejmowaniu ale jest baaaardzo nieefektywne jeśli różnica między liczbami jest duża, można je znacząco usprawnić mnożąc dzielnik przez 10^n tak aby był o jeden rząd wielkości mniejszy od dzielnej i dopiero odejmować.

    ***

    No... wreszcie udało mi się (z pomocą kolegów z grupy lab.) coś wykombinować:
    //---------------------------------------------------------------------------
    
    #include <stdlib>
    #include <string>
    #include <stdio>
    
    //---------------------------------------------------------------------------
    class liczba
    {
    private:
            char* tbl;
            int dl;
    public:
            liczba(const char*);
            liczba(const int=0);
            ~liczba();
            void set(const char*);
            void setpos(const char, const int);
            char getpos(const int)const;
            char* get()const;
            int getlen()const;
            void print()const;
    
    };
    
    liczba::liczba(const char* _tbl)
    {
            dl=strlen(_tbl);
            int i=0, lz=0;
            while(_tbl[i]=='0'&&i<dl) {lz++; i++;};
            if((dl-lz)==0) lz--;
            tbl=new char[dl-lz];
            for(int i=0; i<dl-lz; i++)
                    tbl[i]=_tbl[i+lz]-'0';
            dl-=lz;
    }
    
    liczba::liczba(const int _dl): dl(_dl)
    {
            tbl=new char[dl];
    }
    
    liczba::~liczba()
    {
            delete [] tbl;
    }
    
    void liczba::set(const char* _tbl)
    {
            delete [] tbl;
            dl=strlen(_tbl);
            int i=0, lz=0;
            while(_tbl[i]=='0'&&i<dl) {lz++; i++;};
            if((dl-lz)==0) lz--;
            tbl=new char[dl-lz];
            for(int i=0; i<dl-lz; i++)
                    tbl[i]=_tbl[i+lz]-'0';
            dl-=lz;
    }
    
    void liczba::setpos(const char x, const int i)
    {
            tbl[i]=x;
    }
    
    char liczba::getpos(const int i)const
    {
            if(i<dl&&i>=0) return tbl[i];
            return 0;
    }
    
    char* liczba::get()const
    {
            return tbl;
    }
    
    int liczba::getlen()const
    {
            return dl;
    }
    
    void liczba::print()const
    {
            for(int i=0; i<dl; i++)
                    printf("%d", tbl[i]);
    }
    //---------------------------------------------------------------------------
    char* suma(const liczba& a, const liczba& b)
    {
            int dlmax;
            if(a.getlen()>=b.getlen()) dlmax=a.getlen();
            else dlmax=b.getlen(); // ustalenie dlugosci dluzszego czynnika
            char* c=new char[dlmax+2]; // rezerwacja pamieci na wynik z uwzgletnieniem znaku terminujacego
    //-------------------zerowanie i terminowanie zmiennej, bedzie przekazana jako c-string--
            for(int i=0; i<dlmax+1; i++)
                    c[i]='0';
            c[dlmax+1]='\0';
    //---------------------------------------------------------------------------------------
            char x=0, cr=0;
    //-------------------petla sumowania cyfr------------------------------------------------
            for(int i=0; i<dlmax; i++){
                    x=a.getpos(a.getlen()-1-i)+b.getpos(b.getlen()-1-i)+cr;
                    //--------obsluga przeniesienia--------
                    if(x>9){
                            x-=10;
                            cr=1;}
                    else cr=0;
                    //-------------------------------------
                    c[dlmax-i]=x+'0';}
    //--------------------------------------------------------------------------------------
            c[0]=cr+'0'; // dopisanie ewentualnego przeniesienia
            return c;
    }
    
    char* roznica(const liczba& a, const liczba& b, int bsr=0)
    {
            int dlmax;
            if(a.getlen()>=b.getlen()) dlmax=a.getlen();
            else dlmax=b.getlen(); // ustalenie dlugosci najdluzszego czynnika
            char* c=new char[dlmax+1]; // rezerwacja pamieci na wynik
    //-------------------zerowanie i terminowanie zmiennej, bedzie przekazana jako c-string--
            for(int i=0; i<dlmax; i++)
                    c[i]='0';
            c[dlmax]='\0';
    //---------------------------------------------------------------------------------------
            signed char x=0, cr=0;
    //-------------------petla odejmowania cyfr------------------------------------------------
            for(int i=0; i<dlmax; i++){
                    x=a.getpos(a.getlen()-1-i)-b.getpos(b.getlen()-1-i+bsr)-cr;
                    //-------------------obsluga pozyczki------------------
                    if(x<0){
                            x+=10;
                            cr=1;}
                    else cr=0;
                    //-----------------------------------------------------
                    c[dlmax-1-i]=x+'0';}
            return c;
    }
    //------------------------funkcja treningowa, brak komentarzy-----------------------------
    char* ilzcyfr(const liczba& a, const char& b)
    {
            int dlmax=a.getlen();
    
            char* c=new char[dlmax+2];
    
            for(int i=0; i<dlmax+1; i++)
                    c[i]='0';
            c[dlmax+1]='\0';
    
            char x=0, cr=0;
            for(int i=0; i<dlmax; i++){
                    x=a.getpos(a.getlen()-1-i)*b+cr;
                    if(x>9){
                            cr=x/10;
                            x=x%10;}
                    else cr=0;
                    c[dlmax-i]=x+'0';}
            c[0]=cr+'0';
            return c;
    }
    //----------------------------------------------------------------------------------------
    char* iloczyn(const liczba& a, const liczba& b)
    {
            int dla=a.getlen();
            int dlb=b.getlen();
    
            char* c=new char[dla+dlb+2]; // wynik jest niedluzszy niz suma dlugosci obydwu cyfr
            for(int i=0; i<dla+dlb+2; i++)
                    c[i]=0;
            c[dla+dlb+1]='\0';
    
            char* temp=new char[dla+dlb+1]; // zmienna pomocnicza przechowyjaca wynik mnozenia przez pojedyncza cyfre
            for(int i=0; i<dla+dlb; i++)
                    temp[i]=0;
    
            char x=0, cr=0; // zmienne pomocnicze
    //--------------------------------petla cyfr drugiego czynnika---------------------------
            for(int j=0; j<dlb; j++){
            //-----------------------petla cyfr pierwszego czynnika--------------------------
                    for(int i=0; i<dla; i++){
                            x=a.getpos(a.getlen()-1-i)*b.getpos(b.getlen()-1-j)+cr;
                            //-------obsluga przeniesienia-----------------------------------
                            if(x>9){
                                    cr=x/10;
                                    x=x%10;}
                            else cr=0;
                            //---------------------------------------------------------------
                            temp[dla+dlb-j-i-1]=x;}
                    temp[dlb-1-j]=cr; // dopisanie ewentualnego przeniesienia
                    x=0, cr=0; // zerowanie wykorzystanych zmiennych pomocniczych
    //-------------------------------petla sumowania wynikow mnozen przez kolejne cyfry------
                    for(int k=0; k<dla+dlb; k++){
                            x=temp[dla+dlb-1-k]+c[dla+dlb-k]+cr;
                            temp[dla+dlb-1-k]=0;
                            //-------obsluga przeniesienia-----------------------------------
                            if(x>9){
                                    x-=10;
                                    cr=1;}
                            else cr=0;
                            //---------------------------------------------------------------
                            c[dla+dlb-k]=x;}
                    c[0]=cr; // dopisanie przeniesienia
                    x=0, cr=0; // zerowanie wykorzystanych zmiennych pomocniczych
                    }
    //-------------------------------petla konwersji wartosci wyniku na string---------------
            for(int i=0; i<dla+dlb+1; i++)
                    c[i]+='0';
    //---------------------------------------------------------------------------------------
            delete [] temp; // usuniecie zmiennej pomocniczej
            return c;
    }
    //---------------------------------------------------------------------------
    int compare (const liczba& a, const liczba& b, int bsr=0)
    {       //zwraca 1 gdy a>=b*10^bsr  lub 0 gdy a<b*10^bsr
            int dla = a.getlen();
            int dlb = b.getlen()+bsr;
            if(dla>dlb) return 1;
            else if(dla<dlb) return 0;
            else{
                    for(int i=0; i<dla; i++){
                            if(a.getpos(i)==b.getpos(i)) continue;
                            if(a.getpos(i)>b.getpos(i))return 1;
                            else return 0;
                    }
            }
            return 1;
    }
    //---------------------------------------------------------------------------
    char* iloraz(const liczba& a, const liczba& b)
    {
            int dla=a.getlen();
            int dlb=b.getlen();
            int off=dla-dlb; // roznica rzedow liczb
            char x; // cyfra wyniku
            liczba temp; // zmienna pomocnicza do odejmowania
            char* tempa=suma(a, "0"); // Przypisanie stringa odpowiadającego liczbie a do wskaźnika
            temp.set(tempa); // skopiowanie liczby a do zmiennej pomocniczej temp
            delete [] tempa; // zwolnienie pamięci zajmowanej przez string
            char* rozn;
            if(compare(temp, b, off)==0) off--; // jesli po wyrownaniu rzedow dzielnik jest wiekszy od dzielnej zmniejsz jego rzad o jeden
            char* c=new char[off+1]; // tablica wynikowa
            for(int i=0; i<off+1; i++)
                    c[i]=0; // inicjalizacja tablicy wynikowej
            int dlc=off;
            while(off>=0){ // zaczynamy od najbardziej znaczacej cyfry wyniku
                    x=0; // zerowanie zmiennej przed obliczeniem kolejnej cyfry wyniku
                    while(compare(temp, b, off)){ // odejmujemy od dzielnej dzielnik pomnozony przez 10^off dopuki wynik nie będzie mniejszy od dzielnika
                            rozn=roznica(temp, b, off); // to jest konieczne aby zachowac wskaznik do stringa ktory tworzy ta funkcja
                            temp.set(rozn);
                            delete [] rozn; // ... aby po jego zczytaniu moc zwolnic zajmowana przezen pamiec
                            x++; // zliczamy odjecia
                            }
                    c[dlc-off]=x+'0'; // ... i zapisujemy je jako kolejne cyfry wyniku
                    off--; // przechodzimy do kolejnej cyfry wyniku
            }
            c[dlc-off]=0; // terminujemy wynikowy ciąg
            return(c);
    }
    //---------------------------------------------------------------------------
    int main(int argc, char* argv[])
    {
            char* wpis=new char[255];
            char* wynik;
            liczba c;
    
            printf("a=");
            scanf("%s", wpis);
            liczba a(wpis);
            printf("b=");
            scanf("%s", wpis);
            liczba b(wpis);
            delete [] wpis;
    //for(int z=0; z<10000; z++){
                    printf("\n");
            a.print();
            printf(" + ");
            b.print();
            printf(" = ");
            wynik=suma(a, b);
            c.set(wynik);
            delete [] wynik;
            c.print();
                    printf("\n");
            a.print();
            printf(" - ");
            b.print();
            printf(" = ");
            wynik=roznica(a, b);
            c.set(wynik);
            delete [] wynik;
            c.print();
                    printf("\n");
            a.print();
            printf(" * ");
            b.print();
            printf(" = ");
            wynik=iloczyn(a, b);
            c.set(wynik);
            delete [] wynik;
            c.print();
                    printf("\n");
            a.print();
            printf(" / ");
            b.print();
            printf(" = ");
            wynik=iloraz(a, b);
            c.set(wynik);
            delete [] wynik;
            c.print();
                    printf("\n");
    //}
            system("pause");
    
            return 0;
    }
    //---------------------------------------------------------------------------
  • #8 1306635
    tzok
    VIP Zasłużony dla elektroda
    Posty: 38685
    Pomógł: 3162
    Ocena: 6451
    Update funkcji main():
    int main(int argc, char* argv[])
    {
            char* wpis=new char[255];
            char* ops;
            char op;
            int dalej=1;
            liczba c;
            printf("a=");
            scanf("%s", wpis);
            do{
                    liczba a(wpis);
                    printf("operacja [+, -, *, /, =]: ");
                    scanf("%1s", &op);
                    if(op=='='){
                            printf("\nwynik=");
                            a.print();
    			delete [] wpis;
                            break;}
                    printf("b=");
                    scanf("%s", wpis);
                    liczba b(wpis);
                    delete [] wpis;
                    switch(op){
                            case '+': wpis=suma(a, b); break;
                            case '-': wpis=roznica(a, b); break;
                            case '*': wpis=iloczyn(a, b); break;
                            case '/': wpis=iloraz(a, b); break;}
                    a.set(wpis);
                    printf("\na=");
                    a.print();
                    printf("\n");
                    }while(dalej);
            printf("\n");
            system("pause");
            return 0;
    }

    Okazuje się, że kod:
    scanf("%s", wpis);
    scanf("%c", &op);
    Nie zadziała, bo drugi scanf zczyta entera potwierdzającego pierwszy scanf.
  • #9 1312244
    Grzesiek1711
    Poziom 16  
    Posty: 169
    Pomógł: 10
    Ocena: 7
    coś ta procedurka nie do końca działa - właśnie dzielenie wywala błąd bo mi np 10/20=-48 czyli coś jeszcze nie do końca jest OK - ale i tak jestem pod wielkim wrażeniem normalnie RE5PECT...(nie sądziłem że to będzie tak szybko liczyło)
  • #10 1312376
    tzok
    VIP Zasłużony dla elektroda
    Posty: 38685
    Pomógł: 3162
    Ocena: 6451
    tzok napisał:
    *reprezentacja liczb jest stałoprzecinkowa bez znaku

    Tylko ten termin "stałoprzecinkowa" mi z roztargnienia wyszedł - reprezentowane są tylko liczby całkowite.
    ***
    Faktycznie - jest tam błąd z alokacją pamięci, tylko gdzie?
    int main(int argc, char* argv[])
    {
            char* wpis=new char[32];
            char* w=0;
            char* ops;
            char op;
            int dalej=1;
            liczba c;
            printf("Uzycie:\n");
            printf("<liczba>[Enter]<operator><liczba>[Enter]<operator><liczba>[Enter]...[=][Enter]\n: ");
            scanf("%s", wpis);
            liczba a(wpis);
            do{
                    printf(": ");
                    scanf(" %c", &op);
                    if(op=='='){
                            printf("\nwynik=");
                            a.print();
                            delete [] wpis;
                            break;}
                    scanf("%s", wpis);
                    liczba b(wpis);
                    switch(op){
                            case '+': w=suma(a, b); break;
                            case '-': w=roznica(a, b); break;
                            case '*': w=iloczyn(a, b); break;
                            case '/': w=iloraz(a, b); break;}
                    a.set(w);
                    delete [] w;
                    printf("= ");
                    a.print();
                    printf("\n");
                    }while(dalej);
            printf("\n\n");
            system("pause");
            return 0;
    }

    ****
    ok, już znalazłem błąd:
    char* iloraz(const liczba& a, const liczba& b)
    {
            int dla=a.getlen();
            int dlb=b.getlen();
            int off=dla-dlb; // roznica rzedow liczb
            char x; // cyfra wyniku
            liczba temp; // zmienna pomocnicza do odejmowania
            char* tempa=suma(a, "0"); // Przypisanie stringa odpowiadającego liczbie a do wskaźnika
            temp.set(tempa); // skopiowanie liczby a do zmiennej pomocniczej temp
            delete [] tempa; // zwolnienie pamięci zajmowanej przez string
            char* rozn;
            if(compare(temp, b, off)==0) off--; // jesli po wyrownaniu rzedow dzielnik jest wiekszy od dzielnej zmniejsz jego rzad o jeden
            if (off<0) off=0;
            char* c=new char[off+2]; // tablica wynikowa
            for(int i=0; i<off+2; i++)
                    c[i]='0'; // inicjalizacja tablicy wynikowej
            c[off+1]=0; // terminujemy wynikowy ciąg
            int dlc=off;
            while(off>=0){ // zaczynamy od najbardziej znaczacej cyfry wyniku
                    x=0; // zerowanie zmiennej przed obliczeniem kolejnej cyfry wyniku
                    while(compare(temp, b, off)){ // odejmujemy od dzielnej dzielnik pomnozony przez 10^off dopuki wynik nie będzie mniejszy od dzielnika
                            rozn=roznica(temp, b, off); // to jest konieczne aby zachowac wskaznik do stringa ktory tworzy ta funkcja
                            temp.set(rozn);
                            delete [] rozn; // ... aby po jego zczytaniu moc zwolnic zajmowana przezen pamiec
                            x++; // zliczamy odjecia
                            }
                    c[dlc-off]=x+'0'; // ... i zapisujemy je jako kolejne cyfry wyniku
                    off--; // przechodzimy do kolejnej cyfry wyniku
            }
    
            return(c);
    }
  • REKLAMA
  • #11 1799888
    tzok
    VIP Zasłużony dla elektroda
    Posty: 38685
    Pomógł: 3162
    Ocena: 6451
    Dzieci, nie róbcie tego w domu (kod jest paskudny ale działa). Oto wersja finalna, nadal tylko liczby całkowite ale ze znakiem:
    //---------------------------------------------------------------------------
    
    #include <stdlib>
    #include <string>
    #include <stdio>
    
    //---------------------------------------------------------------------------
    class liczba
    {
    private:
            char* tbl;
            int dl;
            signed char sgn;
    public:
            liczba(const char*);
            liczba(const int=0);
            ~liczba();
            int set(const char*);
            void setpos(const char, const int);
            char getpos(const int)const;
            char* get()const;
            int getlen()const;
            void print()const;
            signed char getsgn()const;
            void setsgn(const signed char);
    
    };
    
    liczba::liczba(const char* _tbl)
    {
            sgn=1;
    
            dl=strlen(_tbl);
            int i=0, lz=0;
            if(_tbl[0]=='-') {sgn=-1; lz++; i++;}
            while(_tbl[i]=='0'&&i<dl) {lz++; i++;};
            if((dl-lz)==0) lz--;
            dl=lz;
            i=0;
            while(_tbl[i+lz]-'0'>=0&&_tbl[i+lz]-'0'<=9) {dl++; i++;}
            tbl=new char[dl-lz];
            for(int i=0; i<dl-lz; i++)
                    //if(_tbl[i+lz]-'0'>=0&&_tbl[i+lz]-'0'<=9) tbl[i]=_tbl[i+lz]-'0';
                    //else tbl[i]=0;
                    tbl[i]=_tbl[i+lz]-'0';
            dl-=lz;
    }
    
    liczba::liczba(const int _dl): dl(_dl)
    {
            sgn=1;
            tbl=new char[dl];
    }
    
    liczba::~liczba()
    {
            delete [] tbl;
    }
    
    int liczba::set(const char* _tbl)
    {
            sgn=1;
            delete [] tbl;
            dl=strlen(_tbl);
            int ln;
            int i=0, lz=0;
            if(_tbl[0]=='-') {sgn=-1; lz++; i++;}
            while(_tbl[i]=='0'&&i<dl) {lz++; i++;};
            if((dl-lz)==0) lz--;
            ln=lz;
            i=0;
            while(_tbl[i+lz]-'0'>=0&&_tbl[i+lz]-'0'<=9) {ln++; i++;}
            //if(ln==lz)ln++;
            if (dl==ln){
                    tbl=new char[dl-lz];
                    for(int i=0; i<dl-lz; i++)
                            //if(_tbl[i+lz]-'0'>=0&&_tbl[i+lz]-'0'<=9) tbl[i]=_tbl[i+lz]-'0';
                            //else tbl[i]=0;
                            tbl[i]=_tbl[i+lz]-'0';
                    dl-=lz;
                    return 0;}
            else {
                    dl=ln;
                    tbl=new char[dl-lz];
                    for(int i=0; i<dl-lz; i++)
                            //if(_tbl[i+lz]-'0'>=0&&_tbl[i+lz]-'0'<=9) tbl[i]=_tbl[i+lz]-'0';
                            //else tbl[i]=0;
                            tbl[i]=_tbl[i+lz]-'0';
                    dl-=lz;
                    return 1;}
    }
    
    void liczba::setpos(const char x, const int i)
    {
            tbl[i]=x;
    }
    
    char liczba::getpos(const int i)const
    {
            if(i<dl&&i>=0) return tbl[i];
            return 0;
    }
    
    char* liczba::get()const
    {
            return tbl;
    }
    
    int liczba::getlen()const
    {
            return dl;
    }
    
    void liczba::print()const
    {
            if(sgn==-1) printf("-");
            for(int i=0; i<dl; i++)
                    printf("%d", tbl[i]);
    }
    signed char liczba::getsgn()const
    {
            return sgn;
    }
    void liczba::setsgn(const signed char newSgn)
    {
            sgn=newSgn;
    }
    //---------------------------------------------------------------------------
    char* equal(const liczba& a)
    {
            int dlmax=a.getlen();
            int so=0;
            if(a.getsgn()==-1)so=1;
            char* c=new char[dlmax+so+1];
            if(a.getsgn()==-1)c[0]='-';
            for(int i=0; i<dlmax+1; i++)
                    c[i+so]=a.getpos(i)+'0';
            c[dlmax+so]='\0';
            return c;
    }
    //---------------------------------------------------------------------------
    char* suma_(const liczba& a, const liczba& b)
    {
            int dlmax;
            if(a.getlen()>=b.getlen()) dlmax=a.getlen();
            else dlmax=b.getlen(); // ustalenie dlugosci dluzszego czynnika
            char* c=new char[dlmax+2]; // rezerwacja pamieci na wynik z uwzgletnieniem znaku terminujacego
    //-------------------zerowanie i terminowanie zmiennej, bedzie przekazana jako c-string--
            for(int i=0; i<dlmax+1; i++)
                    c[i]='0';
            c[dlmax+1]='\0';
    //---------------------------------------------------------------------------------------
            char x=0, cr=0;
    //-------------------petla sumowania cyfr------------------------------------------------
            for(int i=0; i<dlmax; i++){
                    x=a.getpos(a.getlen()-1-i)+b.getpos(b.getlen()-1-i)+cr;
                    //--------obsluga przeniesienia--------
                    if(x>9){
                            x-=10;
                            cr=1;}
                    else cr=0;
                    //-------------------------------------
                    c[dlmax-i]=x+'0';}
    //--------------------------------------------------------------------------------------
            c[0]=cr+'0'; // dopisanie ewentualnego przeniesienia
            return c;
    }
    
    char* roznica_(const liczba& a, const liczba& b, int bsr=0)
    {
            int dlmax;
            if(a.getlen()>=b.getlen()) dlmax=a.getlen();
            else dlmax=b.getlen(); // ustalenie dlugosci najdluzszego czynnika
            char* c=new char[dlmax+1]; // rezerwacja pamieci na wynik
    //-------------------zerowanie i terminowanie zmiennej, bedzie przekazana jako c-string--
            for(int i=0; i<dlmax; i++)
                    c[i]='0';
            c[dlmax]='\0';
    //---------------------------------------------------------------------------------------
            signed char x=0, cr=0;
    //-------------------petla odejmowania cyfr------------------------------------------------
            for(int i=0; i<dlmax; i++){
                    x=a.getpos(a.getlen()-1-i)-b.getpos(b.getlen()-1-i+bsr)-cr;
                    //-------------------obsluga pozyczki------------------
                    if(x<0){
                            x+=10;
                            cr=1;}
                    else cr=0;
                    //-----------------------------------------------------
                    c[dlmax-1-i]=x+'0';}
            return c;
    }
    //------------------------funkcja treningowa, brak komentarzy-----------------------------
    char* ilzcyfr(const liczba& a, const char& b)
    {
            int dlmax=a.getlen();
    
            char* c=new char[dlmax+2];
    
            for(int i=0; i<dlmax+1; i++)
                    c[i]='0';
            c[dlmax+1]='\0';
    
            char x=0, cr=0;
            for(int i=0; i<dlmax; i++){
                    x=a.getpos(a.getlen()-1-i)*b+cr;
                    if(x>9){
                            cr=x/10;
                            x=x%10;}
                    else cr=0;
                    c[dlmax-i]=x+'0';}
            c[0]=cr+'0';
            return c;
    }
    //----------------------------------------------------------------------------------------
    char* iloczyn_(const liczba& a, const liczba& b)
    {
            int dla=a.getlen();
            int dlb=b.getlen();
    
            char* c=new char[dla+dlb+2]; // wynik jest niedluzszy niz suma dlugosci obydwu cyfr
            for(int i=0; i<dla+dlb+2; i++)
                    c[i]=0;
            c[dla+dlb+1]='\0';
    
            char* temp=new char[dla+dlb+1]; // zmienna pomocnicza przechowyjaca wynik mnozenia przez pojedyncza cyfre
            for(int i=0; i<dla+dlb; i++)
                    temp[i]=0;
    
            char x=0, cr=0; // zmienne pomocnicze
    //--------------------------------petla cyfr drugiego czynnika---------------------------
            for(int j=0; j<dlb; j++){
            //-----------------------petla cyfr pierwszego czynnika--------------------------
                    for(int i=0; i<dla; i++){
                            x=a.getpos(a.getlen()-1-i)*b.getpos(b.getlen()-1-j)+cr;
                            //-------obsluga przeniesienia-----------------------------------
                            if(x>9){
                                    cr=x/10;
                                    x=x%10;}
                            else cr=0;
                            //---------------------------------------------------------------
                            temp[dla+dlb-j-i-1]=x;}
                    temp[dlb-1-j]=cr; // dopisanie ewentualnego przeniesienia
                    x=0, cr=0; // zerowanie wykorzystanych zmiennych pomocniczych
    //-------------------------------petla sumowania wynikow mnozen przez kolejne cyfry------
                    for(int k=0; k<dla+dlb; k++){
                            x=temp[dla+dlb-1-k]+c[dla+dlb-k]+cr;
                            temp[dla+dlb-1-k]=0;
                            //-------obsluga przeniesienia-----------------------------------
                            if(x>9){
                                    x-=10;
                                    cr=1;}
                            else cr=0;
                            //---------------------------------------------------------------
                            c[dla+dlb-k]=x;}
                    c[0]=cr; // dopisanie przeniesienia
                    x=0, cr=0; // zerowanie wykorzystanych zmiennych pomocniczych
                    }
    //-------------------------------petla konwersji wartosci wyniku na string---------------
            for(int i=0; i<dla+dlb+1; i++)
                    c[i]+='0';
    //---------------------------------------------------------------------------------------
            delete [] temp; // usuniecie zmiennej pomocniczej
            return c;
    }
    //---------------------------------------------------------------------------
    int compare (const liczba& a, const liczba& b, int bsr=0)
    {       //zwraca 1 gdy a>=b*10^bsr  lub 0 gdy a<b*10^bsr
            int dla = a.getlen();
            int dlb = b.getlen()+bsr;
            if(dla>dlb) return 1;
            else if(dla<dlb) return 0;
            else{
                    for(int i=0; i<dla; i++){
                            if(a.getpos(i)==b.getpos(i)) continue;
                            if(a.getpos(i)>b.getpos(i))return 1;
                            else return 0;
                    }
            }
            return 1;
    }
    //---------------------------------------------------------------------------
    char* iloraz_(const liczba& a, const liczba& b)
    {
            int dla=a.getlen();
            int dlb=b.getlen();
            int off=dla-dlb; // roznica rzedow liczb
            char x; // cyfra wyniku
            liczba temp; // zmienna pomocnicza do odejmowania
            char* tempa=equal(a); // Przypisanie stringa odpowiadającego liczbie a do wskaźnika
            temp.set(tempa); // skopiowanie liczby a do zmiennej pomocniczej temp
            delete [] tempa; // zwolnienie pamięci zajmowanej przez string
            char* rozn;
            if(compare(temp, b, off)==0) off--; // jesli po wyrownaniu rzedow dzielnik jest wiekszy od dzielnej zmniejsz jego rzad o jeden
            if (off<0) off=0; // jesli dzielnik jest wiekszy do dzielnej ustaw przesuniecie na 0
            char* c=new char[off+2]; // tablica wynikowa
            for(int i=0; i<off+2; i++)
                    c[i]='0'; // inicjalizacja tablicy wynikowej
            c[off+1]=0; // terminujemy wynikowy ciąg
            int dlc=off;
            while(off>=0){ // zaczynamy od najbardziej znaczacej cyfry wyniku
                    x=0; // zerowanie zmiennej przed obliczeniem kolejnej cyfry wyniku
                    while(compare(temp, b, off)){ // odejmujemy od dzielnej dzielnik pomnozony przez 10^off dopuki wynik nie będzie mniejszy od dzielnika
                            rozn=roznica_(temp, b, off); // to jest konieczne aby zachowac wskaznik do stringa ktory tworzy ta funkcja
                            temp.set(rozn);
                            delete [] rozn; // ... aby po jego zczytaniu moc zwolnic zajmowana przezen pamiec
                            x++; // zliczamy odjecia
                            }
                    c[dlc-off]=x+'0'; // ... i zapisujemy je jako kolejne cyfry wyniku
                    off--; // przechodzimy do kolejnej cyfry wyniku
            }
    
            return(c);
    }
    char* iloraz(const liczba& a, const liczba& b)
    {
            char* wynik=iloraz_(a, b);
            if(a.getsgn()==b.getsgn())return wynik;
            else {
                    int dl=strlen(wynik);
                    char* c=new char[dl+2];
                    c[0]='-';
                    for(int i=0; i<dl+1; i++)
                            c[i+1]=wynik[i];
                    c[dl+1]=0;
                    delete [] wynik;
                    return c;}
    }
    char* iloczyn(const liczba& a, const liczba& b)
    {
            char* wynik=iloczyn_(a, b);
            if(a.getsgn()==b.getsgn())return wynik;
            else {
                    int dl=strlen(wynik);
                    char* c=new char[dl+2];
                    c[0]='-';
                    for(int i=0; i<dl+1; i++)
                            c[i+1]=wynik[i];
                    c[dl+1]=0;
                    delete [] wynik;
                    return c;}
    }
    char* suma(const liczba& a, const liczba& b)
    {
            bool minus=0;
            char* wynik=0;
            if((a.getsgn()==-1)&&(b.getsgn()==1))
                    if(compare(a, b)){
                            wynik=roznica_(a, b);
                            minus=1;}
                    else{
                            wynik=roznica_(b, a);
                            minus=0;}
            else if((a.getsgn()==1)&&(b.getsgn()==-1))
                    if(compare(a, b)){
                            wynik=roznica_(a, b);
                            minus=0;}
                    else{
                            wynik=roznica_(b, a);
                            minus=1;}
            else if((a.getsgn()==-1)&&(b.getsgn()==-1))
                    if(compare(a, b)){
                            wynik=suma_(a, b);
                            minus=1;}
                    else{
                            wynik=suma_(a, b);
                            minus=1;}
            else
                    if(compare(a, b)){
                            wynik=suma_(a, b);
                            minus=0;}
                    else{
                            wynik=suma_(a, b);
                            minus=0;}
    
            if(minus){
                    int dl=strlen(wynik);
                    char* c=new char[dl+2];
                    c[0]='-';
                    for(int i=0; i<dl+1; i++)
                            c[i+1]=wynik[i];
                    c[dl+1]=0;
                    delete [] wynik;
                    return c;}
            else return wynik;
    }
    char* roznica(const liczba& a, const liczba& b)
    {
            bool minus=0;
            char* wynik=0;
            if((a.getsgn()==-1)&&(b.getsgn()==1))
                    if(compare(a, b)){
                            wynik=suma_(a, b);
                            minus=1;}
                    else{
                            wynik=suma_(a, b);
                            minus=1;}
            else if((a.getsgn()==1)&&(b.getsgn()==-1))
                    if(compare(a, b)){
                            wynik=suma_(a, b);
                            minus=0;}
                    else{
                            wynik=suma_(b, a);
                            minus=0;}
            else if((a.getsgn()==-1)&&(b.getsgn()==-1))
                    if(compare(a, b)){
                            wynik=roznica_(a, b);
                            minus=1;}
                    else{
                            wynik=roznica_(b, a);
                            minus=0;}
            else
                    if(compare(a, b)){
                            wynik=roznica_(a, b);
                            minus=0;}
                    else{
                            wynik=roznica_(b, a);
                            minus=1;}
    
            if(minus){
                    int dl=strlen(wynik);
                    char* c=new char[dl+2];
                    c[0]='-';
                    for(int i=0; i<dl+1; i++)
                            c[i+1]=wynik[i];
                    c[dl+1]=0;
                    delete [] wynik;
                    return c;}
            else return wynik;
    }
    
    
    //---------------------------------------------------------------------------
    int main(int argc, char* argv[])
    {
            char* wpis=new char[2000000];
            char* w=0;
            char* ops;
            char op;
            int dalej=1;
            liczba c;
            printf("Uzycie:\n");
            printf("<liczba>[Enter]<operator><liczba>[Enter]<operator><liczba>[Enter]...[=][Enter]\n: ");
            liczba a;
            scanf("%s", wpis);
            while(a.set(wpis)){
                    printf(" NIE LICZBA!!!\n: ");
                    scanf("%s", wpis);}
            do{
                    printf(": ");
                    scanf(" %c", &op);
                    if(op=='='){
                            printf("\nwynik=");
                            a.print();
                            delete [] wpis;
                            break;}
                    scanf("%s", wpis);
                    liczba b;
                    if(b.set(wpis)) {printf(" NIE LICZBA!!!\n"); continue;}
                    switch(op){
                            case '+': w=suma(a, b); break;
                            case '-': w=roznica(a, b); break;
                            case '*': w=iloczyn(a, b); break;
                            case '/': w=iloraz(a, b); break;
                            default: printf("ZLY OPERATOR!!!\n"); w=equal(a);}
                    a.set(w);
                    delete [] w;
                    printf("= ");
                    a.print();
                    printf("\n");
                    }while(dalej);
            printf("\n\n");
            system("pause");
            return 0;
    }
    //---------------------------------------------------------------------------
    

Podsumowanie tematu

✨ Dyskusja dotyczy implementacji operacji arytmetycznych na bardzo dużych liczbach całkowitych, przechowywanych w klasie jako tablica bajtów, po jednym bajcie na pozycję, z reprezentacją stałoprzecinkową bez znaku (później z uwzględnieniem znaku). Autor przedstawił kod klasy liczba oraz zewnętrzne funkcje realizujące podstawowe działania: dodawanie, odejmowanie, mnożenie i częściowo dzielenie. Wskazano, że klasyczne mnożenie jest bardzo wolne przy liczbach o milionach cyfr, dlatego rozważano szybsze algorytmy, takie jak Karatsuba i FFT, które znacząco skracają czas obliczeń. Poruszono temat konwersji między systemem dziesiętnym a binarnym, z uwagi na efektywność operacji binarnych, oraz przedstawiono algorytm szybkiej konwersji. Dzielenie początkowo realizowane było przez odejmowanie, co jest nieefektywne, ale zaproponowano usprawnienia poprzez skalowanie dzielnika. W kolejnych wersjach kodu dodano obsługę liczb ze znakiem. W dyskusji pojawiły się uwagi dotyczące błędów w alokacji pamięci i poprawności działania dzielenia. Ostatecznie zaprezentowano działający, choć nieoptymalny, kod realizujący podstawowe operacje na bardzo dużych liczbach całkowitych, z możliwością dalszej optymalizacji i rozbudowy.
Wygenerowane przez model językowy.
REKLAMA