46
Абстрактни типове с две противоречащи една на друга операции Кр. Манев, 09.2008

Абстрактни типове с две противоречащи една на друга операции

  • Upload
    skule

  • View
    50

  • Download
    1

Embed Size (px)

DESCRIPTION

Абстрактни типове с две противоречащи една на друга операции. Кр. Манев, 09.2008. Абстрактни типове. Абстрактен тип наричаме: Множество от (математически дефини-рани) обекти ; - PowerPoint PPT Presentation

Citation preview

Page 1: Абстрактни типове с две противоречащи една на друга операции

Абстрактни типове с две противоречащи една на

друга операции

Кр. Манев, 09.2008

Page 2: Абстрактни типове с две противоречащи една на друга операции

Абстрактни типове

Абстрактен тип наричаме: Множество от (математически

дефини-рани) обекти; Множество от операции.

Операциите могат да бъдат както вътрешни – ар-гументите им са само обекти от множеството, така и външни – аргументите им са както обекти от множеството, така и други обекти.

Page 3: Абстрактни типове с две противоречащи една на друга операции

Имплементаци на абстрактните типове

Всеки абстрактен тип може да се имплементира по няколко различни начина: Обектите от множеството представяме в

някаква структура от данни. Ако желаем, можем да оформим структурата от данни като тип (typedef) или обект;

Всяка операция имплементираме с програмна функция/метод – функциите, типа и начина на задаване на аргументите, типа на резултата и т.н. наричаме интерфейс.

Page 4: Абстрактни типове с две противоречащи една на друга операции

Програмиране с абстрактни типове

Когато трябва да решим приложна задача с помощта на имплементирания абстрактен тип, добре е да спазваме простото правило: всеки достъп до обект на типа да става само през интерфейса.

Резултатът ще бъде - винаги можем да подменим имплементацията на интерфейса без да променяме кода, който решава приложната задача.

Page 5: Абстрактни типове с две противоречащи една на друга операции

Програмиране с абстрактни типове

Абстрактен тип

Стр. от данниФункции

Стр. от данниФункции

Стр. от данниФункции

Имплементации

Интерфейс

Приложна програма

Page 6: Абстрактни типове с две противоречащи една на друга операции

Абстрактният типразбиване (на

множество)

Дефиниция: A = {a1, a2,…, aN} е множество с N елемента. R = {S1, S2,…, SK}, Si A наричаме разбиване на A, ако:

Si , i = 1, 2,…,K ;

Si Sj = , 1 i, j K, i j ;

i = 1, 2,…,K

Si = A .

{{0, 3, 6, 9}, {1, 4, 7}, {2, 5, 8}} е разбиване на {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}.

Page 7: Абстрактни типове с две противоречащи една на друга операции

Операции с абстрактния тип

разбиване (на множество)

Нека R = {S1, S2,…, SK} е разбиване на A = {a1, a2,…, aN}. Дефинираме следните операции: find(R, ai) – резултат Sj, ai Sj;

join(R, Si , Si) – резултат ново разбиване: R’ = {S1, …, Si-1,Si+1,…, Sj-1,Sj+1 ,…, SK, S}, S = Si Si;

init(R, N) – резултат R = {{1}, {2},…, {N}}, и т.н .

За нашия пример, find(R, 0)= S1, find(R, 1)= S2,

join(R, S1 , S2) = {{0, 1, 3, 4, 6, 7, 9}, {2, 5, 8}}.

Page 8: Абстрактни типове с две противоречащи една на друга операции

Задача

Да разгледам сега една задача, която е подходяща за решаване с помощта на абстрактния тип разбиване:

Даден е граф G(V,E) с тегла c:ER на ребрата. Да се построи Минимално покриващо дърво T(V,E’) на G, т.е. такова покриващо дърво, че сумата от теглата на ребрата му да е минимална.

Page 9: Абстрактни типове с две противоречащи една на друга операции

Алгоритъм на Крускал

Сортираме ребрата на графа в нарастващ ред на теглата – и нека той е e1, e2,…, eM

От всеки връх v на графа правим отделно дърво Tv({v},)

