Vlerësimet asimptotike në internet. Vlerësimi asimptotik i algoritmit

Analiza e krahasimit të kostove kohore të algoritmeve të kryera për të zgjidhur një shembull të një problemi të caktuar, me sasi të mëdha të dhënash hyrëse, quhet asimptotike. Algoritmi me kompleksitetin më të ulët asimptotik është më efikasi.

Në analizën asimptotike, kompleksiteti i algoritmitështë një funksion që ju lejon të përcaktoni se sa shpejt rritet koha e funksionimit të algoritmit me rritjen e vëllimit të të dhënave.

Vlerësimet kryesore të rritjes që gjenden në analizën asimptotike janë:

  • Ο (O-i madh) – vlerësimi i sipërm asimptotik për rritjen e funksionit kohor;
  • Ω (Omega) – vlerësim më i ulët asimptotik për rritjen e funksionit kohor;
  • Θ (Theta) – vlerësime asimptotike të poshtme dhe të sipërme për rritjen e funksionit kohor.

Le n- sasia e të dhënave. Pastaj rritja e funksionit të algoritmit f(n) mund të kufizoni funksionet g(n) në mënyrë asimptotike:

Për shembull, koha për të pastruar një dhomë varet në mënyrë lineare nga sipërfaqja e së njëjtës dhomë (Θ( S)), d.m.th., me rritjen e sipërfaqes në n herë, koha e pastrimit do të rritet gjithashtu me n një herë. Kërkimi i një emri në librin e telefonit do të marrë kohë lineare Ο (n), nëse përdorni një algoritëm linear kërkimi, ose kohë që varet logaritmikisht nga numri i regjistrimeve ( Ο (regjistri 2 ( n))), në rast të përdorimit të kërkimit binar.

Me interesin më të madh për ne është Ο -funksioni. Për më tepër, në kapitujt vijues, kompleksiteti i algoritmeve do të jepet vetëm për kufirin e sipërm asimptotik.

Nën shprehjen “kompleksiteti i algoritmit është Ο (f(n))" nënkuptohet se me një rritje të vëllimit të të dhënave hyrëse n, koha e funksionimit të algoritmit nuk do të rritet më shpejt se disa konstante shumëzuar me f(n).

Rregulla të rëndësishme të analizës asimptotike:

  1. O(k*f) = O(f) – faktor konstant k(konstante) hidhet poshtë sepse ndërsa vëllimi i të dhënave rritet, kuptimi i tij humbet, për shembull:

O(9,1n) = O(n)

  1. O(f*g) = O(f)*O(g) - vlerësimi i kompleksitetit të produktit të dy funksioneve është i barabartë me produktin e kompleksitetit të tyre, për shembull:

O(5n*n) = O(5n)*O(n) = O(n)*O(n) = O(n*n) = O(n 2)

  1. O(f/g)=O(f)/O(g) – vlerësimi i kompleksitetit të herësit të dy funksioneve është i barabartë me herësin e kompleksitetit të tyre, për shembull:

O(5n/n) = O(5n)/O(n) = O(n)/O(n) = O(n/n) = O(1)

  1. O(f+g) është e barabartë me dominantin O(f) Dhe O(g) - një vlerësim i kompleksitetit të shumës së funksioneve përcaktohet si një vlerësim i kompleksitetit të dominantit të termave të parë dhe të dytë, për shembull:

O(n 5 +n 10) = O(n 10)

Numërimi i numrit të operacioneve është i lodhshëm dhe, më e rëndësishmja, nuk është aspak e nevojshme. Bazuar në rregullat e listuara më sipër, për të përcaktuar kompleksitetin e një algoritmi, nuk është e nevojshme, siç bëmë më parë, të numërohen të gjitha operacionet; mjafton të dimë se çfarë kompleksiteti është një dizajn i caktuar algoritmi (operator ose grup operatorësh) ka. Kështu, një algoritëm që nuk përmban sythe dhe rekursione ka kompleksitet të vazhdueshëm O(1). Kompleksiteti i ekzekutimit të ciklit n përsëritjet është e barabartë me O(n). Ndërtimi i dy sytheve të mbivendosur në varësi të ndryshores së njëjtë n, ka kompleksitet kuadratik O(n 2).


Qasje të ndryshme mund të përdoren për të vlerësuar performancën e algoritmeve. Më e thjeshta është thjesht të ekzekutoni çdo algoritëm në disa detyra dhe të krahasoni kohën e ekzekutimit. Një mënyrë tjetër është të vlerësohet matematikisht koha e ekzekutimit duke numëruar operacionet.

Le të shqyrtojmë një algoritëm për llogaritjen e vlerës së një polinomi të shkallës n në një pikë të caktuar x.
P n (x) = a n x n + a n-1 x n-1 + ... + a i x i + ... + a 1 x 1 + a 0

Algoritmi 1- për çdo term, përveç një konstrukti 0 x në një fuqi të caktuar me shumëzim sekuencial dhe më pas shumëzohet me një koeficient. Pastaj shtoni kushtet.

Llogaritja i termi i th( i=1..n) kërkon i shumëzimet Pra, total 1 + 2 + 3 + ... + n = n(n+1)/2 shumëzimet Përveç kësaj, kërkohet n+1 shtesë. Total n(n+1)/2 + n + 1= n 2 /2 + 3n/2 + 1 operacionet.

