MWPZ - Mistrzostwa Wielkopolski w Programowaniu Zespolowym

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.