简体   繁体   中英

Calculate the Fibonacci sequence using two threads.

I have this code:

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"

#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { }

int Fibonacci(int nNumber) {
    if (nNumber == 0)
        return 0;
    if (nNumber == 1)
        return 1;

    return Fibonacci(nNumber-1) + Fibonacci(nNumber-2);
}

void __fastcall TForm1::Button1Click(TObject *Sender) {
    int k=0;
    int val;

    k = StrToInt(Edit1->Text);

    for (int i=0; i < k; i++) {
        val =  Fibonacci(i);
        Form1->ListBox1->Items->Add("F"+IntToStr(i)+"-->"+IntToStr(val));
    }
}

How can I make 2 threads which will compute and print the even and odd indexed numbers of the fibonacci sequence, respectively?

This is for an exercise in Builder 6.

You can use a formula for Fibonacci numbers that uses only the even or odd elements. To develop this formula:

f(n) = f(n-1) + f(n-2) [1]
f(n-1) = f(n-2) + f(n-3) [2]
f(n-2) = f(n-3) + f(n-4) [3]

Combining [1] and [2]:
f(n) = 2 * f(n-2) + f(n-3) [4]

Rearranging [3]:
f(n-3) = f(n-2) - f(n-4) [5]

Combining [4] and [5]:
f(n) = 3 * f(n-2) - f(n-4) [6]

Now each thread can calculate even or odd Fibonacci numbers using [6], without waiting for other thread's results.

int Fibonacci(int nNumber) {
    switch (nNumber)
    {
    case 0: return 0;
    case 1: return 1;
    case 2: return 1;
    case 3: return 2;
    default: return 3 * Fibonacci(nNumber-2) - Fibonacci(nNumber-4);
    }
}

However, this may ruin the exercise for you, making things much simpler than they are supposed to be.

There are a number of ways to accomplish this. Anatolyg's answer is excellent; that is really the ideal way of solving that problem.

However, there is a big problem with the way you are computing the Fibonacci sequence, in terms of computational efficency.


Note: you can change nearly every algorithm here so that it will compute just evens or odds. These are just better ways of computing fibonacci numbers.

The way you are currently computing the numbers is not very efficent. The time complexity of the algorithm for computing just one of the numbers using your current algorithm is already O(2^n), not even counting the repeated calls in the loop. A little program for computing fibonacci numbers shouldn't need to hog the whole cpu for a minute just to compute the first 100 numbers.

A better recursive algorithm is as follows (from this page ):

unsigned long fib(unsigned int n) {
    return n == 0 ? 0 : fib2(n, 0, 1);
}
unsigned long fib2(unsigned int n, unsigned long p0, unsigned long p1) {
    return n == 1 ? p1 : fib2(n - 1, p1, p0 + p1);
}

We can now compute the value of fib(n) in only O(n), rather than O(2^n).

Here is another, slightly more efficient O(n) algorithm:

unsigned long fib(unsigned int n) {
    unsigned long a=0,b=1; 
    for(unsigned long i = 0; i<n; i++)
        a=(b+=a)-a; //there are a number of other variations on this statement,
                    //but that's not the point.
    return a;
}


However, in your context, if you use that as your function, it still has another problem that it is a Schlemiel the Painter's Algorithm . That is not good. Since it has to compute all the numbers, each time, you are taking O(n) per number, and you are then calling it n times, for a total time complexity of O(n^2). That's still much better than the O(2^n) you had before, but it's still not good.

We can do better than that.

#INCLUDE <cmath>

#DEFINE (phi) (1.618033988749894848204586834365638117720309179805762862135448L)
#DEFINE (phiC) (–0.618033988749894848204586834365638117720309179805762862135448L)
#DEFINE (root5) (2.236067977499789696409173668731276235440618359611525724270897L)
//using long doubles to minimize the truncation errors,
//as fibonacci numbers can get extremely large

unsigned long fib(unsigned int n) {
    return lrint(((pow(phi,n)-pow(phiC,n))/root5);
}

This one uses Binet's formula to calculate the result directly, rather than computing them sequentially, so this one would be particularily easy to run from multiple threads.

Assuming that the target machine can compute long double exponents in O(1), your algorithm for outputting all the fibonacci numbers is only O(n). Many architectures can't, however, so you will most likely end up with, at the best, O(n) for the function, O(n^2) for the whole program.

We can do better than that.

#INCLUDE <iterator>

class FibIterator: public std::Iterator<const unsigned long>{
    unsigned long a=0,b=1;
public:
    std::Iterator& operator++() {
        unsigned long c = b;
        b+=a;
        a=c;
        return(*this);
    }
    std::Iterator& operator--() {
        unsigned long c = a;
        a=b-a;
        b=c;
        return(*this);
    }
    const unsigned long& operator*(){
        return a;
    }
}

(Might not work 100%, I haven't coded this much c++ in one sitting in years)

Since you are computing them one-after-another, there is no need to recompute everything each time. When you know the previous two fibonacci numbers, computing the next number is only O(1). This code is actually for an Iterator type that will iterate over the sequence of fibonacci numbers. This could also be implemented as just a simple function, but this way is more convenient, because you can use it to fill an entire list with only one call.

If you just use this as part of your for loop, you can compute all the numbers you want in only O(n).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM