Metoda Agregatelor

ArrayList - inserare

Raspuns: ĉi = 3

Explicatie: Incepem cu un array cu capacitate 1 si 0 elemente (vom nota cost insert = ci, copiere n elemente = cpyn.

  • insert1: c = ci = 1
  • insert2: c = cpy1 + ci = 2
  • insert3: c = cpy2 + ci = 3
  • insert4: c = ci = 1
  • insert5: c = cpy4 + ci = 5
  • insert6: c = ci = 1
  • insert7: c = ci = 1
  • insert8: c = ci = 1
  • insert9: c = cpy8 + ci = 9

Observam ca numarul de copieri este cpy1 + cpy2 + cpy4 + cpy8 + … + cpyn = 2 * n (suma geometrica). (1)

De asemenea, au avut loc n insert-uri, care au c = n. (2)

Din (1) si (2) ⇒ ĉi = T(n) / n = (2 * n + n) / n = 3.

ArrayList - stergere

Raspuns = ĉi = 2.

Presupunem cel mai rau caz: capacitate = 4 * n, nr. elemente = n + 1 (delete va cauza o copiere, deoarece la primul delete ajungem la 1/4 din capacitate)

  • delete1: cpyn/2 + cdelete = n/2 + 1 → capacitatea devine 2 * n
  • delete2: cdelete = 1
  • delete3: cdelete = 1
  • delete(n/2): cpyn/4 + cdelete = n/4 + 1 → capacitatea devine n

Observam ca numarul de copieri este cpyn/2 + cpyn/4 + cpyn/8 + … = n. (1)

Numarul de stergeri efective este n. (2)

Din (1) si (2) ⇒ ĉi = T(n) / n = (n + n) / n = 2.

ArrayList - cazul generic

Fie capacitatea initiala = 1, ci = costul inserarii, cd = costul stergerii.

Stim ca:

  • ci = 1 pentru cazul fara copiere
  • ci + m cazul pentru copiere si inserare
  • cd = 1 pentru cazul fara copiere
  • cd + m pentru cazul stergerii si copierii
  • nfinal = k - p numarul final de elemente (elemente inserare - elemente sterse)
  • nroperatii = k + p

⇒ ĉi = Ctotal / (k + p) = (Cins + Cdel) / (k + p)

Am stabilit precedent ca:

  • costul tuturor copierilor la inserare este de 2k
  • costul tuturor inserarilor efective este k
  • ⇒ Cins = 3k

De asemenea:

  • costul tuturor copierilor la stergere este de p
  • costul tuturor stergerilor efective este tot p
  • ⇒ Cdel = 2p

⇒ ĉi = (3k + 2p) / (k + p)

Interpretare:

  • pentru p = 0, avem ĉi = 3 (adevarat, asta este costul doar inserarilor)
  • pentru k = 0 (presupunem ca avem deja un array), ĉi = 2 (adevarat, costul stergerii)
  • pentru k = p, avem ĉi = 2.5
  • ⇒ costul amortizat este mai apropiat de 3 pentru mai multe inserari, dar mai apropiat de 2 pentru mai multe stergeri.

Metoda bancherului

(pentru insertion: 3 bani → 1 inserare, 1 banut atribuit inseratiei curente, 1 banut atribuit altei inseratii din bucata copiata precedent)

Idee de rezolvare: impartim array-ul in 2 bucati:

  • bucata care se va copia = elementele care raman dupa stergeri
  • bucata elementelor care se sterg.

Pentru fiecare operatie de stergere (cost 1), “plasam” un banut catre prima bucata a array-ului (cea care se va copia). In acest fel, la momentul copierii, fiecare element are deja banutul pregatit care ii acopera costul. Astfel, nu mai trebuit platit un cost suplimentar pentru copiere.

Metoda potentialelor

ĉi = ci + Φ(Di) - Φ(Di-1).

Definim n = elemente curente, m = capacitate:

Fie

  Φ(n,m) = 2n - m,  daca n >= m/2
         = m/2 - n, altfel

1. Cazul stergerii in cazul in care capacitatea nu se schimba:

  • ci = 1
  • ΔΦ = Φ(n - 1, m) - Φ(n, m):
    • caz 1: n > m/2 ⇒ ΔΦ = (2(n - 1) - m) - (2n - m) = -2
    • caz 2: n = m/2 ⇒ ΔΦ = (m/2 - (n - 1)) - (2n - m) = 1 + 3m/2 - 3n, dar n = 3m/2 ⇒ ΔΦ = 1 + 3m/2 - 3m/2 = 1
    • caz 3: n < m/2 ⇒ ΔΦ = (m/2 - (n - 1)) - (m/2 - n) = 1

Aflam ca pentru un load factor de peste 1/2 (suntem aproape de capacitatea maxima) avem cost amortizat -1, iar 2 daca ne apropiem de injumatatire.

In operatiile de analiza amortizata, noi luam drept cost amortizat costul operatiilor mai “scumpe”, neluand in calcul operatiile “ieftine”, deci ĉi = 2 in acest caz, nu -1.

2. Cazul stergerii in care capacitatea se schimba:

  • Perechea (n, m) este de fapt (n + 1, 4 * n) → (n, 2 * n)
  • ΔΦ = Φ(n, 2 * n) - Φ(n + 1, 4 * n) = (2 * n - 2 * n) - (4 * n / 2 - n - 1) = 1 - n
  • ci = 1 + n (deletion + copiere)
  • ĉi = 1 + n + (1 - n) = 2

1.

typedef struct list {
    int e;
    struct list *next;
} *List;

List Void() {
    return NULL;
}

List Cons(int e, List list) {
    List newElem = (List)malloc(sizeof(struct list));
    newElem->e = e;
    newElem->next = list;

    return newElem;
}

2.

Size(Void) = 0
Size(Cons(e, L)) = 1 + Size(L)
Add(Void, x) = Cons(x, Void)
Add(Cons(y, L), x) = Cons(y, Add(L, x)
Append(Void, L) = L
Append(Cons(x, L1), L2) = Cons(x, Append(L1, L2))
Reverse(Void) = Void
Reverse(Cons(x, L)) = Append(Reverse(L), Cons(x, Void))

Helper:

isNull(Void) = True
isNull(Cons(x, L)) = False

3.

bool isNull(List l) {
    return l == NULL;
}

int size(List l) {
    if (isNull(l))
        return 0;
    return 1 + size(l->next);
}

List add(List l, int x) {
    if isNull(l))
        return Cons(x, Void());
    return Cons(l->e, add(l->next, x));
}

List append(List l1, List l2) {
    if (isNull(l1))
        return l2;
    return Cons(l1->e, append(l1->next, l2));
}

List reverse(List l) {
    if (isNull(l))
        return Void();
    return append(reverse(l->next), Cons(l->e, Void()));
}