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.