[C++] Obliczanie silni dla dużych liczb

mario765i
Użytkownik
Użytkownik
Posty: 22
Rejestracja: 23 paź 2011, o 10:47
Płeć: Mężczyzna
Lokalizacja: Wrocław
Podziękował: 2 razy

[C++] Obliczanie silni dla dużych liczb

Post autor: mario765i »

Witam! Mam do napisania program następującej treści: Podaję na wejściu liczbę naturalną n (0<=n<=300), a na wyjściu otrzymuję n!. Napisałem program obliczający silnię dla małej liczby, oto kod:

Kod: Zaznacz cały

#include<iostream>
#include<cstdio>
using namespace std;

int main()
{
    int n, w=1;
    scanf("%d",&n);

    if (n==0) printf("%d
",w);
    else
    {
        while (n>0)
        {
            w=w*n;
            n=n-1;
        }
        printf("%d
",w);
    }

    return 0;
}
Poczytałem trochę na necie i dowiedziałem się, że do tego programu potrzebna jest funkcja, która dodaje dwie długie liczby oraz funkcja, która mnoży dwie długie liczby. Oto kody tych funkcji:

Kod: Zaznacz cały

#include<iostream>
using namespace std;

// dwie bardzo proste funkcje
inline int min(int a, int b) { if (a<b) return a; return b; }
inline int max(int a, int b) { if (a>b) return a; return b; }

// funkcja odwraca kolejność w tablicy
void reverse( int *tab, int n )
{
	int i=0, j=n-1;
	while (i<j)
	{
		swap( tab[i], tab[j] );
		i++; j--;
	}
}

// Funkcja dodaje dwie liczby, których cyfry zapamiętane są w tablicach
void add( int *A, int n , int *B, int m, // dane : dwie tablice z cyframi (długie liczby)
          int *C, int &d )               // wynik: tablica z cyframi, liczba d
{                                        // zależność : C = A+B ,  d = rozmiar tablicy C
	// odwracamy liczby dla uproszczenia algorytmu
	reverse(A, n); reverse(B, m);

	// algorytm dodawania : złożoność O( n+m )
	int r = min(n,m);
	int carry = 0;
	for(int i=0; i<r; i++)
	{
		int q = A[i] + B[i] + carry; // q<20
		C[i] = q%10;
		carry  = q/10; // carry<2
	}
	for (int i=r; i<n; i++)
	{
		int q = A[i] + carry; // B[i]==0
		C[i] = q%10;
		carry  = q/10;
	}
	for (int i=r; i<m; i++)
	{
		int q = B[i] + carry; // A[i]==0
		C[i] = q%10;
		carry  = q/10;
	}

	d = max(n,m);
	if (carry>0)
	{
		d++;
		C[ d-1 ] = carry;
	}

	// odwracamy wynik
	reverse(C, d);
	// odwracamy liczby A i B
	reverse(A, n); reverse(B, m);
}

Kod: Zaznacz cały

#include<iostream>
using namespace std;

// funkcja odwraca kolejność w tablicy
void reverse( int *tab, int n )
{
	int i=0, j=n-1;
	while (i<j)
	{
		swap( tab[i], tab[j] );
		i++; j--;
	}
}

// Funkcja mnoży dwie liczby, których cyfry zapamiętane są w tablicach
void mult( int *A, int n , int *B, int m, // dane : dwie tablice z cyframi (długie liczby)
           int *C, int &d )               // wynik: tablica z cyframi
{                                         // zależność : C = A*B ,  d = rozmiar tablicy C
	// odwracamy liczby dla uproszczenia algorytmu
	reverse(A, n); reverse(B, m);

	// zerowanie wyniku
	for (int i=0; i<n+m; i++) C[i] = 0;

	// algorytm mnożenia : złożoność O( n*m )
	for(int i=0; i<n; i++)
	{
		int carry = 0;
		for (int j=0; j<m; j++)
		{
			int q = A[i]*B[j] + carry + C[i+j]; // q<100
			C[i+j] = q%10;
			carry  = q/10; // carry<10
		}
		if (carry>0) C[i+m] = carry;
	}

	// usuwanie wiodących zer
	d = n+m; // długość liczby C
	while (d>1 && C[d-1]==0) d--;

	// odwracamy wynik
	reverse(C, d);
	// odwracamy liczby A i B
	reverse(A, n); reverse(B, m);
}
I tutaj jest moja prośba. Mógłby ktoś pomóc mi połączyć te funkcje z programem liczącym silnię dla dużej liczby.
mario765i
Użytkownik
Użytkownik
Posty: 22
Rejestracja: 23 paź 2011, o 10:47
Płeć: Mężczyzna
Lokalizacja: Wrocław
Podziękował: 2 razy

[C++] Obliczanie silni dla dużych liczb

Post autor: mario765i »

Jest to program do wysłania na sprawdzarkę. W treści zadania jest wskazówka, która mówi o skorzystaniu z funkcji mnożenia dwóch długich liczb. Wnioskuję więc, że nie mogę korzystać z zewnętrznych bibliotek.
wawek91
Użytkownik
Użytkownik
Posty: 795
Rejestracja: 2 cze 2010, o 08:56
Płeć: Mężczyzna
Lokalizacja: Tarnów
Podziękował: 14 razy
Pomógł: 66 razy

[C++] Obliczanie silni dla dużych liczb

Post autor: wawek91 »

No ok czyli skoro masz skorzystać z funkcji mnożenia 2 dużych liczby to funkcja dodawania dwóch dużych liczb jest Ci zbędna. Zatem w swoim programie (poza przepisaniem kodów funkcji reverse i mult) musisz zapisywać liczby w tablicach (ponieważ tego wymaga funkcja reverse i mult). Reszta odbywa się podobnie jak u Ciebie tylko zamiast mnożyć jedna liczba * druga liczba to po prostu przekazujesz do funkcji mult te 2 liczby ale zamieszczone w tablicach. Wynik masz w tablicy C (tak jest nazwana w funkcji mult) i znowu tą tablice przekazujesz do funkcji mult i tak w kółko i w kółko jak to w bywa w silnii. Spróbuj sam pokombinować jak coś pomoge.
mario765i
Użytkownik
Użytkownik
Posty: 22
Rejestracja: 23 paź 2011, o 10:47
Płeć: Mężczyzna
Lokalizacja: Wrocław
Podziękował: 2 razy

[C++] Obliczanie silni dla dużych liczb

Post autor: mario765i »

Jeśli dobrze zrozumiałem, to chodzi Ci o coś takiego:

Kod: Zaznacz cały

#include<iostream>
using namespace std;

// funkcja odwraca kolejność w tablicy
void reverse( int *tab, int n )
{
	int i=0, j=n-1;
	while (i<j)
	{
		swap( tab[i], tab[j] );
		i++; j--;
	}
}

// Funkcja mnoży dwie liczby, których cyfry zapamiętane są w tablicach
void mult( int *A, int n , int *B, int m, // dane : dwie tablice z cyframi (długie liczby)
           int *C, int &d )               // wynik: tablica z cyframi
{                                         // zależność : C = A*B ,  d = rozmiar tablicy C
	// odwracamy liczby dla uproszczenia algorytmu
	reverse(A, n); reverse(B, m);

	// zerowanie wyniku
	for (int i=0; i<n+m; i++) C[i] = 0;

	// algorytm mnożenia : złożoność O( n*m )
	for(int i=0; i<n; i++)
	{
		int carry = 0;
		for (int j=0; j<m; j++)
		{
			int q = A[i]*B[j] + carry + C[i+j]; // q<100
			C[i+j] = q%10;
			carry  = q/10; // carry<10
		}
		if (carry>0) C[i+m] = carry;
	}

	// usuwanie wiodących zer
	d = n+m; // długość liczby C
	while (d>1 && C[d-1]==0) d--;

	// odwracamy wynik
	reverse(C, d);
	// odwracamy liczby A i B
	reverse(A, n); reverse(B, m);
}


int main()
{
	int x[1000], y[1000], z[2000];
	int n,       m,       d;

	cin >> n;
	for (int i=0; i<n; i++)
	{
	    cin >> x[i]; // cyfry pooddzielane spacjami
	}

	cin >> m;
	for (int j=0; j<m; j++)
	{
	    cin >> y[j]; // cyfry pooddzielane spacjami
	}

	mult(x,n, y,m, z,d);
	for (int i=0; i<d; i++)
	{
		cout << z[i]; // wynik (bez spacji)
	}

	return 0;
}
Tylko nie wiem jak teraz policzyć silnie.
wawek91
Użytkownik
Użytkownik
Posty: 795
Rejestracja: 2 cze 2010, o 08:56
Płeć: Mężczyzna
Lokalizacja: Tarnów
Podziękował: 14 razy
Pomógł: 66 razy

[C++] Obliczanie silni dla dużych liczb

Post autor: wawek91 »

Ok nie chce mi się sprawdzać kompilatorem, ale to co napisałeś powinno dobrze mnożyć 2 duże liczby tak? Jeśli działa dobrze to teraz popatrz na swój kod (ten w pierwszym poście) i zobacz jak liczyłeś silnie. Zrób trochę inaczej (tzn pętle while zamień na taką która idzie od dołu do góry (tzn zmień warunek i inkrementuj zamiast dekrementować). I teraz przyda się nasza funkcja dodawania. Stwórz tablicę (tak jak wcześniej u Ciebie to 'n') gdzie na początku będzie zwykła 1. Następnie w każdym kolejnym obiegu pętli modyfikuj tą tablicę tzn. dodawaj do niej 1 (pamiętaj że ta dodawana jedyna musi być zapisana w tablicy bo tak działa funkcja add) i przemnażaj poprzedni wynik mnożenia razy właśnie ta nowa wartość. Nie wiem czy Ci nie zamotałem ale może mnie zrozumiałeś.
mario765i
Użytkownik
Użytkownik
Posty: 22
Rejestracja: 23 paź 2011, o 10:47
Płeć: Mężczyzna
Lokalizacja: Wrocław
Podziękował: 2 razy

[C++] Obliczanie silni dla dużych liczb

Post autor: mario765i »

Próbowałem coś z tym zrobić. Oto wynik:

Kod: Zaznacz cały

#include<iostream>
using namespace std;

// funkcja odwraca kolejność w tablicy
void reverse( int *tab, int n )
{
	int i=0, j=n-1;
	while (i<j)
	{
		swap( tab[i], tab[j] );
		i++; j--;
	}
}

// Funkcja mnoży dwie liczby, których cyfry zapamiętane są w tablicach
void mult( int *A, int n , int *B, int m, // dane : dwie tablice z cyframi (długie liczby)
           int *C, int &d )               // wynik: tablica z cyframi
{                                         // zależność : C = A*B ,  d = rozmiar tablicy C
	// odwracamy liczby dla uproszczenia algorytmu
	reverse(A, n); reverse(B, m);

	// zerowanie wyniku
	for (int i=0; i<n+m; i++) C[i] = 0;

	// algorytm mnożenia : złożoność O( n*m )
	for(int i=0; i<n; i++)
	{
		int carry = 0;
		for (int j=0; j<m; j++)
		{
			int q = A[i]*B[j] + carry + C[i+j]; // q<100
			C[i+j] = q%10;
			carry  = q/10; // carry<10
		}
		if (carry>0) C[i+m] = carry;
	}

	// usuwanie wiodących zer
	d = n+m; // długość liczby C
	while (d>1 && C[d-1]==0) d--;

	// odwracamy wynik
	reverse(C, d);
	// odwracamy liczby A i B
	reverse(A, n); reverse(B, m);
}


int main()
{
	int x[1000], y[1000], z[2000];
	int n,       m,       d;

int N, W=1;
cin >> N;

	cin >> n;
	for (int i=0; i<n; i++)
	{
	    cin >> x[i]; // cyfry pooddzielane spacjami
	}

	cin >> m;
	for (int j=0; j<m; j++)
	{
	    cin >> y[j]; // cyfry pooddzielane spacjami
	}

	mult(x,n, y,m, z,d);
	for (int i=0; i<d; i++)
	{
		cout << z[i]; // wynik (bez spacji)
	}

if (N==0) cout << W;
else
{
    while (N<=300)
    {
        W=W*N;
        N=N+1;
    }
    cout << W;
}

	return 0;
}
Nie wiem za bardzo jak zrealizować w programie to co napisałeś. Jestem bardzo początkujący, jeśli chodzi o programowanie. Nie wiem czy moja wiedza wystarczy do napisania tego programu. Oczywiście będę próbował...
wawek91
Użytkownik
Użytkownik
Posty: 795
Rejestracja: 2 cze 2010, o 08:56
Płeć: Mężczyzna
Lokalizacja: Tarnów
Podziękował: 14 razy
Pomógł: 66 razy

[C++] Obliczanie silni dla dużych liczb

Post autor: wawek91 »

Twoje W powinno być jako Twój x, natomiast N powinno być zadeklarowane tak: int N[1]; N[0] = 1;
Xitami

[C++] Obliczanie silni dla dużych liczb

Post autor: Xitami »

Kod: Zaznacz cały

WIELKA_LICZBA silnia(int n){
    WIELKA_LICZBA s=1;
    while(n>1)
        s *= n--;
    return s;}
gdzie tu miejsce na mnożenie dwu wielkich liczb?

chyba, że dysponujemy mnożeniem opartym o metodę Karatsuby i FFT to wtedy silnię liczy się zupełnie inaczej.
temu algorytmowi metody te nie pomogą

eBkiq

-- 13 listopada 2011, 08:37 --

ile razy wykona się fragment kodu int q = A*B[j] + carry + C[i+j];
czyli mnożenie małej przez małą?
dodaj sobie tam jakiś licznik i zobacz jak rośnie ze wzrostem n
łatwo o kilkukrotne przyśpieszenie, zamiast jednej cyfry w komórce tablicy można przechowywać np 4,
wtedy w odpowiednim miejscu będzie nie %10 i /10 tylko %10000 i /10000
i możesz liczyć nawet 420'000! (2'179'289 cyfr) 2^32/420'000<10'000

jeżeli "n" miałoby być "długą" liczbą, czyli większą od 2^31 (bo dla takich ma to sens)
to myślę, że prędzej słońce zgaśnie nim doczekasz się wyników

wyrzuć to co masz i zacznij od początku
potrzebujesz tylko mnożenia "długiej" przez int,
program będzie ze 3 razy krótszy, znacznie prostszy i wielokrotnie szybszy
bardzo prostym programem policzyłem 40000! w dwie sekundy
ODPOWIEDZ