LAB 9
Matching pairs
-
The idea with two indexes…
-
Draw a picture
-
What if the array is already sorted?
-
What is then an optimal approach? Using Binary Search or using two indexes?
Algorithm:
boolean matchingPairs(int[] A, int s) sort(A) i := 1 j := A.length while i < j and A[i] + A[j] != s do // (*) = Maintenance Snapshot if A[i] + A[j] > s then j-- else i++ if i >= j then return false else return true
Loop invariant:
LI: For every i'' such that i''<i there is no matching pair for i'' For every j'' such that j''>j there is no matching pair for j'' Proof. Initialization: (before the loop) Only possibilities are i''=0 and j''> A.length And thus trivially there are no matching pairs for i'' and j'' in A Maintenance: (at the step (*)) Assume that at the point (*) we have indexes i and j. Assume that in the previous step indexes were i' and j'. Now we distinguish cases: Case 1: j was decreased in the previous step i.e., j=j'-1 and i=i From LI we have that LI holds for all j'' such that j ''> j' now it remains to show that it also holds for j''= j'. That is, there is no matching pair for j'. Assume i_j' is a candidate for a matching pair with j' We distinguish different possibilities for i_j' 1. i_j'<i According to LI there is no matching pair for i_j' 2. i_j'=i Since j was decreased in the previous step then we know that A[i']+A[j']>s Since i'=i (i didn't change) then also A[i']+A[j']>s Thus, i is not matching pair for j' 3. i_j'>i Since A is sorted we have that A[i_j']>A[i] Since A[i']+A[j']>s then it holds A[i_j'] + A[j'] > s Thus, there is no i_j'>i that is matching pair of j' All together, there is no matching pair for j'' s.t. j'' = j' (=j+1), and from LI (of the previous step) no matching pair for j'' s.t. j'' > j'. Thus, no matching pair for j'' s.t. j'' > j. Case 2: i was increased in the previous step i.e., i=i+1 and j=j' // Can be shown in the same way as Case 1 Termination. The loop either terminated because: - i=j. From LI it means that for each i''<i (=j) there is no matching pair, and for each j''>j (=i) there is no matching pair. Thus, there is no matching pair in A - S[i]+S[j]=s, i.e., matching pair is found
Discussion
-
What is a loop invariant?
-
Snapshot-view of the problem vs Assertion view
- Imagine how the progresses from step i to step i+1
- Focus on implement the transitions
- In principle, the transition is organized to maintain loop invariant -
Think of the middle case (not exact algorithm step)
-
Then code to match transitions (= transition of Loop Invariant)
-
Highlights from the book “HOW TO THINK ABOUT ALGORITHMS” about loop invariants
Find closest sum to s
-
Story: Imagine in DeSpar you receive a coupon for 50 Eur.,
and you want to either spent or lose as least as possible -
Variant of matching pairs
-
Find i,j, and s’=A[i]+A[j] such that for every other i’ and j’
|s-A[i]-A[j]|>=|s-A[i’]-A[j’]|
-
Hint: adaptation idea: store i and j that we closest s
Algorithm:
structure Pair int i_opt; int j_opt; Pair MatchingPairs(int [] A) i := 1 j := A.length i_opt := i j_opt := j minDiff := s - A[i_opt] - A[j_opt] while i<j && A[i]+A[j] != s if absolute(s - (A[i]+A[j])) < minDiff i_opt := i j_opt := j if A[i]+A[j] > s then j-- else i++ if A[i]+A[j] = s return Pair(i,j) else Pair(i_opt,j_opt)
Exact Contiguous Sum
Problem:
We are given an array of positive integers A and a positive number s.
The question is whether there exists a segment A[i..j] of A
such that the sum of the numbers of this segment equals s.
A[i]+A[i+1]+..+A[j-1]+A[j] = s
Straightforward approach
boolean ContiguousSum(int [] A, s) for i := 1 to A.length do for j := i to A.length do sum := 0 for k := i to j do sum += A[k] if sum = s then return true return false
runtime: cubic
Ideas: …
boolean ContiguousSum(int [] A, s) for i := 1 to A.length do sum := 0 for j:= i to n do sum := sum + A[j] if sum = s then return true // jumping out the window solution :( return false
runtime: quadratic
-
Can we do better?
-
Let’s look it again…
(pause)
- We iterate over array with i
- We want that each i’<i the contiguous sum does not start at i’
-
What to do?
-
Let us look at snapshots:
- sum = A[i]<s
- sum = A[i]+A[i+1] < s
- sum = A[i]+A[i+1]+A[i+2] < s
- sum = A[i]+A[i+1]+A[i+2] = s YEAH! but if not we ctd.
- …
-
sum = A[i]+A[i+1]+A[i+2]+A[i+3]+A[i+4] > s (at some moment)
-
remove A[i] from the above sum ?
-
i is not in the segment because we try all possible segments for i
-
thus we look at the next one i:=i+1
-
what about sum, should we recompute it?
-
not all right?
-
s’ := s - A[i]
-
what about j (=i+4 in the lase case)
- should it start from i again?
- no…
- why it is sufficient to ctd with A[i+1]+A[i+2]+A[i+3]+A[i+4], j=i+4
- and not necessary to look into with A[i+1]+A[i+2]+A[i+3]
- because we know that A[i+1]+A[i+2]+A[i+3] < s
-
since A[i]+A[i+1]+A[i+2]+A[i+3]<s
-
running time: n^2 or n ?
-
Try now!
(pause)
- We develop the algorithm on fly
Algorithm (first attempt):
boolean ContiguousSum(int [] A, s) i := 1 j := 1 sum := A[i] while i<=n && sum!=s while j< n && sum < s // move j j++ sum := sum + A[j] if sum > s // move i sum := sum - A[i] if i<n // move i if i<n i++ if i>j // e.g., when A[j]>s then sum=A[j(=i)] j := i if sum<s and j=n // edge case -- find way to solve it i : = n+1 // just to exit if sum = s then return true else return false
Algorithm with a single loop:
boolean ContiguousSum(int [] A, s) i := 1 j := 1 sum := A[i] while i<=A.length && j<=A.length && sum != s if j<=A.length && sum < s // move j j++ // if sum<s and j=n if j<n sum := sum + A[j] if i<=A.length && sum > s // move i i++ if i < j sum = sum - A[i] else // i >= j sum := A[i] j := i if sum = s then return true else return false
Loop invariant
For every i' < i For every j' < j: For i'<=j' A[i']+..+A[j'] != s AND sum := A[i]+..+A[j]
perhaps better
( For every i' <= i For every j' < j: For i'<=j' A[i']+..+A[j'] != s OR For every i' < i For every j' <= j: For i' <= j' A[i']+..+A[j'] != s ) AND sum := A[i]+..+A[j]
Maximal Integral
Problem:
We are given an array of integers A (also negative ones)
The question is to find the segment A[i..j] that has the biggest sum
(A[i]+A[i+1]+..+A[j-1]+A[j] = sum).
Idea:
- like an integral (surface bellow and above)
- how to solve it?
- straight: quadratic
- more optimal approach?
(pause)
- Hint: Divide & Conquer
- Develop picture on board
- Discuss
(pause)
- Show alg
Alg using Divide & Conquer approach:
Structure Inter{ int ms; // max sum int ml; // max left int mr; // max right int is; // interval sum } Inter MaxContSum(int [] A, int l, int r) Inter inter if l<r int m := (l+r)/2 lInt := MaxContSum(A,l,m) rInt := MaxContSum(A,m+1,r) inter.ms := max(lInt.ms,rInt.ms,lInt.mr+rInt.ml) inter.ml := max(lInt.is + rInt.ml, lInt.ml) inter.mr := max(rInt.is + lInt.mr, rInt.mr) inter.is := lInt.is+rInt.is else // base case inter.ms := A[l] inter.ml := A[l] inter.mr := A[l] inter.is := A[l] return inter
-
Running time??
-
Master theorem
T(n) = 2 T(n/2) + n^0
=> T(n) = \Theta(n)