Efficient Calculation of Fibonacci Numbers, Binomial Coefficients, and Shortest Paths - Pr, Study notes of Computer Science

Algorithms and pseudocode for calculating fibonacci numbers, binomial coefficients, and the length of the shortest path between nodes in a graph. The fibonacci sequence is generated using recursion and dynamic programming. Binomial coefficients are calculated using pascal's triangle and dynamic programming. The shortest path problem is solved using floyd's algorithm. The document also includes examples and java code translations.

Typology: Study notes

Pre 2010

Uploaded on 03/28/2010

koofers-user-71o
koofers-user-71o 🇺🇸

8 documents

1 / 37

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
Dynamic Programming
With Divide and Conquer – top-down technique
Divide instance into subinstances
Solve subinstances
Combine solutions to solve original instance
May lead to several overlapping subinstances,
causing an inefficient algorithm
Dynamic programming - bottom-up technique
Normally start with the smallest, simplest
subinstances
May combine subinstance solutions, obtaining
answers to subinstances of increasing size
Can take advantage of any overlapping
subinstances
oKeep track of known results in a table
Printed on 2020-11-28 at 07:03
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c
pf1d
pf1e
pf1f
pf20
pf21
pf22
pf23
pf24
pf25

Partial preview of the text

Download Efficient Calculation of Fibonacci Numbers, Binomial Coefficients, and Shortest Paths - Pr and more Study notes Computer Science in PDF only on Docsity!

Dynamic Programming

With Divide and Conquer – top-down technique  Divide instance into subinstances  Solve subinstances  Combine solutions to solve original instance  May lead to several overlapping subinstances, causing an inefficient algorithm Dynamic programming - bottom-up technique  Normally start with the smallest, simplest subinstances  May combine subinstance solutions, obtaining answers to subinstances of increasing size  Can take advantage of any overlapping subinstances o Keep track of known results in a table Printed on 2020-11-28 at 07:

Fibonacci Numbers

Fib(0) = 0 Fib(1) = 1  n > 1 : Fib( n ) = Fib( n –1) + Fib( n –2) function Fib(n) if n < 2 then return n else return Fib ( n– 1) + Fib ( n– 2) n Fib(n) # calls # calls/Fib(n) 0 0 1 1 1 1 1 2 1 3 3 3 2 5 2. 4 3 9 3 5 5 15 3 6 8 25 3. 7 13 41 3. 8 21 67 3. 9 34 109 3. 10 55 177 3. 11 89 287 3. 12 144 465 3. 13 233 753 3. 14 377 1219 3. 15 610 1973 3. 16 987 3193 3. 17 1597 5167 3. 18 2584 8361 3. 19 4181 13529 3. 20 6765 21891 3. 21 10946 35421 3. 22 17711 57313 3. 23 28657 92735 3. 24 46368 150049 3. 25 75025 242785 3. Not surprising: nCalls( n ) = 1 + nCalls( n –1) + nCalls( n –2) Standard implementation is in fact an example of dynamic programming: combine earlier, simpler cases to generate later, more complex cases. k1=1 { will be fib(n-1) } k2=0 { will be fib(n) } for j = 1 to n k2 = k2 + k k1 = k2 – k1 {old k2} return k Printed on 2020-11-28 at 07:

Pascal’s Triangle Not necessary to fill entire table  suffice to keep a vector of length k, representing current line  update the vector left to right Here, the time to calculate takes a time in ( nk ) and space in ( k ) Printed on 2020-11-28 at 07: 0 1 2 3 ... k-1 k 0 1 1 1 1 2 1 2 1 : : n-1 C(n –1, k –1) C(n-1, k)

n C(n, k) n k

