Lecture 2026/02/26

Amortized Analysis, Tampering + forgery

void add (struct sequence *s, int i, int e){
if (s->size==s->cap){
s->array = realloc(s->array,___) 
}
}
circle-info

realloc

  • increases block of memory to a new size.

  • if necessary, allocates a new larger block + frees old block w/ data copied over.

  • How big is to make new array?

naive:

  • One larger each time it is TO FALL.

  • Considering adding k-elements to the end to the end of the sequence when it is already full (size n )

  • (n+1)+(n+2)+(n+3)+....

  • ⇒ O(n) time to add elts to the sequence every time.

Fix: Doubling strategy + use amortized analysis.

  • when the array is filled, double its size.

most calls will be cheap

If an array has cap k and is empty:

  • k inserts at a cost of 1 each ( k steps taken)

  • 1 insert costs k+1 - cap now 2k ( k+1 steps taken )

  • k-1 inserts at a cost of 1 each ( k-1 steps taken )

  • 1 insert costs 2k+1 - cap now 4k (2k+1 steps taken.

  • 2k-1 inserts cost 1 each ( 2k-1 steps )

  • 1 insert costs 4k+1 - cap now 8k ( 4k+1 steps )

...

  • `2j12^{j-1}k` inserts cost 1 each ( `2j12^{j-1}k-1` steps )

  • 1 insert costs 2^j k - cap now 2^{j+1}+1 steps.

  • Sum the inserts vs. sum of steps.

  • k + 1 + k-1 + 1 + 2k -1 +1 + ... + (2^{j-1}k-1)+1

  • k + 1 + k + 2k + ... + 2^{j-1}k

  • K+1 + k ( 1 + 2 + ... + 2^{j-1});

  • k+ 1 + k ( 2^{j}-1)

  • k+1 + k2^{j}-k

  • k2^{j}+1

total steps

  • k + k +1 + k-1 + 2k+1 + 2k-1 + 4k+1 + ... + 2^{j-1}k -1 + 2^{j}k +1

  • k + 2k + ... + 2^{j}k + 2^{j}k+ 1

  • k ( 1 + 2 + ... + 2^{j}) + 2^jk + 1

  • k(2^{j+1}-1) + 2^{j}k+1

  • 2k2^{j} - k + 2^{j}k + 1

  • 3(2^{j}k + 1 -k)

number of steps per insertion:

  • 32jkk+12jk+1\frac{3\cdot 2^jk-k+1}{2^{j}k+1}

  • limj(32jkk+12jk+1)\lim_{j\rightarrow \infty} (\frac{3\cdot 2^jk-k+1}{2^{j}k+1})

  • 33

Thus a constant number of steps (O(1)) # steps PER insertion when

taken over entire sequence of operations.

Note, people who use your Sequence ADT may not be using it correctly.

Defensive Programming

  • tampering + forgery of ADTs

e.g.

So the question becomes how to prevent?

  • Leave the Sequence Definition, OUT of the header file, so people can't define it.

    • In header file, just forward declare struct first.

  • Since the structure definition is out of the header file, the program will not know that the Sequence ADT has these different fields, size, cap, etc..., hence will throw an error to those trying to tamper/forge.

circle-info

sort of why you have to leave information out of header-files

Unfortunately:

Now prevents the "good" users from using the Sequence ADT.

Fix:

  • prevent tampering + forgery, while allowing regular use:

    • always going to use Sequence *s, instead of sequences.

    • since we know pointers are all the same size.

What are the increasesCap helper, can we prevent main.c from using it, since it isn't an ADT operation?

  • Leave the increaseCap declaration out of Sequence.h, then main.C would not be able to call it.

Unfortunately, this doesn't prevent a client from making their OWN declarations

e.g. making their own declarations of increaseCap.

circle-info

Design our ADT, practice of increasing arrays/double array, how to structure files with ADTs, how to keep implementations secret, and talked about void statics.

cut-off for midterm.


  • More ADT practice - Racket - Mutable maps/dictionaries.

  • make-map:

    • no params

    • precondition: true

Last updated