for(i=1;i<=M;i++) {/*ei=(v,w)*/ if (v и w са в различни дървета) свързваме v и w с ребро; }

Page 10: Абстрактни типове с две противоречащи една на друга операции

Интерфейс за разбиване

Нека сме дефинирали структура от данни, подходяща за представяне на АТ разбиване и сме я оформили като тип - Part. Eлементите на множеството - Elem, частите – Sub.

Дефинираме следните функции: Sub find(Part *R,Elem a) Part join(Part *R,Sub S1,Sub S2)

void init(Part *R, int N)

Page 11: Абстрактни типове с две противоречащи една на друга операции

Имплементация на алгоритъма на Крускал

int N,M,g[MM][3],t[MN][2];Part P; void Kruskal() {int j=1;Sub s1,s2; Sort(g); init(&P,N); for(int i=1;i<=M;i++)

if (s1=find(&P,g[i][0])!= s2=find(&P,g[i][1])) { join(&P,s1,s2); t[j][0]=g[i][0]; t[j++][1]=g[i][1]; }}

Page 12: Абстрактни типове с две противоречащи една на друга операции

Сложност по време на алгоритъма на Крускал

Нека означим с Tinit(n), Tfind(n) и Tjoin(n) сложостите по време на имплементациите на функциите.

Тогава за сложността TKr(N,M) на имплeмeнтацията на алг. на Крускал:

TKr(N,M) = O(M.log M) + Tinit(n) +

+ 2.M. Tfind(n) + (N – 1). Tjoin(n)

Page 13: Абстрактни типове с две противоречащи една на друга операции

Имплементация на прост интерфейс за разбиване

typedef {int N,int P[MN];} Part; Elem= Sub=int. За всеки елемент a, в P[a]помним

елемента с най-малка стойност в неговата част на разбиването – лидер на частта:

Дефинираме следните функции: void init(Part *R, int N) { R->N=N;

for(int i=1;i<=N;i++)R->P[i]=i;}

Page 14: Абстрактни типове с две противоречащи една на друга операции

Имплеметация на прост интерфейс за разбиване

int find(Part *R,int a)

{ return R->P[a];} void join(Part *R,int S1,int S2)

{ int L=min(S1,S2);K=max(S1,S2);

for(int i=1;i<=R->N;i++)

if(R->P[i]==K) R->P[i]==L;

}

Page 15: Абстрактни типове с две противоречащи една на друга операции

Сложност по време на имплементацията

Tinit(N) = O(N),

Tfind(N) = O(1),

Tjoin(N) = O(N).

Тогава за сложността TKr(N,M) на имплeмeнтацията на алг. на Крускал:

TKr(N,M) = O(M.log M) + O(N) + 2.M. O(1) + (N – 1). O(N) = O(M.log M + N2) – неприятно за графи с малко ребра

Page 16: Абстрактни типове с две противоречащи една на друга операции

Друга имплементация на интерфейс за разбиване

typedef {int N,int P[MN];} Part; Elem= Sub=int. За всеки елемент a, в P[a]помним

елмента, корен (лидер) на дърво с всички ел. на частта:

Дефинираме следните функции: void init(Part *R, int N) { R->N=N;

for(int i=1;i<=N;i++)R->P[i]=i;}

Page 17: Абстрактни типове с две противоречащи една на друга операции

Имплеметация на прост интерфейс за разбиване

int find(Part *R,int a)

{ int x=a;

while(R->P[x]!=x) x=R->P[x];

return x;} void join(Part *R,int S1,int S2)

{ R->P[S2]=S1;}

Page 18: Абстрактни типове с две противоречащи една на друга операции

Сложност по време на новата

имплементацията

Tinit(N) = O(N),

Tfind(N) = O(N),

Tjoin(N) = O(1).

Тогава за сложността TKr(N,M) на имплeмeнтацията на алг. на Крускал:

TKr(N,M) = O(M.log M) + O(N) + 2.M. O(N) + (N – 1). O(1) = O(M.log M + M.N) – неприятно за графи с много ребра

Page 19: Абстрактни типове с две противоречащи една на друга операции

