0% found this document useful (0 votes)
10 views14 pages

Chapter2 Odd

2020111522256195Foundations of Algorithms - Richard E. Neapolitan Chapter2_odd Solution

Uploaded by

히마가나
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views14 pages

Chapter2 Odd

2020111522256195Foundations of Algorithms - Richard E. Neapolitan Chapter2_odd Solution

Uploaded by

히마가나
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 14

Chapter 2: Divide-and-Conquer Solutions

Section 2.1

1) Use Binary Search, Recursive (Algorithm 2.1) to search for the integer 120 in the
following list (array) of integers. Show the actions step by step.

12 34 37 45 57 82 99 120 134

Solution:

1. Compute the middle index from initial low (1) and high (9) indices: mid = 5

2. Check key at position mid: 120 > S[mid] = 57

3. Compute new middle index from new low (mid=5) and previous high (9) indices: mid
=7

4. Check key at position mid: 120 > S[mid] = 99


5. Compute new middle index from new low (mid=5) and previous high (9) indices: mid
=8

6. Check key at position mid: 120 == S[mid]

7. Key found, return index mid=8


Chapter 2: Divide-and-Conquer Solutions

3) Let us assume that we always perform a successful search. That is, in Algorithm 2.1 the
item x can always be found in the list S. Improve Algorithm 2.1 by removing all unnecessary
operations.

Solution:

index location (index low, index high){

index mid;

mid = (low + high)/2;

if (x = S[mid])

return mid

else if (x < s[mid])

return location (low, mid-1);

else

return location (mid+1, high);

where

Prob: Determine whether x is in the Sorted array ‘s’ of size ‘n’

Input: Positive integer n, sorted array of key ‘s’, indexed from 1 to n a key x.

Output: Location, the location of x is s


Chapter 2: Divide-and-Conquer Solutions

5) Suppose that, in Algorithm 2.1 (line 4), the splitting function is changed to mid = low;.
Explain the new search strategy. Analyze the performance of this strategy and show the
results using order notation.

Solution: The algorithm now is:

index location (index low, index high) {


index mid;
if (low > high)
return 0;
else {
mid = low;
if (x = = s [mid])
return mid
else if (x < s[mid])
return location (low, mid-1);
else
return location (mid+1, high)
}
}

Complexity analysis: The worst case for this algorithm is when x is either the largest array
element, or larger than the largest. In either case, n comparisons are needed, since always
the branch location (mid+1, high) is taken. This is a Linear Search → (n).
Chapter 2: Divide-and-Conquer Solutions

7) Use the divide-and-conquer approach to write an algorithm that finds the largest item in
a list of n items. Analyze your algorithm, and show the results in order notation.

Solution:

int maximum (int low, int high) {


if (low == high)
return a[Low];
int mid = (low + high)/2; //integer division
int L1 = maximum (low, mid);
int L2 = maximum (mid + 1, high);
if A[L1] > A[L2] then
return L1
else
return L2
}

Example: Let A be this array:

7 3 8 13 9 14
1 2 3 4 5 6

Level 1 : 1 ≠ 6 → mid = (1+6)/2 = 3 → A[mid] = 8

Recursive calls:

Level 2:

7 3 8 13 9 14
1 2 3 4 5 6

↑ ↑

4/2

10/2 = 5

7 3 8 13 9 14
1 2 ↑
Chapter 2: Divide-and-Conquer Solutions

max

7 3 8 13 9 14

7 9 6

(0 + 2)/2 = 1

0, 1

7 9

6
2

1/2 0

Complexity analysis: T(n) = 2T(n/2) + 1, which yields (n).

Section 2.2
Chapter 2: Divide-and-Conquer Solutions

9) Give the tree of recursive calls in Exercise 8.

Solution:

11) void nonrecursiveMergesort(int n, keytype S[]) {

index i, j;

for(i=1; i<=n; i = i*2)

for(j=1; j<=n; j = j + 2*i)

merge2(j, j+k, j+2*k, S[]);

The merge2 method is specified on page 63. We make the small modification of passing
S[] as a parameter.
Chapter 2: Divide-and-Conquer Solutions

13) Write an algorithm that sorts a list of n items by dividing it into three sublists of about
n=3 items, sorting each sublist recursively and merging the three sorted sublists. Analyze
your algorithm, and give the results under order notation.

Solution:

Input : Positive integer n, array of keys S, indexed from 1 to n

Output :The array S containing the keys in nondecreasing order.


void mergesort3 (int n, keytype S[ ]) {
if (n>1) {
int h = floor(n/3), i = 2*h;
int m = i - h , t = n – i;
int U[1…h], V[1…m], W[1…t]
Copy S[1] through S[h] to U[1] through U[m]
Copy S[h+1] through S[i] to V[1] through V[t]
Copy S[i+1] through S[n] to W[1] through W[t]
mergesort3 (h, U);
mergesort3 (m, V);
mergesort3 (t, W);
merge (h, m, t, U, V, W, S);
}
}

void merge3 (int h, int m, int t, const int U[], const int V[], const int W[], int S[]) {

//Similar to Algorithm 2.3

}
Chapter 2: Divide-and-Conquer Solutions

