Sunteți pe pagina 1din 6

Uobiajne greke na nacionalnim takmienjima iz programiranja

Autor: Andreja Ili e-mail: andrejko.ilic@gmail.com

Svake godine Komisija za organizaciju takmienja iz programiranja organizuje takmienja za uenike osnovnih i srednjih kola. Godinji ciklus takmienja se sastoji od etiri nivoa takmienja: okrunog takmienja, dravnog takmienja, srpske informatike olimpijade (SIO) i izbornog takmienja. Izborno takmienje se organizuje radi odabira nacionalnog tima, koji de uestvovati na meunarodnim takmienjima. Ovaj nivo takmienja je vanredan nivo, koji ne mora uvek biti odran. Nedavno je uvedeno i dodatno kvalifikaciono takmienje za uenike srednjih kola, kao takmienje najnieg ranga. Ovaj lanak namenjen je svim takmiarima, kako poetnicima tako i onim iskusnijim. Uenici koji poinju svoje takmiarske dane, meu ovim redovima mogu nadi korisne savete i unapred se upoznati sa nekim uobiajnim grekama bivih takmiara. Sa druge strane, iskusnijim takmiarima bih preporuio da sve jedno prou kroz ovaj tekst, jer nekada ove greke mogu imati velikog uticaja. Iskustvo je samo lep naziv za kojim nazivamo nae greke. Sav materijal je sakupljen tokom dugogodinjeg autorovog ueda na nacionalnim i meunarodnim takmienjima - bilo kao takmiar ili kao lan komisije. Sve to budete proitali barem jednom se stvarno dogodilo. Dalji tekst je organizovan u manje sekcije gde de svaka od njih analizirati neku od greaka (ili klasu slinih). Sekcije nisu sortirane, meutim preporuljivo je itati ih redom zbog raznih komentara i sugestija. Neke od njih de sadrati delove naznaene kao savet, na koje treba obratiti posebnu panju. Ukoliko imate komentara ili dodatnih sugestija povodom teksta, nemojte oklevati da se obratite bilo autoru bilo takmiarskoj komisiji.

Datoteke
Jedna od najstandardnijih greaka naih takmiara, naalost ne retko i onih iskusnijih, vezana je za samu komunikaciju sa datotekama. Najfrekventnije su sledede: Pogrean naziv ulazne / izlazne datoteke: Pod ovim podrazumevamo i same tamparske greke ali i one gde se datotekama pristupa relativnom putanjom (primera radi D:\Okruzno takmicenje\nazivProblema.in). Ukoliko i pristupate relativnom putanjom u toku rada, kada predajete svoj kod pazite da ovu putanju promenite. Ispisivanje dodatnih informacija u izlaznoj datoteci: Takmiari esto tampaju dodatne informacije u datotekama prilikom analize i testiranja njihovih programa. Naravno ovo je u redu koristiti pri debug-iranju, ali treba voditi rauna da se na kraju ove stvari uklone iz koda. Pored

toga panju posvetiti i formatu izlaza ne stavljati novi red na kraju, brojeve razdvajati na nain specifiran u problemu... Nezatvaranje izlazne datoteke: U Pascal-u ukoliko se nakon ispisa traenih informacija u datoteku, naroito onih vedih, datoteka ne zatvori (Close()) neke od informacija koje su upisane se mogu izgubiti. Zatvaranje datoteka podrazumeva i flush-ovanje podataka, tako da moete biti sigurni da podaci nede iscureti.

