home *** CD-ROM | disk | FTP | other *** search
/ Amiga ISO Collection / AmigaUtilCD2.iso / Programming / GCC / GERLIB_DEV08B.LHA / gerlib / libg++ / etc / fib / fib.cc < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-12  |  13.5 KB  |  617 lines

  1. #ifdef amiga
  2. #include <use_standard_argc_argv.h>
  3. #endif
  4.  
  5. #include <stream.h>
  6. #include <Integer.h>
  7. #include <builtin.h>
  8.  
  9. // Fun and games with the Fibonacci function.
  10. // Also demonstrates use of the `named return value' extension.
  11.  
  12. // fib1 and fib2 from Doug Lea, others from Doug Schmidt.
  13.  
  14. /**********************************************************************/
  15.  
  16. // Standard iterative version:
  17.  
  18. // Schmidt uses convention that fib(0) = 1, not fib(0) = 0;
  19. // so I just increment n here.
  20.  
  21. Integer 
  22. fib1(int n)
  23. {
  24.   ++n;
  25.   if (n <= 0)
  26.     return 0;
  27.   else
  28.     {
  29.       Integer f = 1;
  30.       Integer prev = 0;
  31.  
  32.       while (n > 1)
  33.         {
  34.           Integer tmp = f;
  35.           f += prev;
  36.           prev = tmp;
  37.           --n;
  38.         }
  39.  
  40.       return f;
  41.     }
  42. }
  43.  
  44. /**********************************************************************/
  45.  
  46. //                                                       n
  47. // via transformed matrix multiplication  of     ( 0  1 )
  48. //                                               ( 1  1 )
  49. // simplified knowing that the matrix is always of 
  50. // the form  (a b)
  51. //           (b c)
  52. // (where (a, b) and (b, c) are conseq pairs of conseq fib's;
  53. // further simplified by realizing that c = a+b, so c is only
  54. // calculated implicitly.
  55. // Given all this, do the power via the standard russian
  56. // peasant divide-and-conquer algorithm, with
  57. // the current multiplier held in (p q)
  58. //                                (q -)
  59. //
  60. // This example also shows that using procedure calls instead of operators
  61. // can be faster for Integers
  62.  
  63.  
  64. // operator version
  65.  
  66. Integer 
  67. fib2(int n) 
  68. {
  69.   ++n;                          // 1-based for compatability; see above
  70.   Integer a = 0;
  71.   if (n <= 0)
  72.     return a;
  73.   else
  74.   {
  75.     Integer b = 1;
  76.     Integer p = 0;
  77.     Integer q = 1;
  78.     
  79.     for(;;)
  80.     {
  81.       if (n == 1)
  82.         return a * p + b * q;
  83.       else if (odd (n))
  84.       {
  85.         Integer aq = a * q;
  86.         Integer bq = b * q;
  87.         a = a * p + bq;
  88.         b = b * p  + aq + bq;
  89.       }
  90.       Integer qq = q * q;
  91.       q = (q * p) * 2L + qq;
  92.       p = (p * p) + qq;
  93.       n >>= 1;
  94.     }
  95.   }
  96. }
  97.  
  98. // with all expressions named, including return value
  99.  
  100. Integer 
  101. fib2a(int n)
  102. {
  103.   Integer a = 0;
  104.   ++n;
  105.   if (n <= 0)
  106.     return a;
  107.   else
  108.   {
  109.     Integer b = 1;
  110.     Integer p = 0;
  111.     Integer q = 1;
  112.     
  113.     for(;;)
  114.     {
  115.       if (n == 1)
  116.       {
  117.         Integer bq = b * q;
  118.         a *= p ;
  119.         a += bq;
  120.     return a;
  121.       }
  122.       else if (odd (n))
  123.       {
  124.         Integer aq = a * q;
  125.         Integer bq = b * q;
  126.         a *= p;
  127.         a += bq;
  128.         b *= p;
  129.         b += bq;
  130.         b += aq;
  131.       }
  132.       Integer qq = q * q;
  133.       Integer pp = p * p;
  134.       Integer pq = p * q;
  135.       q = pq;
  136.       q += pq;
  137.       q += qq;
  138.       p = pp;
  139.       p += qq;
  140.       n >>= 1;
  141.     }
  142.   }
  143. }
  144.  
  145. // procedure call version
  146.  
  147. Integer 
  148. fib2b(int n)
  149. {
  150.   Integer a = 0;
  151.   ++n; 
  152.  
  153.   if (n <= 0)
  154.     return a;
  155.   else
  156.   {
  157.     Integer b = 1;
  158.     Integer p = 0;
  159.     Integer q = 1;
  160.     
  161.     for(;;)
  162.     {
  163.       if (n == 1)
  164.       {
  165.         Integer bq; mul(b, q, bq);
  166.         mul(a, p, a);
  167.         add(a, bq, a);
  168.     return a;
  169.       }
  170.       else if (odd (n))
  171.       {
  172.         Integer aq; mul(a, q, aq);
  173.         Integer bq; mul(b, q, bq);
  174.         mul(a, p, a);
  175.         mul(b, p, b);
  176.         add(a, bq, a);
  177.         add(b, bq, b);
  178.         add(b, aq, b);
  179.       }
  180.       Integer qq; mul(q, q, qq);
  181.       Integer pp; mul(p, p, pp);
  182.       Integer pq; mul(p, q, pq);
  183.       add(pq, pq, q);
  184.       add(q, qq, q);
  185.       add(pp, qq, p);
  186.       n >>= 1;
  187.     }
  188.   }
  189. }
  190.  
  191. // procedure call version, reusing variables
  192.  
  193. Integer 
  194. fib2c(int n)
  195. {
  196.   Integer a = 0;
  197.   ++n; 
  198.  
  199.   if (n <= 0)
  200.     return a;
  201.   else
  202.   {
  203.     Integer b = 1;
  204.     Integer p = 0;
  205.     Integer q = 1;
  206.     Integer bq, aq, qq, pp, pq;
  207.     for(;;)
  208.     {
  209.       if (n == 1)
  210.       {
  211.         mul(b, q, bq);
  212.         mul(a, p, a);
  213.         add(a, bq, a);
  214.     return a;
  215.       }
  216.       else if (odd (n))
  217.       {
  218.         mul(a, q, aq);
  219.         mul(b, q, bq);
  220.         mul(a, p, a);
  221.         mul(b, p, b);
  222.         add(a, bq, a);
  223.         add(b, bq, b);
  224.         add(b, aq, b);
  225.       }
  226.       mul(q, q, qq);
  227.       mul(p, p, pp);
  228.       mul(p, q, pq);
  229.       add(pq, pq, q);
  230.       add(q, qq, q);
  231.       add(pp, qq, p);
  232.       n >>= 1;
  233.     }
  234.   }
  235. }
  236.  
  237. /**********************************************************************/
  238. // Ullman memoizers:
  239.  
  240. class Ullman_Array
  241. {
  242.   // Ullman's array implementation allows Initialization, Store, and Fetch
  243.   // in O(1) time.  Although it takes O(n) space the time to initialize enables
  244.   // us to compute a Fibonacci number in O(log n) time!
  245.   // The basic concept of Ullman's array implementation is the use of a 
  246.   // Hand_Shake_Array and an Index_Array.  An Index location in the array is 
  247.   // only considered initialized if the contents in the Hand_Shake_Array and
  248.   // Index_Array point to each other, i.e. they ``shake hands!''
  249.   
  250. private:
  251.   int       max_array_range;
  252.   int       max_set_size;
  253.   int      *index_array;
  254.   int      *hand_shake_array;
  255.   Integer  *storage_array;
  256.   int       current_max;
  257.   
  258. public:
  259.   Integer uninitialized;
  260.  
  261.   Ullman_Array (int array_range, int set_size);
  262.   void    store (int index, Integer &item);
  263.   Integer fetch (int index);
  264.  
  265. };
  266.  
  267. inline
  268. Ullman_Array::Ullman_Array (int array_range, int set_size)
  269. {
  270.   max_array_range  = array_range;
  271.   max_set_size     = set_size;
  272.   index_array      = new int[max_array_range + 1];
  273.   hand_shake_array = new int[max_set_size];
  274.   storage_array    = new Integer[max_set_size];
  275.   current_max      = -1;
  276.   uninitialized    = 0; // No fibonacci number has value of 0.
  277. }
  278.  
  279. // Store Item at the proper Index of the Ullman array.
  280. // The Hand_Shake_Array determines whether the Index has already
  281. // been stored into.  If it has NOT, then a new location for it
  282. // is set up in the Storage_Array and the Hand_Shake_Array and Index_Array 
  283. // are set to point at each other.
  284.  
  285. inline void
  286. Ullman_Array::store (int index, Integer &item)
  287. {
  288.   int  hand_shake_index = index_array[index];
  289.   
  290.   if (hand_shake_index > current_max || hand_shake_index < 0
  291.       || index != hand_shake_array[hand_shake_index])
  292.     {
  293.       hand_shake_index                   = ++current_max;
  294.       hand_shake_array[hand_shake_index] = index;
  295.       index_array[index]                 = hand_shake_index;
  296.     }
  297.   storage_array[hand_shake_index] = item;
  298. }
  299.  
  300. // Returns uninitialized if not initialized, else returns Item at Index.
  301.  
  302. inline Integer
  303. Ullman_Array::fetch(int index) 
  304. {
  305.   int hand_shake_index = index_array[index];
  306.   
  307.   if (hand_shake_index > current_max || hand_shake_index < 0
  308.       || index != hand_shake_array[hand_shake_index])
  309.     return uninitialized;
  310.   else 
  311.     return storage_array[hand_shake_index];
  312. }
  313.  
  314. /**********************************************************************/
  315.  
  316. class Memo_Fib : public Ullman_Array
  317. {
  318. public:
  319.   Memo_Fib (int fib_num);
  320.   Integer fib3 (int n);
  321.   Integer fib3a (int n);
  322. };
  323.  
  324.  
  325. // The total number of Items computed by the Fib routine is bounded by
  326. // 4 * ceiling of log base 2 of Fib_Num.  Of course, the basis of the
  327. // recurrence is Fib(0) == 1 and Fib(1) == 1!
  328.  
  329. Memo_Fib::Memo_Fib (int fib_num) : Ullman_Array(fib_num, (4 * lg (fib_num)))
  330. {
  331.   store (0, 1);
  332.   store (1, 1);
  333. }
  334.  
  335. // Uses the memoization technique to reduce the time complexity to calculate
  336. // the nth Fibonacci number in O(log n) time.  If the value of ``n'' is
  337. // already in the table we return it in O(1) time.  Otherwise, we use the
  338. // super-nifty divide-and-conquer algorithm to break the problem up into
  339. // 4 pieces of roughly size n/2 and solve them recursively.  Although this
  340. // looks like an O(n^2) recurrence the memoization reduces the number of
  341. // recursive calls to O(log n)!
  342.  
  343. Integer
  344. Memo_Fib::fib3 (int n)
  345. {
  346.   Integer fib = fetch(n);
  347.   if (fib == uninitialized)
  348.     {
  349.       int m = n >> 1;
  350.       
  351.       fib = fib3 (m) * fib3 (n - m) + fib3 (m - 1) * fib3 (n - m - 1);
  352.       store (n, fib);
  353.     }   
  354.   return fib;
  355. }
  356.  
  357. // The same, with procedure calls instead of operators
  358.  
  359. Integer
  360. Memo_Fib::fib3a (int n)
  361. {
  362.   Integer fib = fetch(n);
  363.   if (fib == uninitialized)
  364.     {
  365.       int m = n >> 1;
  366.       Integer tmp;
  367.       mul(fib3a(m), fib3a(n - m), fib);
  368.       mul(fib3a(m - 1), fib3a(n - m - 1), tmp);
  369.       add(fib, tmp, fib);
  370.       store (n, fib);
  371.     }   
  372.   return fib;
  373. }
  374.  
  375. /**********************************************************************/
  376.  
  377. // Here's a linear-time dynamic programming solution to the same problem. 
  378. // It makes use of G++/GCC dynamic arrays to build a table of ``n'' partial 
  379. // solutions.  Naturally, there is an O(1) space solution, but this O(n) 
  380. // approach is somewhat more intuitive and follows the ``original'' recurrence
  381. // more closely.
  382.  
  383. Integer 
  384. fib4 (int n)
  385. {
  386. #ifdef __GNUC__
  387.   Integer table[n + 1];
  388. #else
  389.   Integer *table = new Integer[n + 1];
  390. #endif
  391.   table[0] = 1;
  392.   table[1] = 1;
  393.  
  394.   for (int i = 2; i <= n; i++) 
  395.     table[i] = table[i - 1] + table[i - 2];
  396.   
  397. #ifdef __GNUC__
  398.   return table[n];
  399. #else
  400.   Integer tmp = table[n];
  401.   delete [] table;
  402.   return tmp;
  403. #endif
  404. }
  405.  
  406. /**********************************************************************/
  407.  
  408. // The extended integers provide numbers of the form:
  409. // (base+sqrt(5)*Rad_5)/2^Pow_2
  410. // These are used to find the solution to the closed form of fib(n). 
  411.  
  412. struct Extended_Int
  413. {
  414.   Integer base;
  415.   Integer rad_5;
  416.   int     pow_2;
  417.  
  418.   Extended_Int (Integer ¶m1, Integer ¶m2, int param3);
  419.   Extended_Int (Extended_Int& param);
  420.   Extended_Int (void) {}
  421.  
  422.   friend Extended_Int operator- (Extended_Int ¶m1, Extended_Int ¶m2);
  423.   friend Extended_Int operator* (Extended_Int ¶m1, Extended_Int ¶m2);
  424.   friend Extended_Int sqrt (Extended_Int ¶m1);
  425.   friend Extended_Int pow  (Extended_Int ¶m1, int num);
  426. };
  427.  
  428. inline
  429. Extended_Int::Extended_Int (Integer ¶m1, Integer ¶m2, int param3)
  430. {
  431.   base  = param1;
  432.   rad_5 = param2;
  433.   pow_2 = param3;
  434. }
  435.  
  436. inline
  437. Extended_Int::Extended_Int (Extended_Int ¶m)
  438. {
  439.   base  = param.base;
  440.   rad_5 = param.rad_5;
  441.   pow_2 = param.pow_2;
  442. }
  443.  
  444. inline Extended_Int 
  445. operator- (Extended_Int ¶m1, Extended_Int ¶m2)
  446. {
  447.   Extended_Int temp;
  448.   temp.base  = param1.base - param2.base;
  449.   temp.rad_5 = param1.rad_5 - param2.rad_5;
  450.   temp.pow_2 = param1.pow_2;
  451.   return temp;
  452. }
  453.  
  454. Extended_Int 
  455. operator* (Extended_Int ¶m1, Extended_Int ¶m2)
  456. {
  457.   Extended_Int temp;
  458.   temp.base  = param1.base * param2.base + 5L * param1.rad_5 * param2.rad_5;
  459.   temp.rad_5 = param1.base * param2.rad_5 + param1.rad_5 * param2.base;
  460.   temp.pow_2 = param1.pow_2 + param2.pow_2;
  461.  
  462.   while (temp.pow_2 > 0 && !(odd (temp.base) || odd (temp.rad_5)))
  463.     {
  464.       temp.base >>= 1;
  465.       temp.rad_5 >>= 1;
  466.       temp.pow_2--;
  467.     }
  468.  
  469.   return temp;
  470. }
  471.  
  472. inline Extended_Int 
  473. sqrt (Extended_Int ¶m1)
  474. {
  475.   return param1 * param1;
  476. }
  477.  
  478. Extended_Int 
  479. pow (Extended_Int ¶m1, int num) 
  480. {
  481.   if (num > 1)
  482.     return odd (num)
  483.       ? param1 * sqrt (pow (param1, num >> 1)) : sqrt (pow (param1, num >> 1));
  484.   else 
  485.     return param1;
  486. }
  487.  
  488. /**********************************************************************/
  489.  
  490. // Calculates fib (n) by solving the closed form of the recurrence. 
  491.  
  492. class Closed_Form : private Extended_Int
  493. {
  494. private:
  495.   Extended_Int cons1;
  496.   Extended_Int cons2;
  497.  
  498. public:
  499.   Closed_Form (void): cons1 (1, 1, 1), cons2 (1, -1, 1) {}
  500.   Integer fib5 (int n);
  501. };
  502.  
  503. Integer
  504. Closed_Form::fib5 (int num) 
  505. {
  506.   Extended_Int temp = pow (cons1, num + 1) - pow (cons2, num + 1);
  507.   
  508.   while (temp.pow_2 > 0 && !(odd (temp.base) || odd (temp.rad_5)))
  509.     {
  510.       temp.base  >>= 1;
  511.       temp.rad_5 >>= 1;
  512.       temp.pow_2--;
  513.     }
  514.   
  515.   return temp.rad_5;
  516. }
  517.  
  518. /**********************************************************************/
  519.  
  520. static const int DEFAULT_SIZE = 10000;
  521.  
  522.  
  523. void dofib1(int fib_num)
  524. {
  525.   start_timer ();
  526.   Integer result = fib1 (fib_num);
  527.   double time = return_elapsed_time(0.0);
  528.   cout << "fib1 = " << result << ". Time = " << time << "\n";
  529. }
  530.  
  531. void dofib2(int fib_num)
  532. {
  533.   start_timer ();
  534.   Integer result = fib2 (fib_num);
  535.   double time = return_elapsed_time(0.0);
  536.   cout << "fib2 = " << result << ". Time = " << time << "\n";
  537. }
  538.  
  539. void dofib2a(int fib_num)
  540. {
  541.   start_timer ();
  542.   Integer result = fib2a(fib_num);
  543.   double time = return_elapsed_time(0.0);
  544.   cout << "fib2a = " << result << ". Time = " << time << "\n";
  545. }
  546.  
  547. void dofib2b(int fib_num)
  548. {
  549.   start_timer ();
  550.   Integer result = fib2b(fib_num);
  551.   double time = return_elapsed_time(0.0);
  552.   cout << "fib2b = " << result << ". Time = " << time << "\n";
  553. }
  554.  
  555. void dofib2c(int fib_num)
  556. {
  557.   start_timer ();
  558.   Integer result = fib2c(fib_num);
  559.   double time = return_elapsed_time(0.0);
  560.   cout << "fib2c = " << result << ". Time = " << time << "\n";
  561. }
  562.  
  563. void dofib3(int fib_num)
  564. {
  565.   start_timer ();
  566.   Memo_Fib    Memo_Test (fib_num);
  567.   Integer result = Memo_Test.fib3 (fib_num);
  568.   double time = return_elapsed_time(0.0);
  569.   cout << "fib3 = " << result << ". Time = " << time << "\n";
  570. }
  571.  
  572. void dofib3a(int fib_num)
  573. {
  574.   start_timer ();
  575.   Memo_Fib    Memo_Test (fib_num);
  576.   Integer result = Memo_Test.fib3a (fib_num);
  577.   double time = return_elapsed_time(0.0);
  578.   cout << "fib3a = " << result << ". Time = " << time << "\n";
  579. }
  580.  
  581. void dofib4(int fib_num)
  582. {
  583.   start_timer ();
  584.   Integer result = fib4 (fib_num);
  585.   double time = return_elapsed_time(0.0);
  586.   cout << "fib4 = " << result << ". Time = " << time << "\n";
  587. }
  588.  
  589. void dofib5(int fib_num)
  590. {
  591.   Closed_Form Rec_Test;
  592.   Integer result = Rec_Test.fib5 (fib_num);
  593.   double time = return_elapsed_time(0.0);
  594.   cout << "fib5 = " << result << ". Time = " << time << "\n";
  595. }
  596.  
  597.  
  598. int
  599. main (int argc, char *argv[])
  600. {
  601.   int         fib_num = (argc > 1) ? atoi (argv[1]) : DEFAULT_SIZE;
  602.  
  603.   cout << "Results for fib(" << fib_num << "):\n\n";
  604.  
  605.   dofib1(fib_num);
  606.   dofib2(fib_num);
  607.   dofib2a(fib_num);
  608.   dofib2b(fib_num);
  609.   dofib2c(fib_num);
  610.   dofib3(fib_num);
  611.   dofib3a(fib_num);
  612. //  dofib4(fib_num);  // This uses too much mem for large n!
  613.   dofib5(fib_num);
  614.   return 0;
  615. }
  616.  
  617.