Как да подобрим втората имплементация

Балансиране на дървото по височина: за целта добавяме в структурата масив h[] и за всяко дърво помним текущата му височина в h[i]. (h[i]=0 в началото)

void join(Part *R,int S1,int S2) { if(h[S2]<h[S1])R->P[S2]=S1; else R->P[S1]=S2; if (h[S2]==h[S1]) h[S2]++; }

Page 20: Абстрактни типове с две противоречащи една на друга операции

Как да подобрим втората имплементация

Повдигане на дърветата: за целта променяме малко функцията find:

int find(Part *R,int a) { int x=a,y=a; while(R->P[x]!=x) x=R->P[x]; while(R->P[y]!=x) {z=R->P[y]; R->P[y]=x; y=z;} return x;}

Page 21: Абстрактни типове с две противоречащи една на друга операции

Сложност по време на имплементацията

Tinit(N) = O(N), Tfind(N) = O(log* N) O(1), Tjoin(N) = O(1). Тогава за сложността TKr(N,M) на

имплeмeнтацията на алг. на Крускал: TKr(N,M) = O(M.log M) + O(N) + 2.M. O(log* N)

+ (N – 1). O(1) = O(M.log M + M + N) – не зависи същестено от съотнешението на брой върхове и брой ребра.

Page 22: Абстрактни типове с две противоречащи една на друга операции

МОБИФОНИ IOI’2001

В равнината е зададена квадратна мрежа с размери SS, S <= 1024. Всяко квадратче е покрито от клетка на мобилен оператор и в него във всеки момент се намират определен брой мобилни телефони. Броят на телефоните в едно квадратче непрекъснато се мени. В определени моменти всяка от клетките докладва на централата за настъпилите изменения, а от време навреме, в централата си задават въпроси за броя на работещите в момента телефони в някаква правоъгълна подобласт на мрежата.

Page 23: Абстрактни типове с две противоречащи една на друга операции

МОБИФОНИ IOI’2001

Команда Описание

0 Инициализира всички клетки с 0

1 X Y A Добавя А телефона в клетката с координати (X,Y).

2 L B R T Колко са работещите телефони в правоъг. от (L,B) до (R,T)?

3 Край на работата

Page 24: Абстрактни типове с две противоречащи една на друга операции

МОБИФОНИ IOI’2001 едномерен вариант

Команда Описание

0 Инициализира всички клетки с 0

1 X A Добавя А телефона в клетката с координата X

2 L R Пита се колко са работещите теле- фони в интервала (L,R)

3 Край на работата

Page 25: Абстрактни типове с две противоречащи една на друга операции

Абстрактен тип Дефинираме абстрактен тип Net –

редица от S клетки с операции: void init(Net* M,int S) -

инициализира мрежа M с размер S.

void chng(Net* M,int X,int A) - регистрира измененията;

int find(Net* M,int L,int R)- дава отговори.

Page 26: Абстрактни типове с две противоречащи една на друга операции

Решение

scanf(”%d %d”,&code,&S); init(&M,S); while(1){ scanf(”%d”,&code);if(code==3) break;

if(code==1) { scanf(”%d %d”,&X,&A);chng(&M,X,A); }

else { scanf(”%d %d”,&L,&R); ptintf(”%d\n”,find(&M,L,R));}}

Page 27: Абстрактни типове с две противоречащи една на друга операции

Имплементация на АТ

typedef int a[1025] Net M;void init(Net* M,int S){ int i;for(i=1;i<=S;i++) M->a[i] =0; }

void chng(Net* M,int X,int A){ M->a[X] +=A; }int find(Net* M,int L,int R){ int i, res=0; for(i=L;i<=R;i++) res+=M->a[i]; return res; }

Page 28: Абстрактни типове с две противоречащи една на друга операции

Сложност

init – O(S) chng - O(1) find - O(S) За целия алгоритъмO(S) + Q1. O(1) + Q2.O(S), където Qk е

броят на заявките от тип kМного голям брой заявки от тип 2

ще компрометира решението.

Page 29: Абстрактни типове с две противоречащи една на друга операции

