Introduction to Algorithms: Verification, Complexity, and Searching (2) Andy Wang

advertisement
Introduction to Algorithms:
Verification, Complexity, and
Searching (2)
Andy Wang
Data Structures, Algorithms, and
Generic Programming
Lecture Overview


Review: Binary search algorithms
More on computational complexity
Binary Search

Goal


Find a value in a collection of values
Idea

Divide and conquer
Binary Search (2)

Requirements

Collection must be “array”-like



Can use an index to jump to any array element
Collection must be sorted
Efficiency


Very fast
No extra space required
Binary Search—the idea
0
11
1
23
2
35
3
47
35 < 47
Search range: 0 – 7
Search target: 35
4
53
5
60
6
72
7
82
Binary Search—the idea
0
11
1
23
2
35
23 < 35
Search range: 0 – 3
Search target: 35
3
47
4
53
5
60
6
72
7
82
Binary Search—the idea
0
11
1
23
2
35
35 == 35
Search range: 2 - 3
Search target: 35
3
47
4
53
5
60
6
72
7
82
Binary Search Algorithm

Three versions




Binary_search
Lower_bound
Upper_bound
Assumptions



Collection L of data type of T with size sz
L is sorted
Element t of type T
Binary Search Algorithm (2)
0
11
1
23
2
35
Lower bound

3
47
4
53
5
60
6
72
7
82
Upper bound
Outcomes



Binary_search: true if t in L; false, otherwise
Lower_bound: smallest j, where t <= L[j]
Upper_bound: smallest j, where t < L[j]
If there are duplicate entries
0
11
1
23
2
35
Lower bound
Smallest j, where t <= L[j]
Search range: 0 – 7
Search target: 35
3
35
4
53
5
60
6
72
7
82
Upper bound
Smallest j, where t < L[j]
If t is not in L…
0
11
1
23
2
24
3
25
4
53
Lower bound
Upper bound
Search range: 0 – 7
Search target: 35
5
60
6
72
7
82
Correctness and Loop Invariants

Correctness




Loop termination
State when entering the loop
State when exiting the loop
Loop invariants


Conditions that remain true for each iteration
Mathematical induction
Invariants—Binary Search
unsigned int lower_bound(T* L, unsigned max, T t) {
unsigned int low = 0, mid, high = max;
while (low < high) {
// (1) low < high
// (2) L[low - 1] < t <= L[high] (if index is valid)
mid = (low + high) / 2;
if (L[mid] < t) {
low = mid + 1;
} else {
high = mid;
}
// (3) low <= high
// (4) high – low has decreased
// (5) L[low - 1] < t <= L[high] (if index is valid)
}
return low;
}
Invariants—Binary Search
unsigned int lower_bound(T* L, unsigned max, T t) {
unsigned int low = 0, mid, high = max;
while (low < high) {
// (1) low < high
// (2) L[low - 1] < t <= L[high] (if index is valid)
mid = (low + high) / 2;
if (L[mid] < t) {
t does not have to be in L
low = mid + 1;
} else {
high = mid;
}
// (3) low <= high
// (4) high – low has decreased
// (5) L[low - 1] < t <= L[high] (if index is valid)
}
return low;
}
Invariants—Binary Search
// (1) low < high
// (2) L[low - 1] < t <= L[high]
mid = (low + high) / 2;
if (L[mid] < t) {
low = mid + 1;
} else {
high = mid;
}
// (3) low <= high
low = mid + 1 = (old_low + high)/2 + 1
low <= (old_low + high)/2 + 1
low < (high + high)/2 + 1
low < high + 1
low <= high
Invariants—Binary Search
// (1) low < high
// (2) L[low - 1] < t <= L[high]
mid = (low + high) / 2;
if (L[mid] < t) {
low = mid + 1;
} else {
high = mid;
}
// (3) low <= high
high = mid = (low + old_high)/2
high > (low + old_high)/2 - 1
high > (low + low)/2 - 1
high > low – 1
high >= low
Invariants—Binary Search
unsigned int lower_bound(T* L, unsigned max, T t) {
unsigned int low = 0, mid, high = max;
while (low < high) {
// (1) low < high
// (2) L[low - 1] < t <= L[high] (if index is valid)
mid = (low + high) / 2;
if (L[mid] < t) {
low = mid + 1;
Termination:
} else {
(3) shows that the loop can terminate
high = mid;
(4) shows progress
}
// (3) low <= high
// (4) high – low has decreased
// (5) L[low - 1] < t <= L[high] (if index is valid)
}
return low;
}
Invariants—Binary Search
unsigned int lower_bound(T* L, unsigned max, T t) {
unsigned int low = 0, mid, high = max;
while (low < high) {
// (1) low < high
// (2) L[low - 1] < t <= L[high] (if index is valid)
mid = (low + high) / 2;
if (L[mid] < t) {
Return value:
low = mid + 1;
(5) smallest t <= L[j], since
} else {
L[j < low] != t, and t <= L[high]
high = mid;
}
// (3) low <= high
// (4) high – low has decreased
// (5) L[low - 1] < t <= L[high] (if index is valid)
}
return low;
}
Computational Complexity



