1. 程式人生 > >斐波那契數列取模(大數)分治演算法

斐波那契數列取模(大數)分治演算法

這是演算法課程上完分之策略後老師留的一道題目:

菲波那契數列如下:1,1,2,3,5,8,13,21,34......其中a[1] = 1, a[2] = 1, a[n]=a[n-1]+a[n-2](n>=3)。對給定的下標n,求解a[n]%1997的值.

其中測試資料n是整數範圍內。

這個題目,主要是用到很關鍵的一個數學知識,斐波那契數列的求法,可以轉換為矩陣的連乘,矩陣的n此方演算法又可以用分治的演算法。

而且又有理論依據:(n*m)%c=[ (n%c)*(m%c) ]%c  ;   (n+m)%c=[   (n%c)+(m%c)  ]%c ,所以過程中的結果可以隨時取模,而不影響最終的結果

關於斐波那契數列的矩陣連乘求法如下:

我們知道我們若要簡單計算f(n),有一種方法就是先儲存 
a=f(0),b=f(1),然後每次設: 
a'=b b'=a+b

並用新的a'和b'來繼續這一運算

如果大家熟悉利用“矩陣”這一工具的話,就知道,如果把a、b寫成一個向量[a,b],完成上述操作相當於乘以矩陣 
0 1 
1 1 
也就是說,如果我們要求第100個fibonacci數,只需要將矩陣 
[0,1]乘上 
0 1 
1 1 
的一百次方,再取出第一項

因為我們知道,矩陣運算滿足結合律,一次次右乘那個矩陣完全可以用乘上那個矩陣的N次方代替,更進一步,那個矩陣的N次方就是這樣的形式: 
f(n-1) f(n) 
f(n) f(n+1)

而求矩陣的N次方,由於矩陣乘法滿足結合律,所以我們可以用log(N)的演算法求出——這個演算法大家都會麼? 
一個是二分,一個是基於二進位制的求冪

二分的原理:要求矩陣的N次方A(N),設i=N/2若N%2==1, 則 A(N)=A(i)*A(i)*A(1)若N%2==0, 則 A(N)=A(i)*A(i)

基於二進位制的原理:將N拆為二進位制數,譬如13=1101那麼 A^13= A^8 * A^4 * A^1 (這裡^表示冪運算)

也就是說,由A^1開始,自乘得到A^2,然後自乘得到A^4,如果N對應位為1,則將這個結果乘到目標上去

這樣的話,將所有乘法改為模乘,就可以得到一個較大Fibonacci數除以M的餘數

若不用遞迴,其實類似

實現程式碼:

複製程式碼
#include<iostream>
using namespace std;
// f(n)=f(i)*f(n-i-1)+f(i+1)*f(n-i)int tempA,tempB,tempC,tempD;
void func(int n,int &a,int &b,int &c,int &d){
    if(n==1){
        a=0;
        b=c=d=1;
        return ;
    }
    if(n%2==0){
        func(n/2,a,b,c,d);
        tempA=a*a+b*c;
        tempB=b*(a+d);
        tempC=c*(a+d);
        tempD=c*b+d*d;
        a=tempA%1997;
        b=tempB%1997;
        c=tempC%1997;
        d=tempD%1997;
        return;
    }
    else{
        func(n/2,a,b,c,d);
        tempA=b*(a+d);
        tempB=a*a+b*(a+c+d);
        tempC=c*b+d*d;
        tempD=c*(a+b+d)+d*d;
        a=tempA%1997;
        b=tempB%1997;
        c=tempC%1997;
        d=tempD%1997;
        return;
    }
}
int main(){
    int n;
    while(cin>>n&&n!=0){
        int a,b,c,d;
        if(n<=2){
            cout<<1<<endl;
            continue;
        }
        else n-=2;
        func(n,a,b,c,d);
        cout<<(b+d)%1997<<endl;
    }    
    return 0;
}

相關推薦

數列大數分治演算法

這是演算法課程上完分之策略後老師留的一道題目: 菲波那契數列如下:1,1,2,3,5,8,13,21,34......其中a[1] = 1, a[2] = 1, a[n]=a[n-1]+a[n-2](n>=3)。對給定的下標n,求解a[n]%1997的值. 其中測試資料n是整數範圍內。 這個題目,主要

【轉】數列大數分治演算法

此中測試資料n是整數侷限內。 這個題目,主如果用到很關鍵的一個數學常識,斐波那契數列的求法,可以轉換為矩陣的連乘,矩陣的n此方演算法又可以用分治的演算法。 並且又有理論根據:(n*m)%c=[ (n%c)*(m%c) ]%c ; (n+m)%c=[ (n%c)+(m%c) ]%c ,所以過程中的成果可以

Parity check 數列 n(0≤n≤) in

I - Parity check Crawling in process... Crawling failed Time Limit:2000MS     Memory Limit:524288KB     64bit IO Format:%lld & %llu

數列談矩陣1

斐波那契序列 集錦 (轉) [定理1] 標準Fibonacci序列(即第0項為0,第1項為1的序列)當N大於1時,一定有f(N)和f(N-1)互質 其實,結合“互質”的定義,和一個很經典的演算法就可以輕鬆證明  對,就是輾轉相除法  互質的定義就是最大公約數為1 數學