Имплементация на АТtypedef int a[1025] Net M;void init(Net* M,int S){ int i; for(i=1;i<=S;i++) M->a[i] =0; }void chng(Net* M,int X,int A){ int i; for(i=x;i<=S;i++) M->a[X] +=A; }int find(Net* M,int L,int R){ return M->a[R]- M->a[L-1]; }

Page 30: Абстрактни типове с две противоречащи една на друга операции

Сложност

init – O(S) chng - O(S) find - O(1) За целия алгоритъмO(S) + Q1. O(S) + Q2. O(1), където Qk е

броят на заявките от тип kМного голям брой заявки от тип 1

ще компрометира решението.

Page 31: Абстрактни типове с две противоречащи една на друга операции

Как да излезем от ситуацията

Изходът от ситуацията в такива задачи (абстр. тип с две “противоречиви” операции) е да се опитаме да направим двете операции сравнително бързи

Обичайното решение е да се замени линейната имплементация (списък) с дървовидна, така че сложността и на двете операции да е сравнима с височината на дървото.

Page 32: Абстрактни типове с две противоречащи една на друга операции

Индексно дърво За всяко i = 1,2,…,S да пресметнем границите на

интервала [i-2k(i)+1,i], където k(i) е броят на нулите отдясно в двоичното представяне на i. В d[i] ще държим сумата на числата в интервала [i-2k(i)+1,i]

1 [1-20+1,1]=[1,1] 5 [5-20+1,5]=[5,5]

2 [2-21+1,2]=[1,2] 6 [6-21+1,6]=[5,6]

3 [3-20+1,3]=[3,3] 7 [7-20+1,7]=[7,7]

4 [4-22+1,4]=[1,4] 8 [8-23+1,8]=[1,8]

Page 33: Абстрактни типове с две противоречащи една на друга операции

Индексно дърво

2 7 3 4 0 5 6 1

2 3 0 6

16

9 5

28

Page 34: Абстрактни типове с две противоречащи една на друга операции

Операциите

typedef int a[1025] Net M;void init(Net* M,int S){ int i; for(i=1;i<=S;i++) M->a[i] =0; }void chng(Net* M,int X,int A){ while(X<=S)M->a[X]+=A;X+=k(X);}int find(Net* M,int L,int R){ int res=0; while(R>=1){res+=M->a[R];res-=k(R);} while(L>=1){res-=M->a[L];res-=k(L);} return res; }

Page 35: Абстрактни типове с две противоречащи една на друга операции

Намиране на k(i) Стойностите на k(i) могат да бъдат

табулирани предварително и след това да бъдат вземани от един масив. Това ще добави O(S. log S) стъпки.

По-бързо ще стане ако в началото на всяка от операциите се постави 1 в променлива e и докато e&i != 0 се измества e на една позиция наляво (е<<=1). При това за следващата стойност на i не се налага ново зареждане на e, а може да се продължи с текукщата стойност.

Page 36: Абстрактни типове с две противоречащи една на друга операции

Сложност

init – O(S); chng - O(log S) find - O(log S) За целия алгоритъмO(S) + Q1. O(log S) + Q2.O(log S) = O(S + Q.log S),

където Q е броят на заявките от тип kРеиението вече не зависи от броя заявки

от различен тип. За двумерния случай, трябва да се приложи техниката в двете посоки. Направете го за домашно непременно.

Page 37: Абстрактни типове с две противоречащи една на друга операции

ХАМАЛИ – НЕТ, Шумен’08

Бали боклук трябва да бъдат извозени до сметището през канализация - N шахти, 1 е сметището. Всяка шахта U е свързана с тръба към точно една шахта V<U и течението ще отнесе до V всеки предмет, пуснат в U. Бала, пусната в U може да стигне до всяка шахта, която се намира на пътя от U до 1, стига в шахтите по пътя да има достатъчно вода, за да не заседне балата в тях.