Mnogi de redi da su ovo nebitne greke koje nemaju veze sa samim algoritmom, to je tano, i kao takve ukoliko do njih doe treba ih ispraviti nakon takmienja. Meutim u pravilniku pie: albe koje su zasnovane na izvornom kodu ili ispisu u izlazni fajl se po strogom pravilu odbijaju. Takmiar nema pravo da trai da mu se ispravi greka u izvornom kodu, bez obzira koliko mala ona bila. Moda zvui surovo ali ovakva pravila moraju postojati. U suprotnom vrlo brzo bi dolo do pitanja gde onda povudi granicu za menjanje koda, poto de se dobijati svakakve albe ablona: ukoliko se u mom kodu na tom i tom mestu promeni karakter u moj algoritam bi bio korektan. Uvek odvojite 10ak minuta na kraju takmienja gde dete proveriti ovu i sline mogude greke (neke od njih demo obratiti u daljem tekstu) koje vas mogu skupo kotati. Zadnjih 10ak minuta vam ne mogu mnogo znaiti u implementaciji ideja, ali vas svakako mogu spasiti ovih previda.

Veliine nizova i matrica


Pored gore navedenog problema sa datotekama, takmiari esto prave sline greke vezane za veliine nizova ili matrica. Starija okruenja imaju jako mali memorijski limit (kod ranijih verzija Pascal-a ak 64k). Takmiari su tada prinueni da pri kodiranju i testiranju definiu male dimenzije niza i matrica, sa idejom da na kraju promene na odgovarajude. Takva implementacija se ne bi kompajlirala na lokalnom raunaru, meutim na takmienjima predati kodovi kasnije komplajliraju zvaninim kompajlerom koji podrava memorijska ogranienja data u problemu1. Meutim neretko takmiari zaborave da promene ove vrednosti, to naalost dovodi to toga da ideja dobija mnogo manje bodova nego to zasluuje. Na kraju obavezno proverite ogranienja! Korisno bi bilo veliine nizova i matrica pamtiti kao konstante na poetku programa. Ukoliko bi koristili hardkodiranu veliinu (to nikada nije preporuljivo), na kraju bi morali da menjate dosta vrednosti u implementaciji dok je u ovom sluaju dovoljno promeniti samo par karaktera. Sa druge strane i sam kod je pregledniji.
#define MAXN 1001 odnosno const MAXN = 1001;

Preporuljivo je da definisati veliine barem za vede od onih datih u problemu. Ovim moete izbedi neke dodatna ispitivanja za granice indekasa ukoliko imate odreene rekurentne veze. Takoe, ne zaboravite da u C/C++ -u indeksi poinju od .
1

Detaljnije o programskim okruenjima i kompajlerima koji se koriste na zvaninim takmienjima moete pogledati u pravilniku za srednjokolsla takmienja na adresi: http://www.yuoi.nis.edu.rs/pravilnik.html

