面试题整理-斐波那契数列

这个很好。其实也就是f(0) = 0, f(1) = 1. 然后 f(n) = f(n-1) +  f(n-2);

测试链接点击打开链接求解:

#include <stdio.h>  
#include <stdlib.h>  
  
long long a[71];  
void init(void) {  
    a[0] = 0;  
    a[1] = a[2] = 1;  
    for (int i = 3; i < 71; ++i) {  
        a[i] = a[i-1] + a[i-2];  
    }  
}  
  
int main(void) {  
    int n;init();  
    while (scanf("%d", &n) != EOF) {  
        printf("%lld\n", a[n]);  
    }  
    return 0;  
}  
//注意打表及长整型。  

解题

首先,不要用递归的方法。那个是很烂的解法。直接忽略。这里也不用打表的方法。也就是说,每给一个数。直接进行计算从而得到结果。

你可能会想到下面的这个方法:

int fib(int n) {  
    if (0 == n) return 0;  
    if (1 == n || 2 == n) return 1;  
    int a = 1, b = 1, ret = 0;  
    for (int i = 3; i <= n; ++i) {  
        ret = a + b;  
        b = a;  
        a = ret;  
    }  
    return ret;  
}  

但是这种方法,有个很明显的缺点。也就是容易溢出。32位只能计算至45。64位只能计算至92。所以当数值一大,不管是用long或者long long也好。都不能解决。

 

大整数斐波那契数列-推荐使用,简单有效

那么首先想到的是利用大整数来进行求解。这里我们用string来摸拟大整数。

#include <iostream>  
#include <string>  
#include <stdio.h>  
#include <stdlib.h>  
using namespace std;  
  
string &_string_add_string(const string &a, const string &b, string &res)  
{  
    int sum_value = 0, add_bit = 0;  
    const int alen = a.length(), blen = b.length();  
    res = "0" + (alen > blen ? a : b);  
    for (int i = alen-1, j = blen-1, k = res.length() - 1;   
         i >= 0 || j >= 0 || add_bit > 0;   
         --i, --j, --k){  
        sum_value = (i>=0 ? a[i]-48: 0) + (j>=0 ? b[j]-48: 0) + add_bit;  
        add_bit = sum_value / 10;   
        res[k] = sum_value%10 + '0';  
    }  
    if (res[0] == '0') res = res.substr(1, res.length() - 1);  
    return res;  
}  
  
string fib(int n) {  
    if (0 == n) return "0";  
    if (1 == n || 2 == n) return "1";  
    string a = "1", b = "1", ret = "0";  
    for (int i = 3; i <= n; ++i) {  
        _string_add_string(a, b, ret);  
        b = a;  
        a = ret;  
    }  
    return ret;  
}  
  
int main(void)  
{  
    int n;  
    string a, b, c;  
    string ret;  
    while (scanf("%d", &n) != EOF) {  
        printf("%s\n", fib(n).c_str());  
    }  
    return 0;  
}  

现在得到一个大整数的解法。也就是对于任意大的数。都可以得到解。现在希望对这个进行改进。大数的加法,测试地址。

lg(n)的斐波那契数列

首先是效率问题,当n特别大的时候。求解起来,很不方便。于是我们想怎么去改进这个效率。

比如F(n) = F(n - 1) + F(n-2);

从而可以得到矩阵公式:

[F(n) F(n-1)] = [F(n-1) F(n-2)] * A;

A矩阵等于

1 1

1 0

不用多说,把公式展开。也就是[ F(n) F(n-1)] = [F(1) F(0)] * pow(A, n-1);

而我们知道[ F(1) F(0)] = [ 1 0]; 也就是说F(n) = pow(A, n - 1) [0][0];

也就是矩阵pow(A, n-1) 最左上角的第一个元素。

实际,也就是把O(n)的累加问题变成了pow()指数问题。

这里写一个高效的pow()模板。是O(log2n)。

template<typename T>  
T pow(T a, int n)  
{  
    T odd = 1;  
    while (n)  
    {  
        if (n&1) odd *= a;  
        a *= a;  
        n>>=1;  
    }  
    return odd;  
}  
根据这个模板,我们只需要重写一个矩阵的乘法就可以了。不要去写一个矩阵类啊。

#include <stdio.h>  
#include <stdlib.h>  
  
typedef struct _node  
{  
    long long a, b;  
    long long c, d;  
} node;  
  
  
void _multiple(node *x, node *y) {  
    node temp;  
    temp.a = x->a*y->a + x->b*y->c;  
    temp.b = x->a*y->b + x->b*y->d;  
    temp.c = x->c*y->a + x->d*y->c;  
    temp.d = x->c*y->b + x->d*y->d;  
    *x = temp;  
}  
  