Alternative formulation: computing formula:  !! ! n k k n k n  ^       shows the symmetry:        ^       n k n k n So we can use min( k , nk ) for the calculation. Alternative formulation: for 0 < k < n :         ^       1 1 k n k n k n — see hand-out requires ( k ) (multiplications and divisions), as compared with dynamic programming requirement of ( nk ) additions. function C(n, k) ans = 1 k = min( k , nk ) while k > 0 ans = ans * n / k n = n – 1 k = k – 1 return ans Printed on 2020-11-28 at 07:

  • Probability of winning a single toss: 0.
  • Number of heads to win:
    • 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.
  • 0.000 0.550 0.798 0.909 0.959 0.982 0.992 0.996 0.998 0.
  • 0.000 0.303 0.575 0.759 0.869 0.931 0.964 0.982 0.991 0.
  • 0.000 0.166 0.391 0.593 0.745 0.847 0.912 0.950 0.973 0.
  • 0.000 0.092 0.256 0.442 0.608 0.740 0.834 0.898 0.939 0.
  • 0.000 0.050 0.164 0.316 0.477 0.621 0.738 0.826 0.888 0.
  • 0.000 0.028 0.102 0.220 0.361 0.504 0.633 0.739 0.821 0.
  • 0.000 0.015 0.063 0.150 0.266 0.397 0.527 0.644 0.741 0.
  • 0.000 0.008 0.039 0.100 0.191 0.304 0.427 0.546 0.654 0.
  • 0.000 0.005 0.023 0.065 0.134 0.228 0.337 0.452 0.563 0.
  • Probability of winning a single toss: 0.
  • Number of heads to win:
    • 1.000 1.000 1.000 1.000 1.
  • 0.000 0.666 0.888 0.963 0.988 0.
  • 0.000 0.444 0.740 0.888 0.954 0.
  • 0.000 0.295 0.591 0.789 0.899 0.
  • 0.000 0.197 0.460 0.679 0.826 0.
  • 0.000 0.131 0.350 0.569 0.740 0.

Since person A wins any given toss with probability p , and loses with probability q

P(i, j) = p P(i -1, j) + q P(i, j -1), i  1, j  1

We can compute P(i, j): function P(i, j) if i = 0 then return 1 else if j = 0 then return 0 else return p P(i -1, j) + q P(i, j –1) Let T(k) be the worst case time needed to calculate P(i, j) , where k = i + j Then T(1) = c

T(k)  2T(k-1) + d , k > 1

where c and d are constants

The total number of recursive calls is therefore 2 - 2 Here, the time to calculate P(n,n) that person A will win (having not started the tossing) is  Since  4T(k-2) + 2d + d, k > 2 n /(2n+1), the time to calculate P(n,n) is in O ( 4T(k-2) + 2d + d, k > 2 n ) and in

( 4T(k-2) + 2d + d, k > 2

n /n ) This is not practical for large values of n i + j j 2n n 2n n

Using Pascal’s Triangle we can declare an array and fill the entries Rather than fill line by line, we work diagonal by diagonal function series(n, p) array P[0..n, 0..n]

q  1  p

{Fill from top left to main diagonal}

for s  1 to n do

P[0,s]  1; P[s,0]  0

for k  1 to s - 1 do

p[k, s - k]  pP[k - 1, s - k] +

qP[k, s - k - 1] {Fill from below main diagonal to bottom right}

for s  1 to n do

for k  0 to n - s do

P[s + k, n - k]  pP[s + k - 1, n - k] +

qP[s + k, n - k - 1] return P[n, n] Since this algorithm only had to fill an n x n array, execution time is

(n

2 ) a storage space in(n) is sufficient

MultiStage Graphs

from Horowitz and Sahni, pp. 203 ff. G = ( N , E ) is a directed graph in which the nodes are partitioned into k ≥ 2 disjoint sets Vi , 1 ≤ Ik. In addition, if < u , v > is an edge in E, then uVi and vVi +1 for some I , 1 ≤ Ik. |V 1 | = |V k | = 1 The multistage graph problem is to find a minimum cost path from N 1 to Nn.

Example:

Nodes: {1..12} Stages: 5 — {1} , {2..5}, {6..8}, {9..11}, {12} Weighted Directed Edge List: (1, 2: 9) (1, 3: 7) (1, 4: 3) (1, 5: 2)

Note: extensive board work — applying dynamic programming to the above

Non-Greedy Change Making