Java中的組織形式、類與物件、靜態的static關鍵字、最終的final關鍵字、方法傳參方式、遞迴階乘、數列、漢諾塔

Java程式的組織形式 Java程式需要把程式碼以類的形式組織起來,然後被Java編譯器編譯,再被JVM執行。Java程式是以類的結構為基礎的。 Java程式的基本要素 識別符號 識別符號命名規範 關鍵字(保留字) 關鍵字(保留字)具有專門的意義和用途

數列Fibonacci實現遞迴、尾遞迴、迴圈

一、遞迴 簡單的來說遞迴就是一個函式直接或間接地呼叫自身,是為直接或間接遞迴。 遞迴一般用於解決三類問題:   (1)資料的定義是按遞迴定義的。(Fibonacci函式,n的階乘)   (2)問題解法按遞迴實現。(回溯)   (3)

數列的實現c語言

非遞迴實現: 核心思想: f(1) = f(2) = 1; f(n) = f(n-1) + f(n-2); #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> int f

大數數列+

斐波那契序列 集錦 (轉) [定理1] 標準Fibonacci序列(即第0項為0,第1項為1的序列)當N大於1時,一定有f(N)和f(N-1)互質 其實,結合“互質”的定義,和一個很經典的演算法就可以輕鬆證明 對,就是輾轉相除法 互質的定義就是最大公約數為1 數學歸納法是很有用的證明方法,我們接下來

數列的遞迴與迴圈的演算法實現

斐波那契數列,但凡學過程式設計的童鞋們應該都懂,背景就不介紹了(就是大兔子生小兔子的故事),無論是面試還是實際的運用,常見的一個思路就是先用最先基本的辦法實現,然後根據實際要求,一步步改進,優化演算法效率。今天就以斐波那契數列這個大家都很熟悉的為例來小小感受一下。

UVA 11582 巨大數的數列 (大數,冪的計算方法)

Problem F: Colossal Fibonacci Numbers! The i'th Fibonacci number f (i) is recursively defined in the following way: f (0) = 0 and f (1) = 1f (i+2) = f 

矩陣快速冪 + 求數列第n項Fibonacci

兩矩陣相乘,樸素演算法的複雜度是O(N^3)。如果求一次矩陣的M次冪,按樸素的寫法就是O(N^3*M)。既然是求冪,不免想到快速冪取模的演算法,前面有快速冪取模的介紹,a^b %m 的複雜度可以降

[luoguP1962] 數列矩陣快速冪

truct ons 技術 pan opera http 快速冪 printf ble 傳送門 解析詳見julao博客連接 http://worldframe.top/2017/05/10/清單-數學方法-——-矩陣/ —&

Java 兔子問題數列擴展篇

aik 第一個 truct func main target htm bre trace Java 兔子問題(斐波那契數列)擴展篇 斐波那契數列指的是這樣一個數列 0, 1, 1, 2,3, 5, 8, 13, 21, 34, 55, 89, 144, ...對於這個

hdu 4549 M數列矩陣高速冪,高速冪降冪

else if stdlib.h article 1.0 ostream void 我們 memset font http://acm.hdu.edu.cn/showproblem.php?pid=4549 f[0] = a^1*b^0%p,f[1] = a^0*b

[luoguP2626] 數列升級版模擬

sub std [1] 斐波那契數 == cnblogs () ios git 傳送門 模擬 代碼 #include <cmath> #include <cstdio> #include <iostream>

用遞歸方法計算數列Recursion Fibonacci Python)

n-1 html pri style strong ans rdo 黃金分割 nac 先科普一下什麽叫斐波那契數列,以下內容摘自百度百科: 斐波那契數列(Fibonacci sequence),又稱黃金分割數列、因意大利數學家列昂納多·斐波那契(Leonardoda Fib

數列的兩種實現遞歸和非遞歸

result 數列 == 非遞歸 fib color 效率 i++ 思想 查找斐波納契數列中第 N 個數。 所謂的斐波納契數列是指: 前2個數是 0 和 1 。 第 i 個數是第 i-1 個數和第i-2 個數的和。 斐波納契數列的前10個數字是: 0, 1, 1, 2,

以計算數列為例說說動態規劃算法Dynamic Programming Algorithm Overlapping subproblems Optimal substructure Memoization Tabulation

ash 麻省理工學院 遞歸樹 經典 top 有關 ctu dynamic 代碼 動態規劃(Dynamic Programming)是求解決策過程(decision process)最優化的數學方法。它的名字和動態沒有關系,是Richard Bellman為了唬人而取的。

數列大數加法

斐波那契 ++ add ret div 加法 clas 註意 cin 題意: 求斐波那契的前10000項目 分析: 模擬豎式加法, 用string作為數字的儲存形式 #include<bits/stdc++.h> using namespace std; st

題解 P2626 【數列升級版

實現 ace 升級版 因數 退出 turn 思路 font return 題解 P2626 【斐波那契數列(升級版)】 這一道題目的解法多種多樣,但就對於題目本身而言拿暴力分應該不是太難,簡單地模擬一下斐波拉契的過程,求出第n個,最後分解質因數也不難暴力出奇跡。對於代碼的實