Opseg promenjivih
Jo jedana jako jednostavna, a kobna, greka je vezan za opseg promenjivih. Takmiari jako brzo prelazne sa samu implementacionu fazu, tako da esto zaboravljaju da posvete panju ogranienjima promenjivih kako ulaznih tako i onih potrebnih za uvanje meurezultata. Zavisno od programskog jezika koji koristite, tipovi podataka imaju razliite opsege. Primera radi u Pascal-u tip podataka Integer ima opseg dok LongInt obuhvata [ . Skoro da ne proe takmienja a da neki uenik nije obratio panju na ulazna ogranienja i definisao elemente niza tipom koji ne moe obuhvatiti vrednosti. Malo komplikovaniji problem je definisanje tipa za neke usputne promenjive. esto je potrebno njih definisati nekim vedim tipom: Int64 za Pascal odnosno long long za C/C++. Pre poetka same implementacije pokuajte da na papiru izdvojite okvirno koje usputne promenjive i strukture de vam biti potrebne. Posebnu panju obratite na tipove kao i mogud opseg pomodnih promenjivih. Ukoliko niste sigurno i dvoumite se izmeu dva tipa, a memorijsko ogranienje vam to doputa, uvek odaberite vedi.

Debug-iranje preko ekrana


Tokom srednjokolskog kolovanja, a naalost nekada i akademskog, vedina uenika nije imala priliku da se upozna sa Debug okruenjem. Alternativni nain ispravljanja implementacionih greaka je ispisivanje dodatnih informacija na ekran (trenutne vrednosti promeljivih, uslova, vrednosti odreenih izraza...). Dodatno ispisivanje moe dosta povedati vreme izvravanja, tako da takmiari mogu dobiti Time Limit Exceeded za pojedine test primere iako je algoritam korektan (poto se standardni izlaz ignorie ukoliko se u problemu trai ispis u datoteci). Sa druge strane ukoliko se ispis reenja vri na standardni izlaz, a ne preko datoteka, dobijamo netano reenje zbog suvinih infromacija. Kod pojedinih C/C++ kompajlera nakon izvravanja programa, komandni prozor se automatski zatvara. Zbog ovoga nije mogude videti reenje ili dodatne informacija koje su ispisane. Kako bi se ovo zaustavilo, takmiari esto na kraju pograma: pauziraju proces: system(pause); uitavaju dodatni karakter: scanf (%c, &c) karakter ReadLn(s)

Oba prisupa de nam pomodi u testiranju. Meutim, kako i kod nekih ranijih greaka o kojima smo priali, nikako nemojte zaboraviti da uklonite ovo pre predaje kodova.

Moduo
Operacija koja rauna ostatak pri deljenju2 dva cela broja se naziva moduo. Skoro sigurno se svaki takmiar nekada upustio u kotac sa nekim od zamki koje moduo moe da nam postavi. Ovde demo izneti neke od njih. U daljem tekstu demo operaciju oznaavati sa . Problem se performansama: Raunanje modula je jako sporo. Neki kompajleri imaju otpimizacije za odredjenje vrednosti modula. U vedini sluajeva, moduo je implementiran kao: . Jako esto je potrebno traiti ostatak pri deljenju sa stepenom dvojke. Ovo se moe izraunati u konstantom vremenu koristedi bit operacije:

Rezultat po modulu: Kod nekih takmiarskih problema, rezultat moe biti jako veliki broj. Kako se od takmiara ne bi dodatno zahtevalo da implementira rad sa velikim brojevima, obino se trai rezultat po nekom datom modulu. Za vrednost modula se uzima veliki prost broj, kako bi se dodatno izbeglo dobijanje tanog rezultata za nekorektan algoritam (Zato?). Meutim, vedina takmiara grei tako to reenje tokom celog algoritma vue i tek na kraju pre tampanja uzme njegov moduo. Na ovaj nain dobija se prekoraenje. Sa druge strane, zbog gore navedenog problema, nije pametno ni u svakom koraku uzimati moduo. Ovo moete izbedi nekada ispitivanjem uslova:
sol = 0; for i = 1 to n do sol = f (sol); sol = sol mod m; sol = 0; for i = 1 to sol = f if (sol sol n do (sol); >= m) = sol mod m;

Sporija varijanta

Bra varijanta

Ukoliko imate neke dodatne informacije o tome kako se moe menjati vrednost promenjive u toku iteracije, moduo moete i runo raunati. Primera radi ukoliko znate da se u svakoj iteraciji vrednost ne moe povedati za vie od puta, to moete implementirati ovako:
sol = 0; for i = 1 to n do sol = f (sol); sol = sol mod m; sol = 0; for i = 1 to sol = f if (sol sol n do (sol); >= m) = sol m;

Sporija varijanta

Bra varijanta

Izraz po modulu: Nekada se i pri samom raunanju izraza moe dodi do prekoraenja, dok po modulu to naravno nije sluaj. Razmotrimo primer: . Pri izvravanju ovog izraza prvo se rauna vrednost proizvoda a tek onda ostatak pri deljunju sa . Ukoliko su promenjive i

Za vie informacija o ovome i kako se ostatak rauna moete pogledati bilo koju knjigu iz teorije brojeva.

jako velike, njihov proizvod moe dovesti do prekoraenja tj. kao rezultat bi dobili neki udan broj. Ukoliko niste sigurni da li moete dobiti prekoraenje, izraz moete raunati kao:

Moduo negativnih brojeva: Runanje modula negativnih brojeva moe biti neoekivano na prvi pogled. Posmatrajmo to kroz primer ispitivanje da li je ceo broj neparan. Ukoliko bi samo ispitali da li je , dobili bi smo greku za negativne vrednosti tada je rezultat moduo operacije jednak .

Gore opisane probleme, kao i sve ostale u ovom tekstu, bi trebalo isprobati u toku priprema. Na takmienjima ovakvi sitni problemi mogu kotati dosta vremena. Zato je bolje biti pripremljen barem za ove standardnije zamke.

Ispitivanje jednakosti dva realna broja


Pri manipulisanju sa realnim vrednostima, jako esto se mogu dobiti neoekivani rezultati. Rad sa njima je po prirodi netaan i kao takav moe proizvesti rezltat sastavljen samo od uma. Jedan od glavnih problema numerike matematike je upravo odreivanje koliko je rezultat nekog metoda korektan koliko odstupa od prave teoretske vrednosti. Treba imati na umu da se brojevi pamte u binarnom sistemu sa odreenom preciznodu. Jako esto se u problemu trai ispitivanje jednakosti dva realna broja. Kako se preciznost menja sa porastom vrednosti realnih brojeva (double i float), najbolji nain je uporeivati ih sa pragom greke koja de zavisiti od samih vrednosti koje uporeujemo.
if (Abs(A B) < delta * Abs(A)) return true; else return false; if (A == B) return true; else return false;

Korektna varijanta

Pogrena varijanta

U gornjem kodu je vrednost izabrana tako da oznaava stepen slinosti. Naravno treba voditi rauna da vrednost promenjive nije . Takoe i samu vrednost treba paljivo birati, shodno traenoj preciznosti u samom problemu. Varijacija gornjih primera moe biti i uslov:
if (Abs(A B) < delta)

Meutim ova varijanta takoe moe dovesti do odreenih problema. Ako se uporeivanje vrednosti menjaju, granicu greke je moda potrebno smanjiti ako se mali brojevi koji bi trebalo biti razliiti pribliavaju zbog fiksirane preciznosti. Slian sluaj moemo imati i sa velikim brojevima.

Moda najbolji pristup ovom problemu bi bio preko relativne razlike:


double RelDif(double a, double b) { double a1 = Abs(a); double b1 = Abs(b); double max = Max(a1, b1); if (d == 0.0) return 0.0; return Abs(a - b) / max; } if (RelDif(A, B) <= delta)) // USLOV

