/** File "FibonacciTimes.cpp", by KWR for CSE250, Fall 2009 Illustrates difference between exponential time and linear time, and: () command-line arguments in C++ () getting system time for blocks of code () using static variables to count function invocations and iterations () code set up for separate compilation with a makefile () conditional compilation, with a user-defined -Dlabel makefile macro () a third-party C++ "bigint" class (by W. Rossi and A. Vinokur, under GPL) () the standard-library "pair" class, in **MUST BE COMPILED WITH -lrt OPTION!!, linking in the real-time library** I.e. g++ -o fib FibonacciTimes.cpp -lrt Usage: ./fib n [base] Outputs the nth Fibonacci number F_n, starting with F_0 = 0, F_1 = 1, in base "base" notation (default=10). The argument n must be base-10. Also outputs user-selected timing data. */ #include #include #include // for setprecision, fixed #include // for pair #include "bigint.h" // copyright (c) by Rossi and Vinokur, under GPL #include "bigint.cpp" //just testing before Makefile #define BigInt VinBigInt // we'll use the VinBigInt class from this file. #include "HiResTimer.h" // code is all in the .h file, inside class braces using namespace std; using std::cout; using std::cerr; using std::istringstream; using std::setprecision; using std::fixed; /** Typedefs are better style than macros in C++. The order is reversed---the replacement name comes first. Note that if we also commented-in the above #define'd macro, this would now textually read "typedef RossiVinBigInt VinBigInt;" and probably cause errors. */ //typedef RossiBigInt BigInt; int countDF = 0; int countSF = 0; /** Output F_n by executing the standard mathematical definition recursively, resulting in an exp(n) runtime and maybe system stack overflow! Base cases: n=0, n=1 */ BigInt dumbFib(const int n) { if (n <= 1) { return BigInt(n); } else { countDF += 2; return( dumbFib(n-1) + dumbFib(n-2) ); } } /** Output the pair (F_n, F_{n-1}). The F_{n-1} is more than you asked for, but the extra work really helps the recursion! REQUIRES n >= 1, because F_{-1} should be regarded as garbage! Hence n = 2 must also become a base case. */ pair smartFibHelper(const int n) { if (n <= 2) { return pair(BigInt((3*n - n*n)/2), BigInt(n-1)); } else { countSF++; pair pf23 = smartFibHelper(n-2); // F_{n-2} = pf23.first, F_{n-3} = pf23.second. Main point is that // F_n = 2*F_{n-2} + F_{n-3}, while F_{n-1} = F_{n-2} + F_{n-3} of course // return pair(BigInt(2)*(pf23.first) + pf23.second, return pair((pf23.first)*2 + pf23.second, pf23.first + pf23.second); } } //may give warning about not ending with a return statement BigInt smartFib(const int n) { return (smartFibHelper(n).first); } /** Fill in an array, with no recursion! Takes O(n) time. */ BigInt smarterFib(const int n) { vector* fibs = new vector(n+1); //indices 0..n fibs->at(0) = BigInt(0); //here we are using value, not pointer fibs->at(1) = BigInt(1); for (int i = 2; i <= n; i++) { fibs->at(i) = fibs->at(i-2) + fibs->at(i-1); } //return fibs->at(n); //natural, but causes memory leak const BigInt preserveMe = fibs->at(n); //can do with reference & ? delete(fibs); return preserveMe; } int main(int argc, char** argv) { // First convert args from C-strings to proper C++ STL strings, // using the same kind of string-array "args" Java uses from the get-go. // Note that it is OK to assign a C-string to an STL string. // One could say (char*)[] in place of char**, but latter is traditional // NOTE: argc *includes* the name of the executable! so always >= 1 vector* args = new vector(argc); for (int i = 0; i < argc; i++) { args->at(i) = *(argv + i); //equivalent to argv[i] } //Now the args are strings. We want integers, n may be a big integer. //Annoyingly, C++ does not have a standard string-to-int conversion. //We could import the "atoi" function from the C library, but that would //require us to use the string::c_str() method to convert the string //back to a char*, which we avoid like the plague. Here's the C++ way: istringstream INS; int n; int base = 10; //default if (argc >= 3) { //change it INS.str(args->at(2)); //safe since argc >= 2 if (INS >> base) { //OK } else { cerr << "Second argument given but not a valid integer" << endl; return(1); } } if (argc >= 2) { //note that this is *not* an "else" of the first "if" INS.str(args->at(1)); if (!(INS >> n)) { cerr << "First argument given but not a valid integer" << endl; return(1); } } else { //argc == 1, i.e. no arguments given cerr << "Usage: fib n [base] with n >= 0" << endl; return(1); } cout << "Clock ticks per second: " << sysconf(_SC_CLK_TCK) << endl; cout << "Clocks per second via sysconf: " << sysconf(CLOCKS_PER_SEC) << endl; cout << "Clocks per second without it : " << CLOCKS_PER_SEC << endl; double t = 0.0; HiResTimer* timer = new HiResTimer(); if (n <= 30) { BigInt dfn = dumbFib(n); t = timer->elapsedTime(); } else { cout << "Now allowing 'dumb' call on n <= 30 only, sorry." << endl; } cout << fixed << setprecision(3) << endl; cout <<"Time for double-branch recursion for fib(" << n << "), in " << timer->getUnits() << ": "<< t << endl; timer->reset(); //BigInt sfn = smartFib(n); BigInt ssfn = smarterFib(n); //Dr. Ngo's notes give same thing. t = timer->elapsedTime(); cout << "Time for single-branch recursion for fib(" << n << "), in " << timer->getUnits() << ": " << t << endl; cout << "F_" << n << " = " << ssfn << endl; cout << "Count of 'dumb calls: " << countDF << endl; cout << "Count of smart calls: " << countSF << endl; delete(timer); return(0); }