面试题整理-斐波那契数列
2016-09-02 22:52
204 查看
这个很好。其实也就是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
);
}
return 0;
}
//注意打表及长整型。
解题
首先,不要用递归的方法。那个是很烂的解法。直接忽略。这里也不用打表的方法。也就是说,每给一个数。直接进行计算从而得到结果。
你可能会想到下面的这个方法:
但是这种方法,有个很明显的缺点。也就是容易溢出。32位只能计算至45。64位只能计算至92。所以当数值一大,不管是用long或者long long也好。都不能解决。
大整数斐波那契数列-推荐使用,简单有效
那么首先想到的是利用大整数来进行求解。这里我们用string来摸拟大整数。
现在得到一个大整数的解法。也就是对于任意大的数。都可以得到解。现在希望对这个进行改进。大数的加法,测试地址。
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)。
#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 <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
);
}
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; }不过虽然是引进了乘法,不过由于大数模板乘法效率并不高效。速度还是比较慢的。还是推荐前面利用加法的大数模板。
相关文章推荐
- 面试题整理-斐波那契数列
- 2016年一些面试题的整理和心情--1异或到100
- Java面试题 -- 个人整理
- 前端面试题及答案整理(二)
- 搜集整理的前端面试题1
- 前端开发--面试题整理(JS篇)
- java 面试题整理《一》
- 网上流传的Linux内核开发面试题整理
- 近期面试题整理(二叉树的中序遍历、合并排序链表数组)
- 海量数据面试题整理 .
- 史上最全的Java&Android面试题搜集整理
- Android面试题及答案(详细整理)
- 前端面试题整理
- java 开发面试题小整理(二)
- [iOS] 面试题整理(带答案) 一
- Android 名企面试题及涉及知识点整理
- 2017.02 Java编程面试题整理(持续更新…)
- 2018年1月iOS招人心得(附面试题)- 答案整理
- 个人整理的面试题
- 收集整理的flex面试题汇总