В 10.00:00 сутринта, всички шахти са празни, започва да вали дъжд и шахтите започват да се пълнят с 10 куб.см вода в секунда. Хамалите хвърлят бала в шахта X и я чакат в шахта Y, разположена по пътя от X до 1. Максималният обем, който може да бъде пренесен така, е равен на минималнoто количество вода в куб.см., което се съдържа в шахта по пътя от X до Y.

Page 38: Абстрактни типове с две противоречащи една на друга операции

ХАМАЛИ – НЕТ, Шумен’08

Какъв обем може да бъде пренесен от X доY в зададен момент, ако конкурентите от „Цепи-Лепи”

АД, не изпомпваха в здадени моменти зададено кол. вода от някои от шахтите. Вход: Първи ред: N и K; Втори ред: за всеки връх – бащата Следват K заявки:1 X Y T – колко обем може да мине от X до Y в момент

T2 T X V – конкурентите изпомпват от връх X, в

момента T, V куб. см вода Изход. За всяка заявка от тип 1 – тъсеният обем

Page 39: Абстрактни типове с две противоречащи една на друга операции

АНАЛИЗ

Нужни са ни две операции с дървото void chng(vertex X, int V) int find(int T,vertex X, vertex Y)

Решение с O(1) за chng() и O(h) за find() e очевидно. При изродени дрвета h=O(N) и сложността на алгоритъма е O(Q.N), кдето Q e общия бройна операциите. Търси се по-добро. Проблемът е, че зададеният тип вече е дърво!!!

Page 40: Абстрактни типове с две противоречащи една на друга операции

АНАЛИЗ

Да означим с t(X) бащата на X, p(X,Y) пътя от X до Y W(X) – количеството вода изпомпена от X

до момент t. Тогава наличната вода в X в момент t ще

бъде C(X) =10.t – W(X) Jump(X)=X’, като X’(X,1); F(X) = max W(Y), Y(X,Jump(X)) равносилно

на търсения min C(Y), Y(X,Jump(X))

Page 41: Абстрактни типове с две противоречащи една на друга операции

Иплементация

int t[MAXN],W[MAXN],F[MAXN];void chng(int X,int V){W[X]+=V; за (всеки A,Xp(A,Jump(A)) F(A)= max{F(A),W[X]; }

int find(int T,int X,int Y){int i=X,M=0; while(i!=Y){if(Jump(i)p(i,B)) {M=max(M,F(i);i=Jump(i);} else {M=max(M,W(i);i=t(i);} return 10*T-M; }

Page 42: Абстрактни типове с две противоречащи една на друга операции

Сложност

Нека S e такова, че дължините на p(X,Jump(X)) са колкото може по-близки до S без да го надхвърлят – идеалът е точно S

Сложността на chng() е O(S), защото се обхожда един път от X до Jump(X)

Сложността на find() е O(N/S+S), защото се обхожда един път в дървото: първо на стъпки от по около S върха и после не повече от S стъпки по 1 връх

Page 43: Абстрактни типове с две противоречащи една на друга операции

Сложност

Сложността на алгоритъма ще бъде Q1.O(S)+Q2.O(N/S+S), където Q1 и Q2 са

брй на операциите отпърви и втори вид Ако S=sqrt(N), тогава алгоритъмът е

много приличен O(Q.sqrt(N)) Ако се преброят предварително

операциите, може да се опита и по-добра стойност за S.

Page 44: Абстрактни типове с две противоречащи една на друга операции

Смятане на Jump(X)

Depth[1]=1; Weight[…] = 1;Обхождаме дървото в пост-ордер и за всеки токущо обходен връх А

{ За(всеки наследник В на А) { Weight(A)=Weight(A)+ Weight(B);

Depth(B)=1+Depth(A); }}

Page 45: Абстрактни типове с две противоречащи една на друга операции

Смятане на Jump(X)

Jump(1)= 1;

С обхождане на дървото в дълбочина, начален връх 1 и а всеки връх А ≠ 1

{Jump(A)= Jump((А));while(Weight(Jump(A))–Weight(A)>S)

{Jump(A)= наследника на Jump(A) в (А,Jump(A);}

}

Page 46: Абстрактни типове с две противоречащи една на друга операции

Weight Depth Jump U

1

2

3

4

5

6

7

8