Tutorial
A mo¿e by tak jeszcze szybciej?
Je¿eli kto¶ bardzo chce, to mo¿e zaimplementowaæ jeszcze szybszy algorytm ni¿ ten, który widzieli¶cie przed chwil±. Wygl±da on nastêpuj±co:
var
d,n,i : longint;
function fib(x: longint): longint;
{
funkcja zostala wyprowadzona ze wzoru:
_ _ x _ _
| 0 1 | | fib(x-2) fib(x-1) |
| | = | |
|_1 1_| |_fib(x-1) fib(x) _|
}
var
i,j,k,h,t: longint;
begin
i:=1; j:=0; k:=0; h:=1; t:=0;
while x>0 do
begin
if odd(x) then
begin
t:=(j*h) mod 10000;
j:=(i*h+j*k+t) mod 10000;
i:=(i*k+t) mod 10000;
end;
t:=(h*h) mod 10000;
h:=(2*k*h+t) mod 10000;
k:=(k*k+t) mod 10000;
x:=x div 2;
end;
fib:=j;
end;
begin
readln(d);
while (d>0) do
begin
d:=d-1;
readln(n);
writeln(fib(n));
end;
end.
Nie wnikajmy dlaczego to dzia³a, bo wymaga to trochê bardziej zaawansowanej matematyki (raczej nie wymaganej na zawodach), ale dzia³a ;). Dlatego Sprawdzarka równie¿ oceni ten program na:
Accepted
Powy¿szy program ma z³o¿ono¶æ czasow± O(logN), czyli znacznie lepsz± od z³o¿ono¶ci O(N) poprzedniego programu. W ogólno¶ci powinni¶cie zaimplementowaæ program o jak najlepszej z³o¿ono¶ci, ale w tym konkretnym przypadku nasz wysi³ek by³ nadmiarowy, bo ju¿ poprzedni program bez trudu zosta³by zaakceptowany. Sk±d ta pewno¶æ? Ze z³o¿ono¶ci O(N) oraz z faktu, ¿e N ≤ 20000 mo¿na wywnioskowaæ, ¿e dla ka¿dego zestawu testowego program wykona rzêdu 20000 operacji w najgorszym wypadku (pamiêtajcie, ¿e to tylko zgrubne oszacowanie!), co zajmie mu pojedyncze milisekundy (dopiero wchodz±c w miliony operacji czas zaczyna byæ zauwa¿alny). Sprawdzarka tego nie wychwyci (limity czasu raczej nie s± mniejsze od 50ms). Z drugiej strony, je¶li Wasz program bêdzie wykonwyaæ du¿o operacji, nawet setki milionów czy miliardy, to jeszcze nie znaczy, ¿e jest z³y (choæ nie spodziewaliby¶my siê limitów powy¿ej kilku sekund to nie mo¿na tego wykluczyæ). Mo¿e przecie¿ nie istnieæ lepszy algorytm. To do Was nale¿y ocenienie czy tak jest. Np. gdyby w tym zadaniu by³o ograniczenie N ≤ 2*109 to poprzedni program bêdzie dzia³aæ d³ugo, a ¿e istnieje du¿o lepszy, to nale¿y przyj±æ, ¿e dosta³by ocenê Time Limit Exceeded. Podsumowuj±c, z ograniczeñ na dane mo¿na wyci±gn±æ po¿yteczne wnioski, ale podchod¼cie do tego ostro¿nie i sceptycznie, bo mo¿e to Was te¿ zmyliæ — robicie to na w³asn± odpowiedzialno¶æ!
Inna mo¿liwo¶æ ulepszenia poprzedniego programu (równie zbêdna w tym przypadku, ale wspominamy, bo technikê mo¿na u¿yæ przy innych zadaniach) to wyliczenie wszystkich liczb Fibonacciego raz, by dla ka¿dego zestawu testowego ju¿ j± tylko wypisaæ. Uwa¿ajcie jednak, ¿eby nie liczyæ liczb wiêkszych ni¿ pojawiaj± siê na wej¶ciu, bo testy wcale nie musz± zawieraæ du¿ych liczb (dla takich testów oczywi¶cie limity czasowe bêd± odpowiednio mniejsze). Kolejna mo¿liwo¶æ ulepszenia to wygenerowaæ na swoim stanowisku program z tablic± statyczn± zawieraj±c± wszystkie wyniki. Jednak w tym przypadku to nie przejdzie, poniewa¿ wysy³any kod ¼ród³owy nie mo¿e mieæ wiêcej ni¿ 20kB.
Warto tutaj jeszcze zaznaczyæ, ¿e kluczem do szybko¶ci dzia³ania programu jest z³o¿ono¶æ czasowa algorytmu. Raczej nie spodziewajcie siê, ¿e maj±c kiepski algorytm ulepszycie go zwyk³ymi optymalizacjami kodu czy specjaln± obs³ug± przypadków szczególnych (nawet napisanie programu w assemblerze nie pomo¿e). Z drugiej strony, maj±c algorytm o poprawnej z³o¿ono¶ci, najprawdopodobniej zmie¶ci siê on w limitach czasu, ... no chyba ¿e napiszecie go wysoce nieoptymalnie.