long long fib(int n)  
{  
    if (0 == n) return 0;  
    if (1 == n || 2 == n) return 1;  
  
    node odd; odd.a = odd.d = 1; odd.c = odd.b = 0; //单位矩阵  
    node temp; temp.a = temp.b = temp.c = 1; temp.d = 0; // A矩阵  
  
    --n;  
    while (n) {  
        if (n&1) _multiple(&odd, &temp);  
        _multiple(&temp, &temp);  
        n >>= 1;  
    }  
  
    return odd.a;  
}  
  
int main()  
{  
    int n;  
    while (scanf("%d", &n) != EOF) {  
        printf("%lld\n", fib(n));  
    }  
    return 0;  
}

结构很清晰。但是还是有前面提出来的问题。也就是溢出问题。C++没有大数。算了,还是自己用string模拟一下吧。

大数的乘法

我们只需要处理的是大数的乘法。而大数的乘法是依赖于大数的加法的。所以相当于加法与乘法都要用到。

那么先来个大数的乘法吧。大数乘法

#include <iostream>  
#include <string>  
using namespace std;  
  
string &_del_zeros_before_dot(string &a)  
{  
    if (a.length() <= 0 || a[0] != '0') return a;  
    int i = 0;  
    while (i < a.length() && a[i] == '0') ++i;  
    a = a.substr(i, a.length() - i);  
    return a;  
}  
  
string &_string_add_string(const string &a, const string &b, string &res)  
{  
    int sum_value = 0, add_bit = 0;  
    const int alen = a.length(), blen = b.length();  
    res = "0" + (alen > blen ? a : b);  
    for (int i = alen-1, j = blen-1, k = res.length() - 1;   
         i >= 0 || j >= 0 || add_bit > 0;   
         --i, --j, --k){  
        sum_value = (i>=0 ? a[i]-48: 0) + (j>=0 ? b[j]-48: 0) + add_bit;  
        add_bit = sum_value / 10;   
        res[k] = sum_value%10 + '0';  
    }  
    if (res[0] == '0') res = res.substr(1, res.length() - 1);  
    return res;  
}  
  
  
string &_gen_zeros_string(int n, string &res) {  
    string temp = "0";  
    res = "";  
    while (n) {  
        if (n&1) res += temp;  
        temp += temp;  
        n >>= 1;  
    }  
    return res;  
}  
  
string &_string_multiply_char(  
    const string &a,   
    char c, int n_zeros, string &res) {  
  
    int ch = c - '0';  
    string zeros_string;  
    _gen_zeros_string(n_zeros, zeros_string);  
    res = "0" + a + zeros_string;  
      
    const int alen = a.length();  
    for (int i = alen - 1, k = alen, add_bit = 0;   
        i >= 0 || add_bit > 0; --i, --k) {  
        int v = (i>=0 ? a[i]-48: 0) * ch + add_bit;  
        add_bit = v / 10;  
        res[k] = v % 10 + '0';  
    }  
    if (res[0] == '0') res = res.substr(1, res.length() - 1);  
  
    return res;  
}  
  
string &_string_multiply_string(const string &a, const string &b, string &res) {  
    string c = a, d = b;  
    _del_zeros_before_dot(c);  
    _del_zeros_before_dot(d);  
    int clen = c.length(), dlen = d.length();  
  
    if (clen < dlen) {   
        string t = c; c = d; d = t;  
        int x = clen; clen = dlen; dlen = x;  
    }  
  
    res = "0";  
    for (int i = dlen - 1; i >= 0; --i) {  
        string temp_res;  
        _string_multiply_char(c, d[i], (dlen - 1 - i), temp_res);  
  
        string add_res;  
        _string_add_string(res, temp_res, add_res);  
        res = add_res;  
    }  
    return res;  
}  
  
int main(void)  
{  
    string a, b, c;  
    while (cin >> a >> b) {  
        _string_multiply_string(a, b, c);  
        cout << c << endl;  
    }  
    return 0;  
} 

大数乘法搞定之后,应该把大数引入至O(lgN)中去。

 带大数的斐波那契数--做研究,不是很推荐

 这里把大数模板代入到O(lgN)中去。

#include <iostream>  
#include <string>  
#include <stdio.h>  
#include <stdlib.h>  
using namespace std;  
  
