7. Obojsmerný lineárny zoznam

7.2 Problém – Faktoriál

Vytvorte program na výpočet hodnoty n! (n-faktoriálu) s využitím vhodnej dynamickej údajovej štruktúry.


Riešenie

Program na výpočet faktoriálu pre dané prirodzené číslo n nechýba v úvode žiadneho kurzu programovania.
 
Ak vám niekto „prikáže naprogramovať faktoriál,“ spomeniete si na matematickú definíciu:
 

 
a po chvíli je na svete program:

int n, i;
unsigned int fakt = 1;
scanf("%d", &n);
for (i = 1; i <= n; i++) fakt *= i;
printf("%d ! = %d", n, fakt);

Algoritmus je síce v poriadku, avšak program bude fungovať len ak n<13. Prečo? Ak údajový typ unsigned int zaberá v pamäti 4 bajty, tak výsledná hodnota 13! = 6227020800 je už väčšia ako maximálna prípustná hodnota tohto typu. My však chceme určiť hodnotu n! pre veľké n – koľko je 100!, 789!, 1500! … ? Zaujímať nás bude navyše nielen samotná hodnota výsledku, ale i počet cifier.


Pri riešení využijeme dynamickú údajovú štruktúru obojsmerný lineárny zoznam. Cifry faktoriálu budú uložené v jeho prvkoch – v programe ich vypočítame „manuálne“ postupným násobením cifru po cifre s pamätaním prenosu. Algoritmus okrem iného spôsobu uchovávania hodnoty faktoriálu v podstate kopíruje „tradičné“ riešenie s využitím cyklu so známym počtom opakovaní. Postupujeme takto:

Najskôr si pripravíme údajovú štruktúru, v ktorej budeme priebežne uchovávať medzivýsledok. Pôjde o obojsmerný zoznam prvkov typu TPrvok:

typedef struct prvok {
	char cifra;
	struct prvok *predch;
	struct prvok *nasled;
} TPrvok;
Vytvorenie prvého prvku s hodnotou 1 môžeme pokladať za úvodnú inicializáciu:
 

 
Prvý prvok zatiaľ nemá predchodcu ani nasledovníka, prvá cifra je zároveň poslednou (keďže je jediná 🙂).

Potom už zrealizujeme vlastný výpočet. Postupne v cykle for vypočítame hodnotu 1!, 2!, 3!, …, (n − 1)! a v poslednom opakovaní napokon n!
 
Nech n = 8. Na obrázku je znázornený zoznam po výpočte hodnoty 7! = 5040:
 

 
Počítajme teraz hodnotu 8! = 8.7!
 
Násobíme od poslednej cifry, pomocný ukazovateľ preto nastavíme na posledný prvok v zozname:
 
cislica = posl;
 
násobíme:
 

 
cislica = cislica->predch;
 

 
cislica = cislica->predch;
 

 
cislica = cislica->predch;
 

 
Prešli sme postupne celým zoznamom. Posledný prenos je ale rôzny od nuly. Preto pridáme na začiatok zoznamu nový prvok (ďalšiu cifru):
 
cislica = (TPrvok*)malloc(sizeof(TPrvok));
 

 
Pridať nový prvok znamená nielen alokovať pamäť, ale aj naplniť jeho hodnotovú časť príslušnou cifrou a nastaviť správne ukazovatele. Prvok pridávame na začiatok – predchodcu teda nemá. Aktualizujeme aj hodnotu ukazovateľa na prvú cifru (máme nový začiatok zoznamu).

Voľba údajovej štruktúry sa ukázala výhodná – v jednom smere prechádzame zoznamom pri násobení po cifrách, v opačnom smere pri výpise výsledku.

Pre väčšie n nemusí byť posledný prenos jednociferné číslo! Na začiatok pôvodného zoznamu sa potom pridáva viac cifier (nových prvkov). Počet cifier výsledku môžeme zistiť pri výpise výsledku na obrazovku.

[Program]