Compares growth of two functions
Independent of constant multipliers and
lower-order effects
Metrics



“Big O” Notation
“Big Omega” Notation
“Big Theta” Notation
Big “O” Notation

f(n) is O(g(n))

iff  c, n0 > 0 | 0 < f(n) < cg(n)  n >= n0
cg(n) = cn2
f(n) = n
n0
Examples

F(n) is O(1)




F(n) = 1
F(n) = 2
F(n) = c (constant)
F(n) is O(log(n))




F(n) = 1
F(n) = 2log(n)
F(n) = 3log2(4n5) + 1
F(n) = c1logc2(c3nc4) + O(log(n)) + O(1)
Examples

F(n) = O(n)





F(n) = 2log(n)
F(n) = n
F(n) = 3n + 1
F(n) = c1n + O(n) + O(log(n))
F(n) = O(nlog(n))




F(n) = 3n + 2
F(n) = nlog(n)
F(n) = 3nlog4(5n7) + 2n
F(n) = c1nlogc2(c3nc4) + O(nlog(n)) + O(n)
Examples

F(n) = O(n2)




F(n) = 3nlog(n) + 2n
F(n) = n2
F(n) = 3n2 + 2n + 1
F(n) = c1n2 + O(n2) + O(nlog(n))
Big “Theta” Notation

f(n) is (g(n))

iff  c1, c2, n0 > 0 | 0 < c1g(n) < f(n) < c2g(n) 
n >= n0
2g(n) = 2n
f(n) = n
1/2g(n) = 1/2n
n0
Examples

F(n) is (1)




F(n) = 1
F(n) = 2
F(n) = c (constant)
F(n) is (log(n))




F(n) = 1
F(n) = 2log(n)
F(n) = 3log2(4n5) + 1
F(n) = c1logc2(c3nc4) + O(log(n)) + O(1)
Examples

F(n) = (n)





F(n) = 2log(n)
F(n) = n
F(n) = 3n + 1
F(n) = c1n + O(n) + O(log(n))
F(n) = (nlog(n))




F(n) = 3n + 2
F(n) = nlog(n)
F(n) = 3nlog4(5n7) + 2n
F(n) = c1nlogc2(c3nc4) + O(nlog(n)) + O(n)
Examples

F(n) = (n2)




F(n) = 3nlog(n) + 2n
F(n) = n2
F(n) = 3n2 + 2n + 1
F(n) = c1n2 + O(n2) + O(nlog(n))
Big “Omega” Notation

f(n) is (g(n))

iff  c, n0 > 0 | 0 < cg(n) < f(n)  n >= n0
f(n) = n
cg(n) = c
n0
Examples