string &_del_zeros_before_dot(string &a)  
{  
    if (a.length() <= 0 || a[0] != '0') return a;  
    int i = 0;  
    while (i < a.length() && a[i] == '0') ++i;  
    a = a.substr(i, a.length() - i);  
    return a;  
}  
  
string &_string_add_string(const string &a, const string &b, string &res)  
{  
    int sum_value = 0, add_bit = 0;  
    const int alen = a.length(), blen = b.length();  
    res = "0" + (alen > blen ? a : b);  
    for (int i = alen-1, j = blen-1, k = res.length() - 1;   
         i >= 0 || j >= 0 || add_bit > 0;   
         --i, --j, --k){  
        sum_value = (i>=0 ? a[i]-48: 0) + (j>=0 ? b[j]-48: 0) + add_bit;  
        add_bit = sum_value / 10;   
        res[k] = sum_value%10 + '0';  
    }  
    if (res[0] == '0') res = res.substr(1, res.length() - 1);  
    return res;  
}  
  
string &_gen_zeros_string(int n, string &res) {  
    string temp = "0";  
    res = "";  
    while (n) {  
        if (n&1) res += temp;  
        temp += temp;  
        n >>= 1;  
    }  
    return res;  
}  
  
string &_string_multiply_char(  
    const string &a,   
    char c, int n_zeros, string &res) {  
  
    int ch = c - '0';  
    string zeros_string;  
    _gen_zeros_string(n_zeros, zeros_string);  
    res = "0" + a + zeros_string;  
      
    const int alen = a.length();  
    for (int i = alen - 1, k = alen, add_bit = 0;   
        i >= 0 || add_bit > 0; --i, --k) {  
        int v = (i>=0 ? a[i]-48: 0) * ch + add_bit;  
        add_bit = v / 10;  
        res[k] = v % 10 + '0';  
    }  
    if (res[0] == '0') res = res.substr(1, res.length() - 1);  
  
    return res;  
}  
  
string &_string_multiply_string(const string &a, const string &b, string &res) {  
    string c = a, d = b;  
    _del_zeros_before_dot(c);  
    _del_zeros_before_dot(d);  
    int clen = c.length(), dlen = d.length();  
  
    if (clen < dlen) {   
        string t = c; c = d; d = t;  
        int x = clen; clen = dlen; dlen = x;  
    }  
  
    res = "0";  
    for (int i = dlen - 1; i >= 0; --i) {  
        string temp_res;  
        _string_multiply_char(c, d[i], (dlen - 1 - i), temp_res);  
  
        string add_res;  
        _string_add_string(res, temp_res, add_res);  
        res = add_res;  
    }  
    return res;  
}  
  
typedef struct _node {  
    string a, b;  
    string c, d;  
}node;  
  
inline void _m_fun(const string &a, const string &b,   
    const string &c, const string &d, string &res) {  
    string ares, bres;  
    _string_multiply_string(a, b, ares);  
    _string_multiply_string(c, d, bres);  
    _string_add_string(ares, bres, res);  
}  
void _multiple(node &x, node &y) {  
    node temp;  
    string ares, bres;  
    _m_fun(x.a, y.a, x.b, y.c, temp.a); //temp.a = x->a*y->a + x->b*y.c;  
    _m_fun(x.a, y.b, x.b, y.d, temp.b); //temp.b = x->a*y->b + x->b*y.d;  
    _m_fun(x.c, y.a, x.d, y.c, temp.c); //temp.c = x->c*y->a + x->d*y.c;  
    _m_fun(x.c, y.b, x.d, y.d, temp.d); //temp.d = x->c*y->b + x->d*y->d;  
    x = temp;  
}  
  
string fib(int n)  
{  
    if (0 == n) return "0";  
    if (1 == n || 2 == n) return "1";  
  
    node odd; odd.a = odd.d = "1"; odd.c = odd.b = "0"; //单位矩阵  
    node temp; temp.a = temp.b = temp.c = "1"; temp.d = "0"; // A矩阵  
  
    --n;  
    while (n) {  
        if (n&1) _multiple(odd, temp);  
        _multiple(temp, temp);  
        n >>= 1;  
    }  
  
    return odd.a;  
}  
  
int main(void)  
{  
    int n;  
    while (scanf("%d", &n) != EOF) {  
        string res = fib(n);  
        printf("%s\n", res.c_str());  
    }  
    return 0;  
}  
不过虽然是引进了乘法,不过由于大数模板乘法效率并不高效。速度还是比较慢的。还是推荐前面利用加法的大数模板。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值