We wish to make change for a given amount, based on currency/coinage whose values are contained in d[] (denomination). At the heart of this algorithm is the idea of finding the smallest number of currency units (coins from here on out) to make change for all amounts  the specified amount. This table (c[1..n][0..amount]) is built from the smallest coin — presumably the unit coin — upwards as the outer loop. The inner loop examines amounts up to the specified amount. Using a coin of the current denomination will decrease the amount to be returned. Thus we can look back to see how many coins will be required to finish the transaction to get the total number of coins, should we choose to use one of this denomination. On the other hand, we can look in the preceeding row of the table to see how many coins would be used if we do not use a coin of this denomination and simply use those of the lower denominations. [Note: there is a sentinel row in c: c[0][k] = 0]

// Table generation --- dynamic programming portion for ( k = 0; k <= amt; k++ ) // Sentinel row c[0][k] = 0; for ( j = 1; j <= n; j++ ) { coin[j] = 0; c[j][0] = 0; // Insure column 0 holds zeroes. for ( k = 1; k <= amt; k++ ) if ( j == 1 ) if ( k < d[j] ) c[j][k] = Integer.MAX_VALUE; else c[j][k] = 1 + c[j][k-d[j]]; else if ( k < d[j] ) c[j][k] = c[j-1][k]; else c[j][k] = Math.min(c[j-1][k], 1 + c[j][k-d[j]]); }

Dynamic Programming: Change Generation /* The dynamic programming change algorithm — C++ Taken from Brassard and Bratley (1996), pp. 263- Author: Timothy Rolfe, based on Brassard and Bratley */ #include <iostream.h> #include <iomanip.h> #include <stdlib.h> // RAND_MAX const bool DEBUG = false; // For debugging: display the array contents void display (int *a, int nrow, int ncol) { int row, col; for ( row = 0; row < nrow; row++ ) { for ( col = 0; col < ncol; col++ ) cout << setw(3) << ( a[row][col] > 99? -1 : a[row][col] ); cout << endl; } } // Used in change() inline int Min(int p, int q) { return p > q? q : p; } /

  • Make change for "amt" units, based on the denominations in d[].
  • Dynamic programming solution: in the process, this builds the
  • table that would solve the change-making process for ALL amount
  • up to "amt". The number of coins used is returned through coin[].
  • NOTE: d[] MUST be sorted; presumably d[1] = 1 / void change(int amt, int coin[], int d[], int n ) { int c = new int [n+1]; int j, k; // c[0][k] remains unchanged at zero as a sentinel row. for ( j = 0; j <= n; j++ ) c[j] = (int) calloc (amt+1, sizeof(int)); // Table generation --- dynamic programming portion for ( j = 1; j <= n; j++ ) { coin[j] = 0; for ( k = 1; k <= amt; k++ ) if ( j == 1 ) if ( k < d[j] ) c[j][k] = RAND_MAX; else c[j][k] = 1 + c[j][k-d[j]]; else if ( k < d[j] ) c[j][k] = c[j-1][k]; else c[j][k] = Min(c[j-1][k], 1 + c[j][k-d[j]]); } if (DEBUG) display(c, n, amt+1); j = n; k = amt; // From the table, get the coins used --- greedy portion while ( k > 0 && j > 0 ) if ( c[j][k] == c[j-1][k] ) // Denom. not used j = j - 1; else // Denom. WAS used { ++coin[j]; k = k - d[j]; } // Plug the memory leak --- return scratch matrix to the heap for ( j = 0; j < n; j++ ) free (c[j]); delete[] c; } void main ( void ) { / Debug data: int denom[] = { 3, 1, 4, 6 }; // / Production data: / int denom[] = { 6, 1, 3, 6, 12, 24, 30 }; // // Capture the number of cells in denom[] int n = sizeof(denom)/sizeof(int); int coin = (int) calloc ( n, sizeof(int) ); int amt; // Amount required in coinage int k; // loop index cout << "Amount: "; cin >> amt; change(amt, coin, denom, n); for ( k = 0; k < n; k++ ) cout << setw(2) << denom[k] << ": " << coin[k] << " used." << endl; }

Specimen program execution follows the Java version.