Section 2.3
15)

Assume g(n) basic operations for partitioning and combining, and none for an instance of
size 1.

(a) Write a recurrence equation T(n) for the number of basic operations needed to solve P
when the input size is n.

Solution: T(n) =5T(n/3) + g(n)

(b) What is the solution to this recurrence equation if g(n)∈ Θ(n)? (Proof is not required.)

Solution: We assume a linear function g(n) = a∙n+b, and n a power of 3: n = 3k. The
recursion is

T(3k) = 5T(3k-1) + a∙3k + b

We denote T(3k) = xk, and the recursion becomes

xk – 5xk-1 = a∙3k + b

Which is solved as a non-homogeneous recursion as explained in Theorem B.3:


The solution to the homogeneous part is r5 = 5, and the two non-homogeneous terms each
contribute a root: r1 = 1, and r3 = 3. The general form of the solution is:

xk = C11k + C33k + C55k

If need be, the constants are found by making use of the first 3 terms of the recursion: x0 =
0, x1 = 3a +b, x2 = 24a + 6b. We solve the linear system of 3 eqns. with 3 unknowns to find
C3 = – 3a/2, etc.
Chapter 2: Divide-and-Conquer Solutions

(c) Assuming that g(n) =n2, solve the recurrence equation exactly for n =27.

Solution: By direct substitution, we find T(1) = 0, T(3) = 9, t(9) = 126, T(27) = 1359.

(d) Find the general solution for n a power of 3.

Solution: We still assume g(n) = n2, as above. The recursion is T(n) = 5T(n/3) + n2, so we
substitute n = 3k, and rename T(3k) = xk to obtain a new recursion xk – 5xk-1 = 9k. This is
solved as a non-homogeneous recursion (Theorem B.3), with the solution xk = C55k + C99k.
The constants are determined by solving the linear system x0 = 0, x1 = 9, and we have C5 = -
9/4, C9 = 9/4.

The final solution is T(3k) = xk = -9/4∙5k + 9/4∙9k

17) Divide-and-conquer algorithm for Towers of Hanoi with n disks.

Solution:

void ToH(int n, char x, char y, char z) {


if (n > 0) {
ToH (n−1, x, y, z);
cout << x << " moves to " << y << endl;
ToH (n−1, z, y, x);
}
}

(a) Show for your algorithm that S (n) = 2n-1. (Here S (n) denotes the number of steps
(moves), given an input of n disks.)

Solution: The recursion equation is T(n) = 2T(n-1) + 1 for n > 1, T(1) = 1. By direct
substitution, we have T(n) = 2·(2·(…) + 1) + 1 = 2n-1 +2n-2 + … + 2 + 1, which, using the sum
of the geometric series from Example A.4, is 2n – 1.

(b) Prove that any other algorithm takes at least as many moves as given in part (a).
Solution: In order to move the largest disk (call it Dn), any other algorithm A’ must first
place disks 1 through n-1 on the intermediate peg, then perform at least one move to place
Dn on the destination peg, and then move disks 1 through n-1 on the destination peg, so we
have that T’(n) ≥ 2T’(n-1) + 1, for n > 1.
Chapter 2: Divide-and-Conquer Solutions

The boundary condition is T’(1) > 1.

If we now write the sum from the solution to part (a), it’s clear that T’(n) ≥ T(n).

Section 2.4

19) Use Quicksort (Algorithm 2.6) to sort the following list. Show the actions step by step.

123 34 189 56 150 12 9 240

Solution:

i j S[0] S[1] S[2] S[3] S[4] S[5] S[6] S[7]


− − 123 34 189 56 150 12 9 240
1 7 123 34 189 56 150 12 9 240
2 6 123 34 9 56 150 12 189 240
3 5 123 34 9 56 150 12 189 240
5 4 123 34 9 56 150 12 189 240
− − 123 34 9 56 150 12 189 240

I j S[0] S[1] S[2] S[3] S[4]


0 4 123 34 9 56 12
1 2 12 34 9 56 123
2 1 9 34 12 56 123

I J S[5] S[6] S[7]


5 7 150 189 240
6 6 150 189 240

9 34 12 56 123 66 150 189 240


Chapter 2: Divide-and-Conquer Solutions

21) Show that if then

This result is used in the discussion of the worst-case time complexity analysis of
Algorithm 2.6 (Quicksort).

Solution:

(p − 1)(p − 2) (n − p)(n − p − 1)
w(n)  + + (n − 1)
2 2

(n − 1)(n − 2) 2(n − 1) n 2 − 3n + 2 + 2n −2 n 2 − n n(n − 1)


 +0+ = = =
2 2 2 2 2
Second solution:

(n − 1)(n − 2) (n − p)(n − p − 1) 2(n − 1)


w(n)  + + =
2 2 2

n2 − 3n + 2 + n 2 − np − n − pn + p 2 + p + 2n − 2
=
2

= 2n2 – 2np −2n + p2 p/2

= 2n2 – 2n (p + 1) + p (p + 1)/2

= 2n2 + (p – 2n) (p + 1)/2


