繁体   English   中英

使用两个线程计算斐波那契数列。

[英]Calculate the Fibonacci sequence using two threads.

我有以下代码:

#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));
    }
}

如何使2个线程分别计算和打印斐波那契数列的偶数和奇数索引号?

这是在Builder 6中进行的练习。

您可以使用仅使用偶数或奇数元素的斐波纳契数公式。 要开发此公式:

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]

现在,每个线程都可以使用[6]计算偶数或奇数斐波那契数,而无需等待其他线程的结果。

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);
    }
}

但是,这可能会给您带来麻烦,使事情比原本应该的简单得多。

有多种方法可以实现此目的。 Anatolyg的回答很好。 这确实是解决该问题的理想方法。

但是,就计算效率而言,计算斐波那契数列的方式存在很大问题。


注意:您可以在此处更改几乎所有算法,以便仅计算偶数或几率。 这些只是计算斐波纳契数的更好方法。

您当前计算数字的方式不是很有效。 使用当前算法计算数字之一的算法的时间复杂度已经是O(2 ^ n),甚至不计算循环中的重复调用。 一个用于计算斐波纳契数的小程序不必只花一分钟的时间就计算整个cpu,只需计算前100个数即可。

更好的递归算法如下(从此页面开始 ):

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);
}

现在,我们只能在O(n)中而不是O(2 ^ n)中计算fib(n)的值。

这是另一种效率更高的O(n)算法:

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;
}


但是,在您的上下文中,如果将其用作函数,则还有另一个问题,那就是Schlemiel画家算法 这是不好的。 由于每次都要计算所有数字,因此每个数字取O(n),然后调用n次,总时间复杂度为O(n ^ 2)。 这仍然比您以前拥有的O(2 ^ n)好得多,但是仍然不好。

我们可以做得更好。

#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);
}

这是一个使用Binet公式直接计算结果的方法,而不是顺序计算结果的方法 ,因此从多个线程运行该方法特别容易。

假设目标计算机可以在O(1)中计算长双指数,则输出所有斐波那契数的算法仅为O(n)。 但是,许多体系结构都无法做到,因此,您最多可能最终以O(n)表示函数,以O(n ^ 2)表示整个程序。

我们可以做得更好。

#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;
    }
}

(可能无法100%正常工作,我已经有几年没有写过这么多的c ++了)

由于您是一个接一个地计算它们,因此无需每次都重新计算所有内容。 当您知道前两个斐波那契数时,计算下一个数仅为O(1)。 该代码实际上是针对Iterator类型的,它将在斐波那契数字序列上进行迭代。 这也可以只是一个简单的函数而实现,但是这种方式更加方便,因为您可以使用它仅用一个调用就可以填充整个列表。

如果仅将此作为for循环的一部分,则可以仅在O(n)中计算所需的所有数字。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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