斐波那契数列:1,1,2,3,5,8.......某一项为前两项的和的数列称为斐波那契数列。
故有递推公式:f(n) = f(n-1) + f(n-2)。边界条件:n=1时和n=2。f(1) = 1 ,f(2)=1
所以求第N项斐波那契数列的递归代码为:
int f(int n)
{
if (n == 1)
{
return 1;
}
if (n == 2)
{
return 1;
}
return f(n - 1) + f(n - 2);
}
斐波那契的非递归算法思想:将数列的第n项看成一个累加和,当n=1和n=2时候返回1,从n=3开始,用num1和num2代表第n-1和n-2项,使用temp记录加和,使用sum来表示第n项的值,每次循环后将temp的值累加到sum上,将n-2置为n-1的值,将num1置为sum的值。代码如下:
int fun(int n)
{
int sum = 0;
int num1 = 1;
int num2 = 1;
if (n == 1 || n == 2)
{
return 1;
}
for (int i = 3; i <= n; ++i)
{
sum = num1 + num2;
num1 = num2;
num2 = sum;
}
return sum;
}
如果当n超过一个数值后,则会造成存储溢出,这是因为int的最大存储值为2的31次方-1。此时需要使用高精度算法来进行斐波那契数列的计算。在此之前先看一个高精度加法的例子
#define MAX_SIZE 500
int max(int a, int b)
{
return a > b ? a : b;
}
int min(int a, int b)
{
return a < b ? a : b;
}
void add()
{
char s1[MAX_SIZE], s2[MAX_SIZE];
int num1[MAX_SIZE] = {0}, num2[MAX_SIZE] = {0}, sum[MAX_SIZE] = {0};
cin >> s1;
cin >> s2;
int num1_len = strlen(s1);
int num2_len = strlen(s2);
for (int i = 0; i < num1_len; i++)
{
num1[num1_len - i - 1] = s1[i] - '0'; //转成数字后倒排
}
for (int i = 0; i < num2_len; i++)
{
num2[num2_len - i - 1] = s2[i] - '0';
}
//加法操作
int add_len = max(num1_len, num2_len) + 1;
for (int i = 0; i < add_len; ++i)
{
sum[i] += num1[i] + num2[i];
sum[i + 1] = sum[i] / 10;
sum[i] = sum[i] % 10;
}
if (sum[add_len - 1] == 0)
{
add_len--;
}
for (int i = add_len-1; i >=0; --i)
{
cout << sum[i];
}
}
算法的思想是:使用char类型数组来接受输入的大数,将char类型的大数数组转为Int类型的数组来表示一个大数,注意这里需要将顺序转置,方便加法计算(方便从低位开始进行计算,小的数字高位没有则补零)。加法的计算规则也很简单:两数之和除以10用余数代表当前的位的数,商代表进位数。
接下来给出斐波那契数列的高精度算法。
首先是使用一个类来定义一个高精度的整数。定义如下:
using namespace std;
class NumberH
{
public:
//构造函数
NumberH(const char* s = 0);
NumberH(char s);
//拷贝构造
NumberH(const NumberH& num);
~NumberH();
public:
//重载+
NumberH operator +(const NumberH& num) const;
//拷贝赋值(注意这里返回是this对象的引用)
NumberH& operator =(const NumberH& num);
char* get_str()
{
return m_str;
}
private:
char* m_str = 0;
};
std::ostream& operator <<(std::ostream& os, NumberH& num);
.cpp文件如下:
#include "numberh.h"
using namespace std;
int max(int a, int b)
{
return a > b ? a : b;
}
void reverse_char_array(char* str)
{
if(str) {
int len = strlen(str);
for(int i = 0; i <= (len / 2); ++i) {
int j = len - i - 1;
char temp = *(str + i);
*(str + i) = *(str + j);
*(str + j) = temp;
}
}
}
char* reverse_array(char* str)
{
char* ret = 0;
if(str) {
int len = strlen(str);
ret = new char[len + 1];
for(int i = 0; i < len; ++i) {
int j = len - i - 1;
*(ret + j) = *(str + i);
}
*(ret + len) = '\0';
}
delete[] str;
return ret;
}
NumberH::NumberH(const char* s)
{
if(s) {
int len = strlen(s);
m_str = new char[len + 1];
strcpy(m_str, s);
} else {
m_str = new char[1];
m_str = '\0';
}
}
NumberH::NumberH(char s)
{
m_str = new char[2];
*(m_str) = s;
*(m_str + 1) = '\0';
}
NumberH::NumberH(const NumberH& num)
{
m_str = new char[strlen(num.m_str) + 1];
strcpy(m_str, num.m_str);
}
NumberH::~NumberH()
{
if(!m_str) {
delete[] m_str;
}
}
/**
* @brief NumberH::operator + 实现两个高精度整数的加法操作
* @param num 高精度整数
* 思想是将char->指向的字符数组,从最后端每一位转为int进行加法操作。
* 对结果再转为char,构造一个新的高精度整数
*
* @return 返回一个高精度整数
*/
NumberH NumberH::operator+(const NumberH& num) const
{
if(num.m_str) {
int num_len = strlen(num.m_str);
int this_len = strlen(m_str);
int add_len = max(num_len, this_len) + 1;
char* ret = new char[add_len + 1];
memset(ret, '0', add_len);
int* num_array = new int[add_len]();
int* this_array = new int[add_len]();
//char--->int : char - '0'
for(int i = num_len - 1; i >= 0; --i) {
int j = num_len - i - 1;
*(num_array + j) = (*(num.m_str + i)) - '0';
}
for(int i = this_len - 1; i >= 0; --i) {
int j = this_len - i - 1;
*(this_array + j) = (*(m_str + i)) - '0';
}
//int--->char: int + '0'
for(int i = 0; i < add_len - 1; ++i) {
int sum = *(num_array + i) + *(this_array + i);
int temp = *(ret + i) - '0';
sum += temp;
*(ret + i + 1) = sum / 10 + '0';
*(ret + i) = sum % 10 + '0';
}
delete[] num_array;
delete[] this_array;
//添加\0结束符
if(*(ret + add_len - 1) == '0') {
*(ret + add_len - 1) = '\0';
} else {
*(ret + add_len) = '\0';
}
ret = reverse_array(ret);
return NumberH(ret);
}
return NumberH();
}
NumberH& NumberH::operator =(const NumberH& num)
{
//自我赋值检测
if(this == &num) {
return *this;
}
delete[] m_str;
m_str = new char[strlen(num.m_str) + 1];
strcpy(m_str, num.m_str);
return *this;
}
ostream& operator <<(ostream& os, NumberH& num)
{
return os << num.get_str();
}
上述代码基本是基于高精度整数加法的,有些许不一样:
NumberH类使用的是一个char*类型的指针指向一块内存空间的,使用new char[len]进行动态分配。并不像上面加法中使用固定长度的char数组。
因此,需要注意的是:在重载+函数中返回的NumberH分配的空间是max(len1,len2)+2,并不是+1,这是因为需要在最后位上加上'\0'的结束符。否则使用strlen()函数会出现错误导致程序报错。
另外进位的计数是求得当前位与'0'字符得差值(通过ASCII表的方法)求得。也就是int---->char需要进行加法(+'0'),char---->int需要进行减法(-'0')。
最后给出高精度算法的斐波那契数列第N项求解代码:
using namespace std;
NumberH Fibonacci(int n)
{
if(n == 1 || n == 2) {
NumberH num('1');
return num;
}
NumberH num1('1');
NumberH num2('1');
NumberH ret('0');
for(int i = 3; i <= n; ++i) {
ret = num1 + num2;
num1 = num2;
num2 = ret;
}
cout << ret << endl;
return ret;
}
int main(int argc, char* argv[])
{
Fibonacci(25);
return 0;
}