Program avoids repetitive computation by computing the sequence linearly form the bottom up: F(0)
through F(n)
. This implementation also overcomes the limitations of a 32-bit integer in C, overflow is avoided by representing integers as arrays of individual digits.
Calculating Fibonacci numbers with the most straightforward recursive implementation of the function is prohibitively slow, as there is a lot of repetitive computation:
int fib(int n) {
// base cases: F(0) = 0, F(1) = 1
if (n < 2)
return n;
// definition of Fibonacci: F(n) = F(n – 1) + F(n - 2)
return fib(n – 1) + fib(n – 2);
}
This recursive function sports an exponential runtime. Instead the approach is to achieve linear runtime by building from base cases, F(0) = 0
and F(1) = 1
, toward the desired result, F(n)
. Thus avoid the expensive and exponentially EXplOsIVe recursive function calls.
The linear Fibonacci function has a big problem, though, which is perhaps less obvious than the
original runtime issue: when computing the sequence, we quickly exceed the limits of C’s 32-bit
integer representation. On most modern systems, the maximum int value in C is 232-1, or
2,147,483,647. The first Fibonacci number to exceed that limit is F(47) = 2,971,215,073
.
Even C’s 64-bit unsigned long long int
type is only guaranteed to represent non-negative integers
up to and including 18,446,744,073,709,551,615 (which is 264-1).The Fibonacci number F(93) = 12,200,160,415,121,876,738
which can be stored as an unsigned long long int
. However, F(94) = 19,740,274,219,868,223,167
which is too big to store in any of C’s extended integer data types.
To overcome this limitation, we will represent integers in this program using arrays, where each index holds a single digit of an integer.
For reasons that will soon become apparent, we will store our integers in reverse order in these arrays. So, for example, the numbers 2,147,483,648 and 10,0087 would be represented as:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
---|---|---|---|---|---|---|---|---|---|---|
[a] | 8 | 4 | 6 | 3 | 8 | 4 | 7 | 4 | 1 | 2 |
[b] | 7 | 8 | 0 | 0 | 0 | 1 |
Storing these integers in reverse order makes it really easy to add two of them together. The ones digits for both integers are stored at index [0] in their respective arrays, the tens digits are at index [1], the hundreds digits are at index [2], and so on.
So, to add these two numbers together, we add the values at index [0] (8 + 7 = 15), throw down the 5 at index [0] in some new array where we want to store the sum, carry the 1, add it to the values at index [1] in our arrays (1 + 4 + 8 = 13), and so on
In this program, we will use this array representation for integers. The arrays will be allocated dynamically, and we will stuff each array inside a struct that also keeps track of the array’s length:
typedef struct HugeInteger
{
// a dynamically allocated array to hold the digits
// of a huge integer, stored in reverse order
int *digits;
// the number of digits in the huge integer (which is
// approximately equal to the length of the array)
int length;
} HugeInteger;
unsigned int n;
Because an unsigned int
is typically 32 bits (like the normal int
data type), but doesn’t need to use
any of those bits to signify a sign, it can make out a higher maximum positive integer value than a normal
int
.
It is necessary to know what the maximum value is that can be represented using an unsigned int
on the system where the program is running. That value is defined in the system’s limits.h
file, which was #include
d from the Fibonacci.c
source
file, like so:
#include <limits.h>
limits.h
defines a value called UINT_MAX
, which is the maximum value an unsigned int
can hold. It
also defines INT_MAX
(the maximum value an int
can hold), UINT_MIN
, INT_MIN
, and many others.
Note that (UINT_MAX + 1)
necessarily causes integer overflow, but since an unsigned int can’t be negative, (UINT_MAX + 1)
just wraps back around to zero.
HugeInteger *hugeAdd(HugeInteger *p, HugeInteger *q);
- Description: Return a pointer to a new, dynamically allocated
HugeInteger struct
that contains the result of adding the huge integers represented byp
andq
. - Notes: If a
NULL
pointer is passed to this function, it simplyreturns NULL
. If any dynamic memory allocation functions fail within this function, alsoreturns NULL
, consideration to avoid memory leaks when you doing so needs to be taken. - Returns A pointer to the newly allocated
HugeInteger struct
, orNULL
in the special cases mentioned above.
HugeInteger *parseString(char *str);
- Description: Convert a number from string format to
HugeInteger
format. - Notes: : If the empty string (“”) is passed to this function, treat it as a zero (“0”). If any
dynamic memory allocation functions fail within this function, or if
str
isNULL
,return NULL
, again trying to avoid memory leaks when doing so. It is assumed the string will only contain ASCII digits ‘0’ through ‘9’, and that there will be no leading zeros in the string. - Returns: A pointer to the newly allocated
HugeInteger struct
, orNULL
if dynamic memory allocation fails or ifstr
isNULL
.
HugeInteger *parseInt(unsigned int n)
- Description Convert the
unsigned integer
n
toHugeInteger
format. - Notes: If any dynamic memory allocation functions fail within this function,
return NULL
, consider avoiding memory leaks when doing so. - Returns A pointer to the newly allocated
HugeInteger struct
, orNULL
if dynamic memory allocation fails at any point.
unsigned int *toUnsignedInt(HugeInteger *p);
- Description: Convert the
integer
represented byp
to a dynamically allocatedunsigned int
, and return a pointer to that value. Ifp
isNULL
, simplyreturn NULL
. If the integer represented byp
exceeds the maximum unsignedint
value defined inlimits.h
,return NULL
. - Note: The sole reason this function returns a pointer instead of an
unsigned int
is so that we canreturn NULL
to signify failure in cases wherep
cannot be represented as anunsigned int
. - Returns: A pointer to the dynamically allocated
unsigned integer
, orNULL
if the value cannot be represented as anunsigned integer
(including the case wherep
isNULL
).
HugeInteger *fib(int n);
- Description This is the Fibonacci function; this is where the magic happens. Implementation of an
iterative solution that runs in
O(nk)
time and returns a pointer to aHugeInteger struct
that containsF(n)
. (See runtime note below.) Making sure to prevent memory leaks before returning from this function. - Runtime Consideration: In the
O(nk)
runtime restriction,n
is the parameter passed to the function, andk
is the number of digits inF(n)
. So, within this function, you can make a total ofn
calls to a function that isO(k)
(or faster). - Space Consideration: When computing
F(n)
for largen
, it’s important to keep as few Fibonacci numbers in memory as necessary at any given time. For example, in building up toF(10000)
, you won’t want to hold Fibonacci numbersF(0)
throughF(9999)
in memory all at once. Find a way to have only a few Fibonacci numbers in memory at any given time over the course of a single call tofib()
. - Notes: You may assume that
n
is a non-negative integer. If any dynamic memory allocation functions fail within this function,return NULL
, noting to avoid memory leaks when doing so. - Returns: A pointer to a
HugeInteger
representingF(n)
, orNULL
if dynamic memory allocation fails.
You must have the gcc compiler installed on your machine.
To compile the source file (.c files) at the command line:
gcc main.c
By default, this will produce an executable file called a.out, which can ran by typing:
./a.out
This will produce an a.exe file, which can be ran by typing:
a.exe
If you want to name the executable file something else, use:
gcc Fibonacci.c testcase01.c -o fib.exe
...and then run the program using:
./fib.exe
...and then run the program using:
fib.exe
Running the program could potentially dump a lot of output to the screen. If you want to redirect your
output to a text file in Linux, it’s easy. Just run the program using the following command, which will
create a file called whatever.txt
that contains the output from your program:
./fib.exe > whatever.txt
When the program begins, it will ask for the number of fibs to calculate. It then will proceed to linearly reach the nth
fibonacci number.