= 2n2 – n (n + 1)/2

= 2n2 − n2 – n = n2 – n/2

n(n − 1)
=
2
Chapter 2: Divide-and-Conquer Solutions

23) Write a non-recursive algorithm for Quicksort (Algorithm 2.6). Analyze your

algorithm, and give the results using order notation.

Solution: Non-Recursive Quick sort Algorithm by using a stack.


struct Stack {
int low, high:
};
void quick-sort (int a[], int n) { //n elements in the array
struct Stack S[100];
int top, i, j, pivot, lo, hi;
top = 0;
S[top].low = 0; //first array index
S[top].high = n − 1; //last array index
top++; //pushing
while (top > 0) { //stack is not empty
lo = s[top].low;
hi = s[top].high;
top--; //pulling sub-problem off stack
if (lo >= hi) continue; //empty problem
pivot = a[lo]; //pivot is leftmost element
i = lo;
j = hi;
while (i < j) {
while (i < hi && a[i] <= k)
i++;
while (j < lo && a[j] >= k)
j−−;
if (i < j)
swap(i,j);
}
if (lo! = j)
swap(lo, j); //place pivot in the “middle”
}
S[top].low = j + 1;
S[top].high = hi;
top++; //pushing right sub-array
S[top].low = lo;
S[top].high = j − 1;
top++; //pushing left sub-array
}// end of while
}

The operation is identical to that of the recursive Quicksort, so it has the same
complexity: (n2) in the worst case and (n∙lg n) on average.
Chapter 2: Divide-and-Conquer Solutions

Section 2.5

25) Show that the number of additions performed by Algorithm 1.4 (MatrixMult)

can be reduced to n3 − n2 after a slight modification of this algorithm.

Solution:

The loops Algorithm 1.4 are modified thus:


for (i=1; i<=n; i++)
for (j=1; j<=n; j++)
C[i][j] = C[i][1] * C[1][j];
for (k=2; k<=n; k++)
C[i][j] = C[i][j] + C[i][k] * C[k][j];

This way, one less addition is performed per innermost for loop, and, since that loop
executes n2 times, the total number of additions is n2 less: n3 − n2.

27) When n=64, n3 = 262,144

29) Write a recurrence equation for the modified Strassen’s algorithm developed by
Shmuel Winograd that uses 15 additions/subtractions instead of 18. Solve the
recurrence equation, and verify your answer using the time complexity shown at the
end of Section 2.5.

Solution: The recursion is the one on p.75, with 15 instead of 18:

T(n) = 7T(n/2) + 15(n/2)2, if n>1 (with n = 2k)

T(1) = 0
The solution follows closely that in Example B.20:
By substituting xk instead of T(2k), we obtain the recursion xk = 7xk-1 + 15∙4k-1, which is non-
homogeneous, with roots 7 and 4. The solution has the form xk = c17k + c24k.
We find the constants by plugging in the values for x0 T(1) = 0, and x1 = T(2) = 15. We
obtain c1 = 5, c2 = -5, so xk = 5∙7k - c2∙4k.
Going back to the variable n, we have 4k = n2, and 7k = (2log2(7))k = (2k)log2(7) = n2.8073… , so
T(n) = 5n2.8073 – 5n2, which corresponds to the formula on p.76 of the text.
Chapter 2: Divide-and-Conquer Solutions

Section 2.8

37) Use the divide-and-conquer approach to write a recursive algorithm that computes n!.
Define the input size (see Exercise 36 in Chapter 1), and answer the following questions.
Does your function have an exponential time complexity?
Does this violate the statement of case 1 given in Section 2.8?

Solution: Input: n and m (m = 1 for first call)


Output: n!
integer factorial (n, m) {
if (n==m)
return n;
else {
if (m > n)
return 1;
else
n + m n + m
return (factorial (n,  ) × factorial (  2  + 1, m));
 2 
}
}
Complexity analysis:

In the above algorithm, an instance of size n – m + 1 is divided into two instances, each of
almost size (n – m + 1)/2. The recurrence is T(n) = 2T(n/2) + 1, which is easily solved as a
non-homogeneous equation (Theorem B.3) or by direct expansion to yield T(n) = n – 1 ∊
(n).

If, however, we define the input size as the number of bits required to represent the input n
(as in Exercise 36 in Chapter 1), then:
• The input size is log2n.
• The output size is log2(n!) = log2(2) + log2(3) + … + log2(n/2 -1) + log2(n/2) + … +
log2(n).
• Let n = 2k
o The input size is k
o The output size is log2(2) + log2(3) + … + log2(n/2 -1) + (k – 1) + … + k.
o Since the sequence is strictly increasing, the sum above is less than
o (k – 1) + (k – 1) + … + (k – 1) = (k – 1)·n/2 = (k – 1)2k-1.
• Therefore, the relationship between input and output is k → (k·2k), which means a
super-exponential time complexity.

The statement of case 1 in Section 2.8 is not violated, since the two instances are not each
of size almost equal to the input size; as seen in the last sum above, the second instance is
(k – 1)2k-1, that is super-exponential in the input k.

You might also like