zoukankan      html  css  js  c++  java
  • 斐波那契数列的分治法计算

               《编程之美》一书中讲述斐波那契数列的问题,之前大学本科的时候就接触这个问题,那时候开始就知道使用递归来计算,可是一直没有考虑过改进下该算法。。。囧~~菜   直到看到这本书,发现原来之前好多问题都可以优化,斐波那契就是其中之一,其中书本中讲述了三种方法、:

    1. 第一种就是对平时的递归算法进行优化,增加了数组专门记录每个子问题的解,实际上是动态规划的思想;
    2. 第二种利用数学中通项公式,利用F0=0,F1=1,Fn=F(n-1)+F(n-2)(n>=2,n∈N*)递推公式得到F(n)的特征方程,然后直接利用公式计算。。。好bug哇~ 不过存在无理数,数据的精度就无法保证了~~
    3. 利用分治策略,将问题转换为矩阵乘幂的问题。

      其余两种大家可以去网上找答案脑补下,我这里关键阐述下第三种哈~

    基本的原理是运用矩阵:

    (Fn Fn-1) = (Fn-1 Fn-2)*A,求解得到

    A = { {1,1}, {1,0}}

    然后通过递推式

    (Fn Fn-1) = (Fn-1 Fn-2)*A = (Fn-2 Fn-3)*A^2 = .... = (F1 F0)*A^(n-1)

    然后只要计算A^(n-1),再与矩阵(F1 F0)相乘,就可以的得到 Fn的值,我非常佩服作者的是,能把重复计算转化为对A的n-1乘法计算,因为计算A的n-1乘法有快速相乘的方法,比如计算m的10000次方,其实最少的计算次数,等于10000的最高比特位的位置与零的个数,即14~28次乘法运算,并不需要10000次乘法。

    对于n有 n = ak*2^k + ak-1*2^k-1 + ... + a1*2 + a0,其中ai = 0 或1 ,i = 0,1,2... k ,将幂数变为二进制形式表示,此时我们只需要进行logn次的运算(以2为底)。 也即 快速指数相乘法。

    下面是自己实现的代码,可能写的比较挫~ 关键是matrix矩阵的写法哈~

    #include <iostream>
    
    using namespace std;
    
    //
    struct Matrix 
    {
        Matrix(int n)
        {
            if (n >= 1)
            {
                nLength = n;
                data = new long long*[nLength];
                for (int i = 0; i!= n; i++)
                {
                    data[i] = new long long[nLength];
                }
            }
            else
            {
                data = NULL;
            }
        }
        Matrix()
        {
            data = NULL;
        }
        // 拷贝构造函数
        Matrix(const Matrix &m)
        {
            int nLen = m.GetMatrixLength();
            if (nLen >= 1)
            {
                nLength = nLen;
                data = new long long*[nLength];
                for (int i = 0; i!= nLen; i++)
                {
                    data[i] = new long long[nLength];
                }
                //
                for (int j = 0; j!=nLen; j++)
                {
                    for (int k = 0; k != nLen; k++)
                    {
                        data[j][k] = m.GetMatrixValue(j,k);
                    }
                }
            }
            else
            {
                data = NULL;
            }
        }
    
        // 重载赋值操作符
        // 错误都在这里。。。注意因为涉及到指针,一定要深拷贝
        Matrix& operator = (const Matrix& m)
        {
            // 如果是自己 直接返回即可
            if (this == &m)
            {
                return *this;
            }
            // 如果当前对象已有空间也需要释放掉 重新分配
            if (data)
            {
                for (int i = 0; i!=nLength ; i++)
                {
                    if (data[i])
                    {
                        delete[] data[i];
                    }
                }
                delete[] data;
                data = NULL;
            }
            //
            if (data == NULL)
            {
                nLength = m.GetMatrixLength();
                data = new long long*[nLength];
                for (int i = 0; i!= nLength; i++)
                {
                    data[i] = new long long[nLength];
                }
            }
            for (int i=0; i!= nLength; i++)
            {
                for (int j = 0; j != nLength; j++)
                {
                    this->data[i][j] = m.GetMatrixValue(i,j);
                }
            }
    
            // 方法返回时 调用析构 函数  释放空间
            return *this;
        }
    
    
        // 释放所有分配的空间
        ~Matrix()
        {
            if (data)
            {
                for (int i = 0; i!=nLength ; i++)
                {
                    if (data[i])
                    {
                        delete[] data[i];
                    }
                }
                delete[] data;
                data = NULL;
            }
        }
    
        void setIdentity()
        {
            for (int i = 0; i != nLength; i++)
            {
                for (int j = 0; j!= nLength; j++)
                {
                    data[i][j] =( (i ==j) ? 1 : 0 );
                }
            }
        }
    
        // 重载乘等操作符 一定要记得深拷贝哦 亲
        Matrix& operator *= (const Matrix& m)
        {
            if (this->nLength == m.nLength)
            {
                Matrix  tmp(m.nLength);
                for (int i = 0; i!= nLength; i++)
                {
                    for (int j = 0; j!= nLength; j++)
                    {
                        // 计算每个位置的值
                        long long nValue = 0;
                        for (int p = 0; p != nLength; p++)
                        {
                            nValue += data[i][p] * (m.GetMatrixValue(p,j));
                        }
                        tmp.SetMatrixValue(i,j,nValue);
                    }
                }
                //
                *this = tmp;
                // 返回时会调用析构 释放tmp对象
            }
            return *this;
        }
    
        //
        void SetMatrixValue(const int i,const int j, const long long nValue)
        {
            data[i][j] = nValue;
        }    long long  GetMatrixValue(const int i, const int j) const 
        {
            long long n = data[i][j];
            return n;
        }
        unsigned int GetMatrixLength() const    {
            return nLength;
        }
    
    private:
        unsigned int nLength;
        long long **data;
    };
    
    
    
    
    void GetMatrixPow(const Matrix &m, int index,Matrix &result)
    {
        // 先单位化结果矩阵
        result.setIdentity();
    //    Matrix  tmp(m.GetMatrixLength());        //
    //    tmp = m;
        Matrix tmp = m;    // C语言风格的赋值 会调用拷贝构造函数
    
        for (;index;index >>=1)
        {
            if (index & 1)
            {
                result *= tmp;
            }
            tmp *= tmp;
        }
    
        // 该函数结束 调用matrix析构函数 释放tmp所占空间
    }
    
    long long Fibonacci(int n)
    {
        if (n == 0)
        {
            return 0;
        }
        else if (n == 1)
        {
            return 1;
        }
    
        long long nValue = 0;
        Matrix A(2);
        A.SetMatrixValue(0,0,1);
        A.SetMatrixValue(0,1,1);
        A.SetMatrixValue(1,0,1);
        A.SetMatrixValue(1,1,0);
        // 结果矩阵一定还是2*2的
        Matrix result(2);
        GetMatrixPow(A , n-1,result);
    
        nValue = result.GetMatrixValue(1,0);//F0* result.GetMatrixValue(0,0) + F1* result.GetMatrixValue(1,0);
        
        return nValue;
    }
    
    
    int main()
    {
        int  N;
        cout<<"输入你要计算的斐波那契数列的项(目前可以计算到93~):";
        cin>>N;
    
        for (int i = 0; i <= N; i++)
        {
            long long nResult = Fibonacci(i);
            cout<<nResult<<endl;
        }
    
        return 0;
    }

    程序的运行结果如下所示:

    image

    当然这里矩阵每个元素我用了long long 64位,经测试可以计算到93~ 需要改进的话,需要选择合适的变量类型。

  • 相关阅读:
    实现一个简单的Http代理服务器
    微软的反向代理库YARP
    Elastic Search 基础——分析(Analysis)
    Elastic Seach基础—映射(Mapping)
    使用WebView2在.Net程序中集成Edge chromium
    Asp.net Core中实现自定义身份认证
    在Razor Pages的Url中增加扩展名后缀
    利用Aria2构造自己的下载工具(一)
    Mongodb中GUID的显示问题
    在Eclipse中使用MAT分析Android程序内存使用状况(转)
  • 原文地址:https://www.cnblogs.com/jiabei521/p/3355613.html
Copyright © 2011-2022 走看看