Algoritmi 2- do ta nxjerrim x-s jashte kllapave dhe rishkruaje polinomin ne forme
P n (x) = a 0 + x(a 1 + x(a 2 + ... (a i + .. x(a n-1 + a n x))).

Për shembull,
P 3 (x) = a 3 x 3 + a 2 x 2 + a 1 x 1 + a 0 = a 0 + x(a 1 + x(a 2 + a 3 x))

Ne do ta vlerësojmë shprehjen nga brenda. Kllapa më e brendshme kërkon 1 shumëzim dhe 1 mbledhje. Vlera e saj përdoret për kllapa pasardhëse... Dhe kështu, 1 shumëzim dhe 1 mbledhje për çdo kllapa, që... n-1 gjë. Dhe pasi të keni llogaritur kllapin më të jashtëm, shumëzoni me x dhe shtoni a 0. Total n shumëzimet + n shtesa = 2n operacionet.

Shpesh një vlerësim i tillë i detajuar nuk kërkohet. Në vend të kësaj, ata japin vetëm shkallën asimptotike të rritjes së numrit të operacioneve ndërsa n rritet.

Funksioni f(n) = n 2 /2 + 3n/2 + 1 rritet afërsisht sa n 2/2(ne hedhim poshtë termin me rritje relativisht të ngadaltë 3n/2+1). Shumëzues konstant 1/2 Ne gjithashtu heqim dhe marrim një vlerësim asimptotik për Algoritmin 1, i cili shënohet me një simbol të veçantë O (n 2)[lexohet si "O i madh nga en katror"].

Ky është një vlerësim i sipërm, d.m.th. numri i operacioneve (dhe për rrjedhojë koha e funksionimit) nuk rritet më shpejt se katrori i numrit të elementeve. Për të kuptuar se si është kjo, shikoni tabelën, e cila tregon numrat që ilustrojnë shkallën e rritjes për disa funksione të ndryshme.

n*log n

1 0 0 1
16 4 64 256
256 8 2,048 65,536
4,096 12 49,152 16,777,216
65,536 16 1,048,565 4,294,967,296
1,048,576 20 20,969,520 1,099,301,922,576
16,775,616 24 402,614,784 281,421,292,179,456

Nëse supozojmë se numrat në tabelë korrespondojnë me mikrosekonda, atëherë për një problem me n=1048576 elementet e algoritmit me kohë ekzekutimi O (log n) do të duhen 20 mikrosekonda, algoritmi përfundimisht do O(n)- 17 minuta, dhe algoritmi me kohën e funksionimit O (n 2)- më shumë se 12 ditë... Tani avantazhi i algoritmit 2 me vlerësim O(n) përpara se Algoritmi 1 është mjaft i dukshëm.

Vlerësimi më i mirë është O(1)... Në këtë rast, koha nuk varet fare nga n, pra konstante për çdo numër elementësh.

Kështu, O()- një vlerësim "i shkurtuar" i kohës së funksionimit të algoritmit, i cili shpesh është shumë më i lehtë për t'u marrë sesa formula e saktë për numrin e operacioneve.

Pra, le të formulojmë dy rregulla për formimin e vlerësimit O().

Gjatë vlerësimit të një funksioni, numri i operacioneve që rritet më shpejt merret si funksion.
Kjo do të thotë, nëse në një program ekzekutohet një funksion, për shembull, shumëzimi O(n) herë, dhe shtesa - O (n 2) herë, atëherë kompleksiteti total i programit është O (n 2), pasi në fund të fundit me rritjen n më shpejt (në njëfarë konstante shumë herë) shtesat do të kryhen aq shpesh sa ato do të ndikojnë në performancën shumë më tepër sesa shumëzimet e ngadalta por të rralla. Simboli O() tregon ekskluzivisht asimptotike!

Gjatë vlerësimit të O(), konstantet nuk merren parasysh.
Lëreni një algoritëm të kryejë 2500n + 1000 operacione, dhe një tjetër - 2n+1. Ata të dy kanë një vlerësim O(n), pasi koha e ekzekutimit të tyre rritet në mënyrë lineare.

Në veçanti, nëse të dy algoritmet, p.sh. O(n*log n), kjo nuk do të thotë se ato janë po aq efektive. E para mund të jetë, të themi, 1000 herë më efektive. O() thjesht do të thotë se koha e tyre rritet afërsisht si funksion n*log n.

Një pasojë tjetër e heqjes së konstantës është algoritmi me kalimin e kohës O (n 2) mund të funksionojë shumë më shpejt se algoritmi O(n) për n të vogël... Për faktin se numri aktual i veprimeve të algoritmit të parë mund të jetë n2 + 10n + 6, dhe e dyta - 1000000n + 5. Megjithatë, algoritmi i dytë herët a vonë do të kalojë të parin... n 2 duke u rritur shumë më shpejt 1000000n.

Baza e logaritmit brenda simbolit O() jo i shkruar.
Arsyeja për këtë është mjaft e thjeshtë. Le të kemi O (log2n). Por log 2 n=log 3 n/log 3 2, A regjistri 3 2, si çdo konstante, asimptotika është një simbol RRETH () nuk merr parasysh. Kështu, O (log2n) = O (log 3 n).

Ne mund të kalojmë në çdo bazë në të njëjtën mënyrë, që do të thotë se nuk ka kuptim ta shkruajmë atë.

Interpretimi matematik i simbolit O().

Përkufizimi
O(g)- shumë funksione f, për të cilat ekzistojnë konstante të tilla C Dhe N, Çfarë |f(x)|<= C|g(x)| per te gjithe x>N.
Regjistro f = O(g) fjalë për fjalë do të thotë se f i përket grupit O(g). Në këtë rast, shprehja e kundërt O(g) = f nuk ka kuptim.

Në veçanti, mund të themi se f(n) = 50n i takon O (n 2). Këtu kemi të bëjmë me një vlerësim të pasaktë. Sigurisht f(n)<= 50n 2 n>1, megjithatë një deklaratë më e fortë do të ishte f(n) = O(n), pasi për C=50 Dhe N=1 drejtë f(n)<= Cn, n>N.

Lloje të tjera vlerësimesh.

Së bashku me vlerësimin O(n) përdoret vlerësimi Ω(n)[lexohet si "Omega e madhe nga en"]. Ai tregon kufirin e poshtëm për rritjen e funksionit. Për shembull, le të përshkruhet numri i veprimeve të algoritmit nga funksioni f(n) = Ω(n 2). Kjo do të thotë që edhe në rastin më të suksesshëm, do të prodhohet të paktën një rend i madhësisë n 2 veprimet.
...Ndërsa rezultati f(n) = O(n 3) garanton se në rastin më të keq do të ketë rregull n 3, jo më.

Përdoret gjithashtu vlerësimi Θ(n)["Theta nga en"], e cila është një hibrid O() Dhe Ω() .
Θ(n 2)është një vlerësim asimptotik i sipërm dhe i poshtëm në të njëjtën kohë - ai gjithmonë do të mbahet sipas rendit të n 2 operacionet. Gradë Θ() ekziston vetëm kur O() Dhe Ω() përputhen dhe barazohen me to.

Për algoritmet e llogaritjes polinomiale të diskutuara më sipër, vlerësimet e gjetura janë njëkohësisht O(), Ω() Dhe Θ() .
Nëse i shtojmë algoritmit të parë për kontrollin për x=0 në fuqizim, pastaj në të dhënat fillestare më të suksesshme (kur x=0) kemi rend n kontrolle, 0 shumëzime dhe 1 mbledhje, duke dhënë një vlerësim të ri Ω(n) së bashku me të vjetrën O (n 2).

Si rregull, vëmendja kryesore ende i kushtohet vlerësimit të sipërm O(), kështu që pavarësisht "përmirësimit", Algoritmi 2 mbetet i preferueshëm.

Kështu që, O()- vlerësimi asimptotik i algoritmit mbi të dhënat më të këqija hyrëse, Ω() - në të dhënat më të mira hyrëse, Θ() - shënimi i shkurtuar i të njëjtit O() Dhe Ω() .

Përkundër faktit se funksioni i kompleksitetit kohor të një algoritmi mund të përcaktohet saktësisht në disa raste, në shumicën e rasteve është e kotë të kërkosh vlerën e tij të saktë. Fakti është se, së pari, vlera e saktë e kompleksitetit kohor varet nga përkufizimi i operacioneve elementare (për shembull, kompleksiteti mund të matet në numrin e operacioneve aritmetike ose operacioneve në një makinë Turing), dhe së dyti, si madhësia e të dhënat hyrëse rriten, kontributi i faktorëve konstant dhe termat e urdhrave më të ulët që shfaqen në shprehjen për kohën e saktë të funksionimit bëhen jashtëzakonisht të parëndësishëm.

Kompleksiteti asimptotik- shqyrtimi i të dhënave të mëdha hyrëse dhe vlerësimi i rendit të rritjes së kohës së funksionimit të algoritmit. Në mënyrë tipike, një algoritëm me kompleksitet më të ulët asimptotik është më efikas për të gjitha të dhënat hyrëse, me përjashtim ndoshta të madhësisë së vogël të të dhënave.

Vlerësimi i kompleksitetit asimptotik shënohet me shkronjën greke Θ (theta).

f(n) = Θ(g(n)), nëse ekzistojnë c1, c2>0 dhe n0 të tilla që c1*g(n)<=f(n)<=c2*g(n) , при n>n0.

Funksioni g(n) është një vlerësim asimptotikisht i saktë i kompleksitetit të algoritmit - funksioni f(n), pabarazia e mësipërme quhet barazi asimptotike, dhe vetë shënimi Θ simbolizon grupin e funksioneve që rriten "sa më shpejt". si funksioni g(n) - d.m.th. deri në shumëzimin me një konstante. Siç rrjedh nga pabarazia e mësipërme, vlerësimi Θ është një vlerësim i sipërm dhe i poshtëm i kompleksitetit. Nuk është gjithmonë e mundur të merret një vlerësim në këtë formë, kështu që vlerësimet e sipërme dhe të poshtme ndonjëherë përcaktohen veçmas.
Rezultati i sipërm i vështirësisë shënohet me shkronjën greke Ο (omicron), dhe është një grup funksionesh që rriten jo më shpejt se g(n).
f(n)= Ο(g(n)), nëse ka c>0 dhe n0 të tilla që 0<=f(n)<=cg(n), при n>n0.
Rezultati më i ulët i vështirësisë shënohet me shkronjën greke Ω (omega), dhe është bashkësia e funksioneve që rriten jo më ngadalë se g(n).
f(n)= Ω(g(n)), nëse ka c>0 dhe n0 të tilla që 0<=cg(n)<=f(n), при n>n0.
Si pasojë: një vlerësim asimptotik ekziston vetëm nëse kufiri i poshtëm dhe i sipërm për kompleksitetin e algoritmit përkojnë. Në praktikën e analizës së algoritmeve, vlerësimi i kompleksitetit më së shpeshti kuptohet si vlerësimi i sipërm i kompleksitetit. Kjo është mjaft logjike, pasi gjëja më e rëndësishme është të vlerësohet koha në të cilën algoritmi është i garantuar të përfundojë punën e tij, dhe jo koha brenda së cilës ai përfundimisht nuk do të përfundojë.

(CONSOLE $APPTYPE)

përdor
SysUtils;
var n:Numër i plotë;
rezultati i funksionit(n:integer):Integer; //formimi i botës
var i:Numër i plotë;
fillojnë
rezultat:=0;
për i:= 2 deri në n div 2 bëj
nëse n mod i =0 atëherë
rezultat:=rezultati+1;
fundi;


fillojnë
lexo(n); // Emri juaj
shkruaj(rezultati(n));
lexuarln;
lexuarln;
fund.
fund.

4. Rekursion me memorizim (një version transparent i programimit dinamik). Një shembull i llogaritjes së shpejtë të koeficientëve binomialë duke përdorur formulën C(n,m)=C(n-1,m)+C(n-1,m-1)

Ekziston një mënyrë për të zgjidhur problemin e llogaritjeve të përsëritura. Është e qartë - duhet të mbani mend vlerat e gjetura në mënyrë që të mos i llogaritni përsëri çdo herë. Sigurisht, kjo do të kërkojë përdorim aktiv të kujtesës. Për shembull, një algoritëm rekurziv për llogaritjen e numrave Fibonacci mund të plotësohet lehtësisht me tre "vija":

krijoni një grup global FD të përbërë nga zero;

pasi të keni llogaritur numrin F(n), vendosni vlerën e tij në FD[n];

në fillim të procedurës rekursive, kontrolloni që FD[n] = 0 dhe, nëse po, ktheni FD[n] si rezultat, dhe përndryshe vazhdoni me llogaritjen rekursive të F(n).

(Funksioni në Pascal)

funksioni C(m, n:Byte):Longint;

Nëse (m=0) ose (m=n)

Përndryshe C:=C(m, n-1)+C(m-1, n-1)

(Procedura në Pascal)

Procedura C(m, n: Byte; Var R: Longint);

Var R1, R2: Longint;

Nëse (m=0) ose (m=n)

Analiza e Algoritmit -

Llojet e analizave

Rasti më i keq: T(n)

Rasti mesatar: T(n)

Vlerësimi asimptotik

O

f (n) = O(g(n)) Þ

($c > 0, n 0 >

O(g(n)) = (f (n) : $ c > 0, n 0 >

Shembull: 2n 2 = O(n 3)


Merge sort

nëse (fq< r) //1


Pema e rekursionit: T(n) = 2*T(n/2) +cn, me –const, c>0

Metodologjia për vlerësimin e algoritmeve rekurzive.

Metoda e përsëritjes

Bazuar në formulën T(n), shkruajmë formulën për elementin më të vogël që ndodhet në anën e djathtë të formulës për T(n).

Zëvendësoni anën e djathtë të formulës që rezulton në formulën origjinale

Ne kryejmë dy hapat e parë derisa të zgjerojmë formulën në një seri pa funksionin T(n)

Le të vlerësojmë serinë që rezulton bazuar në një progresion aritmetik ose gjeometrik

T(n)=T(n-1)+n, T(1)=1

T(n)=θ(g(n)), g(n)=?

T(n-1)=T(n-2)+(n-1)

T(n-2)=T(n-3)+(n-2)

T(n-3)+(n-2)+(n-1)+n=…

1+…(n-2)+(n-1)+n=

Pema e rekursionit - një metodë grafike e shfaqjes së zëvendësimit të një lidhjeje në vetvete

T(n)=2T(n/2)+n 2

T(n/4) T(n/4) T(n/4) T(n/4)

(n/2) 2 (n/2) 2 log n (1/2)*n 2

(n/4) 2 (n/4) 2 (n/4) 2 (n/4) 2 (1/4)*n 2

Metoda e zëvendësimit

  1. Mendoni (sugjeroni) një zgjidhje
  2. Kontrolloni zgjidhjen duke përdorur induksion
  3. Gjeni dhe zëvendësoni konstante

T(n) = 2T(n/2) + n


T(n) = (n log n)

Premisa e induksionit: T(n) ≤ с * n* log n, c>0

Le të jetë i vërtetë ky vlerësim për n/2

T(n/2) ≤ c*(n/2)*log(n/2)

Le ta zëvendësojmë atë në formulën origjinale për T(n)

T(n) ≤ 2*(c*(n/2)*log(n/2))+n ≤

c*n*log(n/2)+n =

c*n*log n - c*n*log 2 +n =

c*n*log n - c*n +n ≤

c*n*log n

c≥1, n ≥ 2

Teorema kryesore mbi vlerësimet e përsëritura

T (n) = aT (n/b) + f (n), a ≥ 1, b > 1, f (n) - (f (n) > 0, n > n0)


Algoritme për renditjen e vargjeve në kohë polinomiale

Renditja është procesi i riorganizimit të objekteve për një të dhënë

agregatët në një rend të caktuar (në rritje

ose në rënie).

Qëllimi i renditjes është zakonisht për të lehtësuar më pas

kërkimi i elementeve në një grup të renditur.

Renditja e thjeshtë e futjes

void sort_by_insertion (tasti a , int N)

për (i=1; i< N; i++)

për (j=i-1; (j>=0)&& (x< a[j]); j--)

a = a[j];

Analiza e renditjes së thjeshtë të futjes

Numri i krahasimeve:

C (N) = 1 + 2 + 3 + ... + N - 1 = (N * (N -1))/2= O (N 2)

Koha totale: T(N) = θ(N 2)

Renditja sipas shkëmbimit të thjeshtë. Metoda e flluskës.

void bubble_sort (tasti a, int N)

për (i=0; i

për (j=N-l; j>i; j--)

nëse (a > a[j]) (

x = a[j]; a[j] = a[j-1]; a[ j -1] = x;

Renditja e analizave me shkëmbim të thjeshtë

Rasti më i keq: grup i rendit të kundërt

Numri i krahasimeve:

C(N) = (N - 1) + (N - 2) + ... + 1 = (N * (N-1))/2 = O(N 2)

Koha totale: T(N) = θ(N 2)


Shtim

Nyja* _Shto (Nyja *r, T s)

r = Nyja(t) e re;

tjetër nëse (s< r->inf)

r->majtas = _Shto(r->majtas, s);

r->right = _Add(r->djathtas, s);


Heqja e një elementi nga pema

Pema T me rrënjë n dhe çelës K.

hiqni nga pema T një nyje me çelës K (nëse ka një të tillë).

Algoritmi:

Nëse pema T është bosh, ndalo;

Përndryshe, krahasoni K me çelësin X të nyjës rrënjësore n.

Nëse K>X, hiqni K në mënyrë rekursive nga nënpema e djathtë e T;

Nëse K

Nëse K=X, atëherë duhet të merren parasysh tre raste.

Nëse të dy fëmijët nuk ekzistojnë, atëherë fshijmë nyjen aktuale dhe rivendosim lidhjen e nyjës prind me të;

Nëse një nga fëmijët mungon, atëherë vendosim vlerat e fushave të fëmijës m në vend të vlerave përkatëse të nyjës rrënjë, duke mbishkruar vlerat e saj të vjetra dhe çlirojmë memorien e zënë nga nyja m;

Nëse janë të pranishëm të dy fëmijët, atëherë gjejmë nyjen m që ndodhet pranë asaj të dhënë;

kopjoni të dhënat (përveç lidhjeve me elementët fëmijë) nga m në n;

fshij në mënyrë rekursive nyjen m.

Elementi në vijim është dhënë

Jepen: pema T dhe çelësi x

Ktheni një tregues në elementin pranë x ose NULL nëse x është elementi i fundit në pemë.

Algoritmi:

Më vete shqyrton dy raste.

Nëse nënpema e djathtë e një kulmi x nuk është bosh, atëherë elementi pranë x është elementi minimal në këtë nënpemë.

Përndryshe, nëse nënpema e djathtë e kulmit x është bosh. Ne lëvizim lart nga x derisa të gjejmë një kulm që është fëmija i majtë i prindit të tij. Ky prind (nëse ka një) do të jetë elementi që kërkohet.


Futja e nyjeve

Futja e një çelësi të ri në një pemë AVL bëhet në të njëjtën mënyrë siç bëhet në pemët e kërkimit të thjeshtë: ne zbresim poshtë pemës, duke zgjedhur drejtimin e lëvizjes djathtas ose majtas në varësi të rezultatit të krahasimit të çelësit në nyjen aktuale dhe çelësin e futur.

I vetmi ndryshim është se kur ktheheni nga rekursioni (d.m.th., pasi çelësi të jetë futur në nënpemën e djathtë ose të majtë), nyja aktuale ribalancohet. Çekuilibri që lind me një futje të tillë në çdo nyje përgjatë rrugës së lëvizjes nuk i kalon dy, që do të thotë se aplikimi i funksionit të balancimit të përshkruar më sipër është i saktë.

Heqja e nyjeve

Për të hequr një kulm nga një pemë AVL, si bazë merret algoritmi, i cili zakonisht përdoret kur hiqen nyjet nga një pemë standarde e kërkimit binar. Ne gjejmë nyjen p me çelësin e dhënë k, në nënpemën e djathtë gjejmë nyjen min me çelësin më të vogël dhe zëvendësojmë nyjen p të fshirë me nyjen e gjetur min.

Gjatë zbatimit, lindin disa opsione. Para së gjithash, nëse nyja e gjetur p nuk ka një nënpemë të djathtë, atëherë, sipas vetive të pemës AVL, në të majtë kjo nyje mund të ketë vetëm një nyje të vetme fëmijë (pema me lartësi 1), ose nyja p është edhe një gjethe. Në të dyja këto raste, thjesht fshini nyjen p dhe si rezultat ktheni një tregues në nyjen e majtë fëmijë të p.

Le të ketë tani p një nënpemë të drejtë. Ne gjejmë çelësin minimal në këtë nënpemë. Sipas vetive të një peme kërkimi binar, ky çelës ndodhet në fund të degës së majtë, duke filluar nga rrënja e pemës. Ne përdorim funksionin rekurziv findmin.

funksioni removemin - heqja e elementit minimal nga një pemë e caktuar. Sipas vetive të një peme AVL, elementi minimal në të djathtë ose ka një nyje të vetme ose është bosh. Në të dyja rastet, ju vetëm duhet të ktheni treguesin në nyjen e duhur dhe të kryeni balancimin në rrugën e kthimit (kur ktheheni nga rekursioni).


Tabelat hash, metoda e lidhjes me zinxhir

Adresimi i drejtpërdrejtë përdoret për grupe të vogla çelësash. Kërkohet përcaktimi i një grupi dinamik, secili element i të cilit ka një çelës nga grupi U = (0,1,..., m - 1), ku m nuk është shumë i madh, nuk ka dy elementë të kenë çelësa të njëjtë.

Për të përfaqësuar një grup dinamik, përdoret një grup (tabela e adresuar drejtpërdrejt), T, çdo pozicion ose qelizë e të cilit korrespondon me një çelës nga hapësira kryesore U.

Qeliza k tregon një element të grupit me çelësin k. Nëse grupi nuk përmban një element me çelës k, atëherë T[k] = NULL.

Operacioni i kërkimit të çelësave kërkon kohë O(1)

Disavantazhet e adresimit të drejtpërdrejtë:

Nëse hapësira kryesore U është e madhe, ruhet një tabelë T me madhësi |U| jopraktike, nëse jo e pamundur, në varësi të sasisë së memories së disponueshme dhe madhësisë së hapësirës së tasteve

Kompleti K i çelësave të ruajtur në të vërtetë mund të jetë i vogël në krahasim me hapësirën e çelësit U, në të cilin rast memoria e alokuar në tabelën T humbet kryesisht.

Një funksion hash është një funksion h që përcakton vendndodhjen e elementeve të grupit U në tabelën T.



Në hashing, një element me çelës k ruhet në qelizën h(k), funksioni hash h përdoret për të llogaritur qelizën për një çelës të caktuar k. Funksioni h harton hapësirën kryesore U në qelizat e tabelës hash T [O..m - 1]:

h: U → (0,1,..., m -1).

vlera h(k) quhet vlera hash e çelësit k.

Kur grupi K i çelësave të ruajtur në fjalor është shumë më i vogël se hapësira e çelësave të mundshëm U, një tabelë hash kërkon shumë më pak hapësirë ​​se një tabelë adresimi direkt.

Qëllimi i një funksioni hash është të zvogëlojë gamën e punës të indekseve të grupeve, dhe në vend të |U| vlerat, ju mund t'ia dilni me vetëm vlerat m.

Kërkesat e memories mund të reduktohen në θ(|K|), ndërsa koha e kërkimit për një element në tabelën hash mbetet e barabartë me O(1) - ky është një kufi në kohën mesatare të kërkimit, ndërsa në rastin e një tabela e adresimit ky kufi është i vlefshëm për skenarin më të keq.

Një përplasje është një situatë ku dy çelësa hartohen në të njëjtën qelizë.

Për shembull, h(43) = h(89) = h(112) = k

Metoda e zinxhirit:

Ideja: Ruani elementet e një grupi me të njëjtën vlerë hash si një listë.

h(51) = h(49) = h(63) = i

Analiza

Rasti më i keq: nëse funksioni hash për të gjithë elementët e grupit prodhon të njëjtën vlerë. Koha e hyrjes është Θ(n), me |U | = n.

Rasti mesatar: për rastin kur vlerat hash janë të shpërndara në mënyrë uniforme.

Çdo çelës mund të ketë po aq gjasa të përfundojë në çdo qelizë të tabelës, pavarësisht se ku bien çelësat e tjerë.

Le të jepet një tabelë T dhe le të ruhen n çelësa në të.

Pastaj, a = n/m është numri mesatar i çelësave në qelizat e tabelës.

Koha për të kërkuar një element që mungon është Θ(1 + α).

Koha konstante për të llogaritur vlerën e funksionit hash plus kohën për të përshkuar listën deri në fund, sepse gjatësia mesatare e listës është α, atëherë rezultati është Θ(1) + Θ(α) = Θ(1 + α)

Nëse numri i qelizave të tabelës është proporcional me numrin e elementeve të ruajtura në të, atëherë n = O(m) dhe, rrjedhimisht, α = n/m = O(m)/m = O(1), që do të thotë të kërkoni për një element në tabelën hash in mesatarisht kërkon kohë Θ(1).

Operacionet

Futja e një elementi në një tabelë

Largimi

kërkojnë gjithashtu kohë O(1).

Zgjedhja e një funksioni hash

Çelësat duhet të shpërndahen në mënyrë të barabartë në të gjitha qelizat

Modeli i shpërndarjes së çelësave të funksionit hash nuk duhet të lidhet me modelet e të dhënave. (Për shembull, të dhënat janë numra çift).

Metodat:

Metoda e ndarjes

Metoda e shumëzimit

Metoda e ndarjes

h (k) = k mod m

Problema e pjesëtuesit të vogël m

Shembulli nr. 1. m = 2 dhe të gjithë çelësat janë çift Þ qelizat tek nuk janë

e mbushur.

Shembulli nr. 2. m = 2 rÞ hash nuk varet nga bitet e mësipërme r.

Metoda e shumëzimit

Le m= 2 r, çelësat janë fjalë w-bit.

h(k) = (A k mod 2 w) >> (w - r), Ku

Një mod 2 = 1 ∩ 2 w-1< A< 2 w

Ju nuk duhet të zgjidhni A afër me 2w-1 Dhe 2 w

Kjo metodë është më e shpejtë se metoda e ndarjes

Metoda e shumëzimit: shembull

m = 8 = 2 3, w = 7

Adresimi i hapur: kërko

Kërkimi është gjithashtu kërkim i njëpasnjëshëm

Suksese kur gjendet kuptimi

Dështim kur gjeni një qelizë bosh ose kaloni nëpër të gjithë tabelën.

Strategjitë e Kërkimit

lineare -

h(k, i) = (h′(k) + i) mod m

Kjo strategji është e lehtë për t'u zbatuar, por është subjekt i problemeve

grumbullimi primar i lidhur me krijimin e një sekuence të gjatë

aktiviteti i qelizave të okupuara, gjë që rrit kohën mesatare të kërkimit.

kuadratike

h(k, i) = (h′(k) + c 1 i+ c 2 i 2) mod m

ku h′(k) është një funksion hash i rregullt

Hashimi i dyfishtë -

h(k,i) = (h 1 (k) + i h 2 (k)) mod m.

Hashimi i dyfishtë

Kjo metodë jep rezultate të shkëlqyera, por h 2 (k) duhet të jetë koprim me m.

Kjo mund të arrihet:

duke përdorur m si fuqitë e dy dhe duke e bërë h 2 (k) prodhojnë vetëm numra tek

m = 2 r иh 2 (k)- e çuditshme.

m- numri i thjeshtë, vlerat h 2 – numra të plotë pozitivë më pak se m

Për të thjeshtë m mund të instalohet

h1(k)=k mod m

h2(k)=1+(k mod m’)

m' më pak se m (m' =m-1 ose m-2)

Adresimi i hapur: Shembull i futjes

Le të jepet tabela A:

Hashimi i dyfishtë

h2(k)=1+(k mod 11)

Ku do të futet elementi?

Analiza e hapur e adresimit

Supozim shtesë për hashimin e njëtrajtshëm: çdo çelës ka të njëjtën gjasa të marrë ndonjë nga m! permutacionet e sekuencave të eksplorimit të tabelës

pavarësisht nga çelësat e tjerë.

Gjetja e një elementi që mungon

Numri i provave për kërkim të suksesshëm

Adresimi i hapur

A< 1 - const Þ O(1)

Si sillet ai? A:

Tabela 50% e përfunduar kërkimin Þ2

Tabela është 90% e plotësuar Þ 10 studime

Tabela është 100% e plotë Þ m studime


Parimi i zgjedhjes së pangopur

Ata thonë se është i zbatueshëm për problemin e optimizimit parimi i zgjedhjes së pangopur, nëse një sekuencë zgjedhjesh optimale në nivel lokal jep një zgjidhje optimale globalisht.

Në mënyrë tipike, një provë e optimalitetit ndjek këtë model:

Është vërtetuar se zgjedhja e pangopur në hapin e parë nuk e mbyll rrugën drejt zgjidhjes optimale: për çdo zgjidhje ka një tjetër, në përputhje me zgjedhjen e pangopur dhe jo më keq se e para.

Është treguar se nënproblemi që lind pas zgjedhjes së pangopur në hapin e parë është i ngjashëm me atë origjinal.

Argumenti plotësohet me induksion.

Optimale për nëndetyrat

Problem thuhet se ka prona optimaliteti për nënproblemet, nëse zgjidhja optimale e një problemi përmban zgjidhje optimale për të gjitha nënproblemet e tij.


Ndërtimi i kodit Huffman

Çdo mesazh përbëhet nga një sekuencë karakteresh nga ndonjë alfabet. Shpesh, për të kursyer kujtesën dhe për të rritur shpejtësinë e transferimit të informacionit, lind detyra e ngjeshjes së informacionit. Në këtë rast, përdoren metoda speciale të kodimit të karaktereve. Këto përfshijnë kodet Huffman, të cilat ofrojnë kompresim nga 20% në 90% në varësi të llojit të informacionit.

Algoritmi Huffman gjen kodet optimale të karaktereve bazuar në frekuencën e karaktereve të përdorura në tekstin e ngjeshur.

Algoritmi i Huffman-it është një shembull i një algoritmi të babëzitur.

Le të supozojmë se në një skedar me gjatësi 100,000 karaktere, frekuencat e karaktereve janë të njohura:

Ne duhet të ndërtojmë një kod binar në të cilin çdo karakter përfaqësohet si një sekuencë e fundme bitësh e quajtur një fjalë kodi. Kur përdorni një kod uniform, në të cilin të gjitha fjalët e kodit kanë të njëjtën gjatësi, shpenzohen të paktën tre bit për karakter dhe i gjithë skedari do të kërkojë 300,000 bit.

Një kod i pabarabartë do të jetë më ekonomik nëse karakteret që shfaqen shpesh janë të koduar me sekuenca të shkurtra bitesh dhe karaktere të rrallë me sekuenca të gjata. Kur kodoni, i gjithë skedari do të kushtojë (45*1 + 13*3 + 12*3 + 16*3 + 9*4 + 5*4)*1000 = 224000. Kjo do të thotë, kodi i pabarabartë jep rreth 25% kursime.

Kodet e parashtesave

Merrni parasysh kodet në të cilat, për secilën nga dy sekuencat e biteve që përfaqësojnë karaktere të ndryshme, asnjëra nuk është parashtesë e tjetrës. Kode të tilla quhen kode prefikse.

Gjatë kodimit, çdo karakter zëvendësohet me kodin e tij. Për shembull, vargu abc duket si 0101100. Për një kod prefiks, dekodimi është unik dhe ndodh nga e majta në të djathtë.

Karakteri i parë i një teksti të koduar me një kod prefiks përcaktohet në mënyrë unike, pasi fjala e koduar e tij nuk mund të jetë fillimi i ndonjë karakteri tjetër. Pasi kemi identifikuar këtë simbol dhe kemi hequr kodin e tij, ne e përsërisim procesin për pjesët e mbetura, e kështu me radhë.

Për të zbatuar në mënyrë efektive dekodimin, duhet të ruani informacionin rreth kodit në një formë të përshtatshme. Një mundësi është të përfaqësohet kodi si pema binare e kodit, gjethet e të cilit korrespondojnë me karakteret që do të kodohen. Në këtë rast, rruga nga rrënja në simbolin e koduar përcakton sekuencën e kodimit të biteve: lëvizja përgjatë pemës në të majtë jep 0, dhe lëvizja në të djathtë jep 1.

Nyjet e brendshme tregojnë shumën e frekuencave për gjethet e nënpemës përkatëse.

Kodi optimal për një skedar të caktuar korrespondon gjithmonë me një pemë binare në të cilën çdo kulm që nuk është një fletë ka dy fëmijë. Kodi uniform nuk është optimal, pasi pema përkatëse ka një kulm me një djalë.

Një pemë e kodit prefiks optimal për një skedar që përdor të gjitha karakteret nga një grup C dhe vetëm ato përmbajnë saktësisht | C | lë një për çdo simbol dhe saktësisht | C | - 1 nyje që nuk janë gjethe.

Duke ditur pemën T që korrespondon me kodin e prefiksit, është e lehtë të gjesh numrin e biteve të kërkuara për të koduar skedarin. Për çdo karakter c nga alfabeti C, le të tregojmë f[c] numrin e paraqitjeve të tij në skedar dhe dT(c) thellësinë e fletës përkatëse dhe, rrjedhimisht, gjatësinë e sekuencës së biteve që kodojnë c. Pastaj për të koduar skedarin do t'ju duhet:

Kjo vlerë quhet kosto e pemës T. Është e nevojshme të minimizohet kjo vlerë.

Huffman propozoi një algoritëm të babëzitur që ndërton një kod prefiks optimal. Algoritmi ndërton një pemë T që korrespondon me kodin optimal nga poshtë lart, duke filluar nga grupi i | C | gjethet dhe bërja | C | - 1 bashkime.

Për çdo simbol është specifikuar frekuenca e tij f [c]. Për të gjetur dy objekte për t'u bashkuar, përdoret një radhë prioritare Q, duke përdorur frekuencat f si prioritete - bashkohen dy objektet me frekuencat më të ulëta.

Si rezultat i bashkimit, fitohet një objekt i ri (kulmi i brendshëm), frekuenca e të cilit konsiderohet e barabartë me shumën e frekuencave të dy objekteve të bashkuar. Ky kulm shtohet në radhë.

Huffman ( C)

1. n ←│C│ │ C │- fuqia C

2. Q ← C Q – radhë prioritare

3. për i ← 1 te n-1

4. bëj z ← Krijo_Nyjen() z – nyja e përbërë nga fushat f, majtas, djathtas

5. x ← majtas [z] ← Dequeue(P)

6. y ← djathtas [z] ← Dequeue(P)

7. f[z] ← f[x] + f[y]

8. Në radhë(Q,z)

9. kthimi Dequeue(P) ktheni rrënjën e pemës

Gradë

Radha zbatohet si një grumbull binar.

Mund të krijoni një radhë në O(n).

Algoritmi përbëhet nga një lak që ekzekutohet n-1 herë.

Çdo operacion i radhës përfundon në O(log n).

Koha totale e funksionimit është O (n log n).

Problemi i ndërtimit të rrjetit

Zonat e shfaqjes: komunikimi dhe rrjetet rrugore.

E dhënë: shumë nyje rrjeti (host, qytete).

E nevojshme: ndërtimi i një rrjeti me peshën totale më të vogël të skajit (gjatësia e kabllove të rrjetit, gjatësia e rrugëve).

Modeli i grafikut: Nyjet e rrjetit janë nyje grafike, E = V 2, ne i dimë peshat e të gjitha skajeve.

Rezultati: pemë e lirë.

Qasja për të kërkuar për MOD

Ne ndërtojmë pemën A duke shtuar një skaj në një kohë, dhe para çdo përsëritjeje, pema aktuale është një nëngrup i disa MOD.

Në çdo hap të algoritmit, ne përcaktojmë një skaj (u, v) që mund t'i shtohet A pa shkelur këtë veti. Ne e quajmë një skaj të tillë të sigurt

GenericMST(G, w)

2 ndërsa A nuk është MOD

3 gjeni një skaj (u, v) që është i sigurt për A

4 A ← A U ((u, v))

____________________________________________________________________________

Klasifikimi i brinjëve

1. Brinjë pemësh(skajet e pemës) janë skajet e grafikut G. Një skaj (u, v) është një skaj i pemës nëse kulmi v është i hapur kur shqyrtohet kjo skaj.

2. Skajet e pasme(skajet e pasme) janë skajet (u,v) që lidhin kulmin u me paraardhësin e tij v në pemën e kërkimit të parë në thellësi. Skajet e ciklit që mund të ndodhin në grafikë të drejtuar konsiderohen skajet e pasme.

3. Brinjë të drejta(skajet e përparme) janë skajet (u,v) që nuk janë skajet e pemës dhe lidhin kulmin u me pasardhësin e tij v në pemën e kërkimit të parë në thellësi.

4. Brinjë të kryqëzuara(skajet e kryqëzuara) - të gjitha skajet e tjera të grafikut. Ata mund të lidhin kulme të së njëjtës pemë kërkimi në thellësi të parë kur asnjë kulm nuk është paraardhës i një tjetri, ose mund të lidhin kulme në pemë të ndryshme.

Algoritmi DFS mund të modifikohet në mënyrë që të klasifikojë skajet e hasura gjatë funksionimit. Ideja kryesore është që çdo skaj (u, v) mund të klasifikohet duke përdorur ngjyrën e kulmit v kur shqyrtohet për herë të parë (megjithëse skajet e drejta dhe të kryqëzuara nuk dallohen).

  1. Ngjyra e bardhë tregon se është një brinjë druri.
  2. Ngjyra gri përcakton skajin e pasmë.
  3. E zeza tregon një skaj të drejtë ose të kryqëzuar.

Rasti i parë rrjedh drejtpërdrejt nga përkufizimi i algoritmit.

Duke marrë parasysh rastin e dytë, vini re se kulmet gri formojnë gjithmonë një zinxhir linear të pasardhësve që korrespondojnë me grupin e thirrjeve aktive në procedurën DFS_Visit; numri i kulmeve gri është një më i madh se thellësia e kulmit të fundit të hapur në pemën e kërkimit të parë të thellësisë. Eksplorimi fillon gjithmonë nga kulmi më i thellë gri, në mënyrë që një skaj që arrin një kulm tjetër gri të arrijë paraardhësin e kulmit origjinal.

Në rastin e tretë, kemi të bëjmë me skajet e mbetura që nuk bien nën rastin e parë apo të dytë. Mund të tregohet se një skaj (u, v) është i drejtë nëse d [u]< d [v], и перекрестным, если d [u] >d[v]

___________________________________________________________________________

Renditja topologjike

kolona e përparësisëçdo skaj (u, v) do të thotë se u paraprin v

Renditja topologjike grafiku është ndërtimi i një sekuence a, ku për të gjitha a i dhe a j vlen: $(a i ,a j) Þ i< j.

Renditja topologjike e një grafi aciklik të orientuar G = (V, E) është një renditje lineare e të gjitha kulmeve të tij, e tillë që nëse grafiku G përmban një skaj (u,v), atëherë u në këtë renditje ndodhet para v (nëse grafiku është jo aciklike, një klasifikim i tillë nuk është i mundur). Renditja topologjike e një grafi mund të konsiderohet si renditja e kulmeve të tij përgjatë një vije horizontale në mënyrë që të gjitha skajet të drejtohen nga e majta në të djathtë.

Sekuenca e renditur: C2, C6, C7, C1, C3, C4, C5, C8

për secilin(u në V) ngjyrë[u] = e bardhë; // inicializoj

L = lista e re e lidhur; // L është një listë e lidhur bosh

për secilin (u në V)

nëse (ngjyra[u] == e bardhë) TopVisit(u);

kthimi L; // L jep urdhrin përfundimtar

TopVisit(u) ( // filloni një kërkim në u

ngjyra[u] = gri; // shëno që ke vizituar

për secilin (v në Adj(u))

nëse (ngjyra[v] == e bardhë) TopVisit(v);

Shtoni u në pjesën e përparme të L; // në përfundim, shtoni në listë

T (n) = Θ(V + E)



Procedurat

Krijo - Vendos (u)- të krijojë një grup nga një kulm u.

Gjej - Set (u)- gjeni bashkësinë së cilës i përket kulmi ukthehet në atë grup ndodhet elementi i specifikuar. Në fakt, kjo kthen një nga elementët e grupit (i quajtur përfaqësuese ose udhëheqës). Ky përfaqësues zgjidhet në çdo grup nga vetë struktura e të dhënave (dhe mund të ndryshojë me kalimin e kohës, përkatësisht, pas thirrjeve Bashkimi).

Nëse thirrja Find - Set për disa dy elemente ktheu të njëjtën vlerë, atëherë kjo do të thotë se këta elementë janë në të njëjtin grup, përndryshe, janë në grupe të ndryshme.

Bashkimi (u,v)- kombinoni bashkësitë që përmbajnë kulme u Dhe v

Ne do të ruajmë grupe elementesh në formë pemët: një pemë korrespondon me një grup. Rrënja e pemës është përfaqësuesi (udhëheqësi) i grupit.

Kur zbatohet, kjo do të thotë që ne krijojmë një grup prind, në të cilin për çdo element ruajmë një lidhje me paraardhësin e tij në pemë. Për rrënjët e pemëve, ne do të supozojmë se paraardhësi i tyre është vetë ata (d.m.th., lidhja lidhet në këtë vend).

Për të krijuar një element të ri (operacion Krijo - Vendos), ne thjesht krijojmë një pemë të rrënjosur në kulmin v, duke vënë në dukje se paraardhësi i saj është vetvetja.

Për të kombinuar dy grupe (operacioni Bashkimi (a,b)), fillimisht do të gjejmë drejtuesit e bashkësisë në të cilën ndodhet a dhe e bashkësisë në të cilën ndodhet b. Nëse drejtuesit përkojnë, atëherë ne nuk bëjmë asgjë - kjo do të thotë që grupet ishin tashmë të bashkuar. Përndryshe, thjesht mund të specifikoni që paraardhësi i kulmit b është i barabartë me f (ose anasjelltas) - duke bashkuar kështu një pemë me tjetrën.

Më në fund, zbatimi i operacionit të kërkimit të liderit ( Gjeni - Set(v)) është e thjeshtë: të parët i ngjitemi nga kulmi v derisa të arrijmë në rrënjë, d.m.th. ndërsa referimi për paraardhësin nuk të çon në vetvete. Është më i përshtatshëm për të zbatuar këtë operacion në mënyrë rekursive.

Heuristike e kompresimit të rrugës

Ky heuristik është krijuar për të përshpejtuar gjërat Gjeni - Set() .

Ajo qëndron në faktin se kur pas thirrjes Gjeni - Set(v) ne do të gjejmë liderin që kërkojmë fq vendosur, pastaj mbani mend se në kulm v dhe të gjitha majat kaluan gjatë rrugës - është ky lider fq. Mënyra më e lehtë për ta bërë këtë është ridrejtimi i tyre prind në këtë kulm fq .

Kështu, grupi i paraardhësve prind kuptimi ndryshon disi: tani është grup i ngjeshur i paraardhësve, d.m.th. për çdo kulm, nuk mund të ruhet paraardhësi i menjëhershëm, por paraardhësi i paraardhësit, paraardhësi i paraardhësve të paraardhësve etj.

Nga ana tjetër, është e qartë se është e pamundur të bëhen këto tregues prind gjithnje me gisht nga lideri: ndryshe, kur kryen nje operacion Bashkimi do të duhej të përditësonte liderët O(n) elementet.

Kështu, në grup prind duhet të trajtohen saktësisht si një grup paraardhësish, ndoshta pjesërisht të ngjeshur.

Zbatimi i heuristikës së ngjeshjes së rrugës lejon arritjen e asimptotikave logaritmike: mesatarisht për kërkesë

Analiza

Inicializimi – O(V)

Cikli funksionon V herë dhe çdo operacion extractMin ekzekutohet në - O(logV) herë, për një total prej O(V logV) herë

Cikli for shkon O(E) herë, reduceKey kërkon kohë O(logV).

Koha totale e funksionimit - O(V log V +E logV)= O(E logV)



Vetia e rrugës më të shkurtër

Le p = (v 1 , v 2 ..... v k)- rruga më e shkurtër nga kulmi v 1 në kulm vk në një grafik të caktuar të ponderuar të drejtuar G = (U. E) me funksionin e peshës w: E → R a p ij = (v i, v i+1 .....v j)- rruga e pjesshme e shtegut p që shkon nga kulmi v i ne krye v j për arbitrare i dhe j (1 ≤ i< j ≤ k). Pastaj p ij– rruga më e shkurtër nga kulmi v i për të v i

Dijkstra(G, w, s) (

për secilin (u në V) ( // inicializimi

d [u] = + pafundësi

ngjyra [u] = e bardhë

d[s] =0 // distanca nga burimi është 0

Q = i ri PriQueue(V) // vendos të gjitha kulmet në Q

ndërsa (Q.nonEmpty()) ( // derisa të përpunohen të gjitha kulmet

u = Q.extractMin() // zgjidhni u më afër s

për secilin (v në Adj[u]) (

nëse (d[u] + w(u,v)< d[v]) { // Relax(u,v)

d [v] = d [u] + w(u,v)

Q.decreaseKey(v, d[v])

ngjyra [u] = e zezë


Vlerësimi i vështirësisë

Algoritmi Bellman-Ford përfundon brenda një periudhe kohore O(V*E), meqenëse inicializimi në rreshtin 1 kërkon kohë O(V), për secilën nga |V| - 1 kalim përgjatë skajeve në rreshtat 2-4 kërkon kohë O(E), dhe ekzekutimi i ciklit for në rreshtat 5-7 kërkon kohë O(E). .

Vlerësimi asimptotik i algoritmit

Analiza e Algoritmit - studim teorik i performancës së programeve kompjuterike dhe burimeve që ato konsumojnë.

Performanca - koha e funksionimit të algoritmit, në varësi të sasisë së të dhënave hyrëse.

Përcaktohet nga funksioni T(n), ku n është vëllimi i të dhënave hyrëse

Llojet e analizave

Rasti më i keq: T(n)– koha maksimale për çdo të dhënë hyrëse me madhësi n.

Rasti mesatar: T(n)– koha e pritur për çdo të dhënë hyrëse me madhësi n.

Rasti më i mirë është koha minimale e funksionimit.

Vlerësimi asimptotik

O- shënimi: kufiri i sipërm asimptotik

f (n) = O(g(n)) Þ

($c > 0, n 0 > 0 Þ 0 ≤ f (n) ≤ c g(n), n ≥ n 0)

O(g(n)) = (f (n) : $c > 0, n 0 > 0 Þ 0 ≤ f (n) ≤ c g(n), n ≥ n 0 )

Shembull: 2n 2 = O(n 3)


Algoritme rekursive, ndërtimi i një vlerësimi asimptotik. Shembull

Merge sort

sort(А,p,r) //p - fillimi i grupit, r - fundi i grupit T(n)

nëse (fq< r) //1

q=(p + r)/2; //Llogaritni mesin e grupit 1

rendit (A,p,q); //rendit anën e majtë të T(n/2)

rendit (A,q+1,r); //rendit anën e djathtë të T(n/2)

merge(p,q,r); //bashkoni dy vargje në një n

Artikuj të ngjashëm