Pri uporeivanju dve realne vrednosti nikako nemojte koristiti jednakost. Ovo je jako frekventna greka takmiara. U vedini sluajeva
if (Abs(A B) < delta)

de zadovoljiti uslove problema. Ukoliko je potrebno, gore navedene komplikovanije varijante de vam pomodi.

Time Limit Exceeded ne mora da znai Time Limit Exceeded


Pri testiranju programa, bilo od strane sistema za ocenjivanje ili takmiara, nekada se moe doneti zakljuak da je algoritam ili implementacija prespora za ogranienja problema. Meutim, ukoliko se samo pokua optimizacija pristupa, a ne i provera memorijskih zahteva i ogranienja, trud nede uroditi plodom. Naredni primer jako lepo ilustruje ovaj problem. Posmatrajmo naradni C kod:
#include <stdio.h> #define MAXN 100 int array[MAXN], i; void main( void ) { for (i = 0; i <= 100; i++) { array[i] = 0; } }

U gore navedenom kodu program de pokuati da pristupi elementu niza sa indeksom . Kako je to izvan granica niza (u C-u su indeksi elemenata numerisani od ), pristupide se elementu na adresi nakon niza , to predstavlja adresu promenjive (poto je ona definisana nakon niza). Tada postavljamo promenjivu odnosno element na . Na ovaj nain smo dobili jako udnu beskonanu petnju.

Literatura
[1] Shahriar Monzoor, Common Mistakes in Online and RealTime Contests, XRDS Crossroads The ACM Magazine for Students, Summer 2008/Vol. 14, No. 4 [2] Donald E. Knuth, The Art of Computer Programming, Addison Wesley, Volume 2, 1980