F(n) is (1)




F(n) = 1
F(n) = 2n
F(n) = n2
F(n) is (log(n))




F(n) = 1
F(n) = 2log(n)
F(n) = nlogn + n + 1
F(n) = n3
Examples

F(n) = (n)





F(n) = 2log(n)
F(n) = n
F(n) = 3n2 + 1
F(n) = nlogn + 3n2 + 1
F(n) = (nlog(n))




F(n) = 3n + 2
F(n) = nlog(n)
F(n) = 3n2
F(n) = n3 + 2n2
Examples

F(n) = (n2)



F(n) = 3nlog(n) + 2n
F(n) = n2
F(n) = 3n3 + 2n + 1
Order the following functions…

n1/50, log(n2), (log(n))2, 5n2, 100log(n), 1.1n
Order the following functions…





n1/50, log(n2), (log(n))2, 5n2, 100log(n), 1.1n
log(n2) = 2log(n)
(log(n))2 = log(n)log(n)
100log(n) = nlog(100) = n2
log(n2) < (log(n))2 < n1/50 < 100log(n) < 5n2 <
1.1n
Complexity Analysis

Steps




Find n = size of input
Find an atomic activity to count
Find f(n) = the number of atomic activities
done by an input size of n
Complexity of an algorithm = complexity of
f(n)
Algorithm Complexity—Loops
for (j = 0; j < n; ++j) {
// 3 atomics
}

Complexity = (3n) = (n)
Loops with Break
for (j = 0; j < n; ++j) {
// 3 atomics
if (condition) break;
}



Upper bound = (3n) = (n)
Lower bound = (3) = (1)
Complexity = O(n)
Loops in Sequence
for
//
}
for
//
}

(j = 0; j < n; ++j) {
3 atomics
(j = 0; j < n; ++j) {
5 atomics
Complexity = (3n + 5n) = (n)
Nested Loops
for (j = 0; j < n; ++j) {
// 2 atomics
for (k = 0; k < n; ++k) {
// 3 atomics
}
}

Complexity = ((2 + 3n)n) = (n2)
Sequential Search
for (T item = begin(L); item != end(L); item = next(L)) {
if (t == item) return true;
}
if (t == item) return true;
return false;



Input size: n
Atomic computation: comparison
Complexity = O(n)
Binary Search
unsigned int lower_bound(T* L,
unsigned max, T t) {
unsigned int low = 0, mid,
high = max;
while (low < high) {
mid = (low + high) / 2;
if (L[mid] < t) {
low = mid + 1;
} else {
high = mid;
}
}
return low;
}



Input size: n
Atomic computation:
comparison
Complexity
= k iterations x
1 comparison/loop
Iteration Count for Binary Search
unsigned int lower_bound(T* L,
unsigned max, T t) {
unsigned int low = 0, mid,
high = max;
while (low < high) {
mid = (low + high) / 2;
if (L[mid] < t) {
low = mid + 1;
} else {
high = mid;
}
}
return low;
}
Iter #
search space
1
n
2
n/2
3
n/4
k
n/2(k-1)
 n/2(k-1) = 1
 n = 2(k-1)
 log2(n) = k - 1
Iteration Count for Binary Search
unsigned int lower_bound(T* L,
unsigned max, T t) {
unsigned int low = 0, mid,
high = max;
while (low < high) {
mid = (low + high) / 2;
if (L[mid] < t) {
low = mid + 1;
} else {
high = mid;
}
}
return low;
}





n/2(k-1) = 1
n = 2(k-1)
log2(n) = k - 1
log2(n) + 1 = k
Complexity function
f(n) = log(n) iterations
x 1 comparison/loop =
(log(n))
Announcement

Exam 1 (9/29)






String class
BitVector and Bit operations
Hash functions/hash tables
Templates
Vectors
Algorithm verification, complexity analysis,
and searching
Download