From: | "Mihai Patrascu" <mihai_p@pcnet.ro> | Block Address | Add to Address Book |
To: | "probleme" <probleme@ldc.ro> |
Subject: | re la diverse |
Date: | Mon, 19 Mar 2001 19:01:32 +0200 |
|
|
Hello, folks! Uite si un mesaj ceva mai [foarte] [enorm de] mare in
care
explic ce am facut eu la problemele propuse de mine si imi dau cu
parerea
despre subiectele de pe lista. Stiu ca e cam overdue, dar am avut
judeteana... Da, bineinteles c-am luat max...
==========================
=== Belman-Ford pe reale
Ideea e ca trebuie scris
if(a[j] > a[i] + d[i][j] + EPS)
in loc de
if(a[j] > a[i] + d[i][j])
Deoarece nu s-a ingramadit nimeni sa explice de ce, iata si explicatia.
Pe
masinile IEEE, float e reprezentat pe 32 de biti, iar double pe 64.
Totusi,
x86 are registrii coprocesorului pe 80 de biti (asa-numitul long
double).
Problema vine de la precizia in plus. De obicei compilatoarele au si
optiuni
pentru a elimina precizia extra, dar asta incetineste groaznic codul.
Pentru
a arata care e problema (didactic), sa presupunem ca am lucra in baza
10 in
loc de 2 si am avea o mantisa de maxim 4 cifre in registri si 3 cifre
in
memorie (gen long double vs. double). Sa zicem ca avem valorile:
a[j] = 1.000e0
a[i] = 0.999e0
d[i][j] = 0.900e-3 (adica 0.0009)
Coprocesorul calculeaza:
a[i] + d[i][j] = 0.9999e0 (mantisa de 4 cifre)
a[j] > a[i] + d[i][j] = 1 (adica adevarat)
Deci se marcheaza schimbarea si in a[j] se scrie (a[i] + d[i][j]),
adica
0.9999e0. Problema e ca a[j] traieste in memorie, unde mantisa e maxim
3
cifre, deci 0.9999e0 va fi aproximat evident la 1.000e0. Deci in
realitate
nimic nu se schimba in memorie, dar programul crede ca a imbunatatit
ceva.
La pasul urmator va sesiza aceeasi pseudo-imbunatatire, samd.
Despre 1.0/0.0, teoretic pe IEEE-uri ar trebui sa fie +INF. Totusi, nu
e
recomandabila la concurs, deoarece pe Borland C e +NAN, iar unele
versiuni
de GCC se simt obligate sa iti de un warning (n-am vazut inca la fata o
asemenea versiune, dar tot imi respingeau sursele la USACO din cauza
asta,
desi nici una din variantele mele de gcc nu faceau asta).
==========================
=== Broscuta care sare ca virgula calul
Da, eu ma gandeam la flux de cost minim. Mie mi-a luat sub 10 minute sa
ma
prind de idee si vreo 20 ca sa implementez (what can I say, am muncit
pentru
aurul ala la inter). Rationamentul a fost cam asa:
1. ce ne cere??? un ciclu elementar intre doua noduri de lungime
minima?
2. pacat ca zice lungime minima, daca nu era un flux clasic...
3. hopa...
Acum legat de implementare. Exista anumiti programatori care ar
implementa
asta asa:
1. construieste graf din tabla
2. dubleaza nodurile (pentru capacitati in noduri)
3. fa flux de cost minim
Acesti programatori isi zic algoritmicieni, dar eu prefer denumirea de
tarani. Flux se face foarte bine pe o tabla, fara sa construiesti
graful; am
facut asta in cateva randuri si timpul de implementare e destul de mic,
oricum nu mai mare decat la un flux pe un graf; doar ca trebuie sa
gandesti
ce scrii, nu sa torni un algoritm invatat. La fel daca ai un graf cu
capacitati in noduri, se poate face la fel de usor fara sa dublezi
nodurile
(chiar am facut asta la ONI anu trecut fara nici o dificultate).
Implementati cu grija, algoritmii merg mult mai repede asa. La problema
asta, cred ca e algoritmul are O(N**3), dar probabil mult mai aproape
de
N**2 (atentie, eu socotesc cu tabla de N**2 casute). Ideea e ca nu iti
trebuie flux maxim, ci flux de cel mult 2. Asta iti simplifica mult si
algoritmul (si scade timpul de implementare).
Cu tot riscul de a-l dezamagi pe Cristian, Mihai are dreptate si
algoritmul
tau nu merge. Nu prea cred ca merge altceva inafar de flux (sau un
fluxulet
deghizat, ceva).
==========================
=== Move to front in O(lgN)
Ideea cu arbori echilibrati e buna teoretic, dar nu prea straluceste in
practica. Ideea e ca ai constante (foarte) mari, plus ca daca nu ma
insel eu
prea tare o operatie iti vine O(lgN), unde N este numarul de caracter
posibile (eg 256). Eu am cerut O(lgK), unde K e pozitia pe care vei
gasi
caracterul. Asta e mult mai bine in practica, fiindca K-ul e mic de
obicei
(asta e toate ideea la move-to-front, right?).
Problema se face cu skip-lists. Am citit articolul pe Internet si e
chiar
grozav. Multumesc pentru sugestie lui Cristian! Daca va intrebati,
problema
NU apare in articolul ala (ce naiba, doar nu copiez probleme pur si
simplu...). Oricum cine nu a citit articolul, chiar ii recomand sa-l
citeasca. Am facut si teste practice si skip-lists merg excelent.
Adresa
era: ftp://ftp.cs.umd.edu/pub/skipLists/skiplists.pdf
BTW, daca va uitati pe pagina autorului, gasiti o lucrare in care
explica ca
formatul binarelor Java e groaznic si el a inventat o metoda care
scoate
compresii de vreo 1:20. Plus o studiu bine argumentat numit "The Java
Memory
Model is Hopelessly Flawed". Asta pentru fanii Java.
==========================
=== Chaos from order
Well, well... Dupa cum e formulata problema, presupun ca vrei sa afli
daca
membrii listei stiu sa faca un merge sau un qsort calumea
(median-of-three,
optimizarile alea pentru stiva O(lgN) etc.). Eu personal raman la
preferinta
mea pentru heapsort la concurs, dar pe 1M merge probabil groaznic,
deoarece
nenoroceste cache-ul procesorului (pe de alta parte quick si merge sunt
foarte localizate). Despre regula cu max 40 de linii, poate tu nu ai
vazut
flux maxim scris in <700 de caractere (daca TOP e abonat cumva la
lista:
ti-am batut recordul de la lot din '98).
Programul meu merge in 7 sec (generarea fisierului de intrare in 4sec),
ceea
ce e foarte bine avand in vedere ca e sub Borland C si n-ai memorie.
Ideea e
sa faci qsort pe bucati mari si sa interclasezi dup-aia bucatile cu
heap-uri. Programul are peste 10k, dar e si portabil (merge atat sub
DOS,
cat si pe sisteme POSIX).
Totusi, ideea lui Mihai e super (adica ce ne-a interzis sa facem). Daca
sortezi intai dupa mantise (cu radix) si apoi dupa exponenti (tot cu
radix),
chiar merge pe IEEE-uri (e un fel de generalizare la radix). Daca ai si
numere negative si pozitive, trebuie sa le sortezi separat.
Promit ce implementez quick, merge, heap si radix pe 1M de double-uri
maine
sau poimaine si va trimit rezultatele. Presupui ca nu vrei bucket sau
alte
minuni d'astea probabiliste (daca pui limita de 5*solutia comisiei,
presupun
ca nu vrei chestii probabilistice).
Inca o nota: daca foloseste cineva qsort-ul din C si se incadreaza in
de 5
ori solutia comisiei inseamna ca comisia a dat ca solutie bubble-sort.
Ideea
e ca qsort() face un apel la fiecare comparatie, iar un apel costa
enorm.
BTW, sub Linux qsort() e implementat cu merge-sort :-)
Si-nca o nota la propunerea de rezolvare cu template-uri C++. La
concurs nu
e recomandabil sa folosesti asta (adica nu prea merg template-urile pe
compilatoarele date; la USACO zice clar "do not use..."). Plus, credeam
ce e
o lista pentru algoritmicieni, nu persoane care programeaza tragand
compenente cu mouse-ul in fereastra :-)... Daca vrei, punem pariu ca
iti
implementez ceva care sa mearga de 5 ori mai repede ca template-urile.
mihai_p
Privacy Policy-
Terms of Service
-
Guidelines
Copyright © 1994-2001
Yahoo! Inc. All rights reserved.