fibonacci 數列及其延展
fibonacci計算
fibonacci數列是指 0,1,1,2,3,5,8,13,21……這樣自然數序列,即從第3項開始滿足f(n)=f(n-1)+f(n-2);
遞歸實現非常簡單:
long long fibonacci(unsigned int n) { int result[2] = {0, 1}; if (n < 2) return result[n]; return fibonacci(n-1) + fibonacci(n-2); }
以計算f(10)為例,必須先求得f(9)和f(8),要計算f(9),又必須先求得f(8)和f(7),如下圖,可以發現存在大量重復的計算
可見,當n比較大時,遞歸的算法效率非常低,比如計算f(100)機器就已經慢的不能接受了,用非遞歸的方法可以保證每個f(k)只被計算一次
long long fibonacci2(unsigned int n) { int result[2] = {0, 1}; if (n < 2) return result[n]; unsigned k; long long m = 0; long long prev = 1; long long prev_prev = 0; for (k=2; k<=n; k++) { m = prev + prev_prev; prev_prev = prev; prev = m; } return m; }
非遞歸的時間復雜度是O(n)。
下面介紹一種使用矩陣運算的方法,推導過程如下:
下面采用二分法計算矩陣的冪,時間復雜度只要O(logn):
void multiply(int c[2][2], int a[2][2], int b[2][2]) { int tmp[2][2]; tmp[0][0] = a[0][0]*b[0][0] + a[0][1]*b[1][0]; tmp[0][1] = a[0][0]*b[0][1] + a[0][1]*b[1][1]; tmp[1][0] = a[1][0]*b[0][0] + a[1][1]*b[1][0]; tmp[1][1] = a[1][0]*b[0][1] + a[1][1]*b[1][1]; c[0][0] = tmp[0][0]; c[0][1] = tmp[0][1]; c[1][0] = tmp[1][0]; c[1][1] = tmp[1][1]; } //二分法求矩陣冪 void matrix_pow(int a[2][2], int n) { int unit[2][2]; memcpy(unit, a, sizeof(unit)); if (n < 2) { } else if (n % 2 == 0) { multiply(a, a, a); matrix_pow(a, n/2); } else { multiply(a, a, a); matrix_pow(a, (n-1)/2); multiply(a, a, unit); } } long long fibonacci3(unsigned int n) { int result[3] = {0, 1, 1}; if (n < 3) { return result[n]; } int matrix[2][2] = {{1,1},{1,0}}; matrix_pow(matrix, n-2); return matrix[0][0] + matrix[0][1]; }
實際上,fibonacci數列有通項公式,可以直接計算第n項的值:
公式的具體推導過程參考這里。
fibonacci應用
在fibonacci數列中,當n趨向於無窮大時,前一項與后一項的比值越來越逼近黃金分割0.618,例如:
1÷1=1,1÷2=0.5,2÷3=0.666...,3÷5=0.6,5÷8=0.625,…………,55÷89=0.617977…,…………144÷233=0.618025…46368÷75025=0.6180339886…...
fibonacci質數的幾個結論:
- f(3)=2和f(4)=3是Fibonacci質數;
- 從f(5)=5開始,某項為Fibonacci質數當且僅當它的項數為質數;
- 第k小的Fibonacci質數是以質數數列中的第k個數為項數的Fibonacci數(f(3)和f(4)除外);
推導過程可以看這里。
對一個fibonacci數列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89……,
而一個質數序列是2, 3, 5, 7, 11, 13, 17, 19……,以這個質數序列作為項數,對應的fibonacci項為:
f(5)=5,f(7)=13,f(11)=89,這些也都是質數。
如何判斷一個整數是不是在fibonacci序列中
最簡單的做法是依次計算 f(1)、f(2)……、f(n),然后跟該整數比較看是否相等。
但當該整數非常大時,這種做法效率很低,一種更簡單的方法參考這里,即一個數 N 如果滿足 5 N^2 + 4 或 5N^2 – 4 能夠開平方,則該數是fibonacci數。
一個台階總共有n級,如果一次可以跳1級,也可以跳2級。求總共有多少總跳法,並分析算法的時間復雜度。
首先我們考慮最簡單的情況。如果只有1級台階,那顯然只有一種跳法。如果有2級台階,那就有兩種跳的方法了:一種是分兩次跳,每次跳1級;另外一種就是一次跳2級。
現在我們再來討論一般情況。我們把n級台階時的跳法看成是n的函數,記為f(n)。當n>2時,第一次跳的時候就有兩種不同的選擇:一是第一次只跳1級,此時跳法數目等於后面剩下的n-1級台階的跳法數目,即為f(n-1);另外一種選擇是第一次跳2級,此時跳法數目等於后面剩下的n-2級台階的跳法數目,即為f(n-2)。因此n級台階時的不同跳法的總數f(n)=f(n-1)+(f-2)。
我們把上面的分析用一個公式總結如下:
/ 1 n=1
f(n) = 2 n=2
\ f(n-1)+(f-2) n>2
這就是我們熟悉的Fibonacci序列。
對上面的題目再改進一下:
一個台階總共有n級,如果一次可以跳1級,也可以跳2級......它也可以跳上n級。此時該青蛙跳上一個n級的台階總共有多少種跳法?
用Fib(n)表示青蛙跳上n階台階的跳法數,設定Fib(0) = 1,
當n = 1 時,只有一種跳法,即1階跳:Fib(1) = 1;
當n = 2 時,有兩種跳的方式,一階跳和二階跳:Fib(2) = Fib(1) + Fib(0) = 2;
當n = 3 時,有三種跳的方式,第一次跳出一階后,后面還有Fib(3-1)中跳法;第一次跳出二階后,后面還有Fib(3-2)中跳法;第一次跳出三階后,后面還有Fib(3-3)中跳法,即Fib(3) = Fib(2) + Fib(1)+Fib(0)=4;
當n = n 時,共有n種跳的方式,第一次跳出一階后,后面還有Fib(n-1)中跳法; 第一次跳出二階后,后面還有Fib(n-2)中跳法..........................第一次跳出n階后,后面還有 Fib(n-n)中跳法,得到
Fib(n) = Fib(n-1)+Fib(n-2)+Fib(n-3)+..........+Fib(n-n) = Fib(0)+Fib(1)+Fib(2)+.......+Fib(n-1)
那么有
Fib(n-1) = Fib(0)+Fib(1)+Fib(2)+.......+Fib(n-2)
兩式相減,得到 Fib(n) - Fib(n-1) = Fib(n-1),即 Fib(n) = 2*Fib(n-1)
現有長為144cm的鐵絲,要截成n小段(n>2),每段的長度不小於1cm,如果其中任意三小段都不能拼成三角形,則n的最大值為多少?
由於形成三角形的充要條件是任何兩邊之和大於第三邊,因此不構成三角形的條件就是任意兩邊之和不超過最大邊。截成的鐵絲最小為1,因此可以放2個1,第三條線段就是2(為了使得n最大,因此要使剩下來的鐵絲盡可能長,因此每一條線段總是前面的相鄰2段之和),依次為:1、1、2、3、5、8、13、21、34、55,以上各數之和為143,與144相差1,因此可以取最后一段為56,這時n達到最大為10。
用2X1(2行1列)的小矩形橫着或者豎着去覆蓋更大的矩形。請問用8個2X1的小矩形無重復地覆蓋一個2X8的大矩形,總共有多少種方法。
設f(8)表示覆蓋2X8大矩形的方法綜述。假設第一個小矩形是豎着去覆蓋大矩形,那么還剩下由7個2X1的小矩形組成的大矩形f(7);假設第一個小矩形是橫着去覆蓋大矩形,那么還剩下由6個2X1的小矩形組成的大矩形f(6)。即f(8)=f(7)+f(6)。依此類推,最后f(1)=1,f(2)=2。使用計算斐波那契數列的方法計算這道題目即可求出答案。
參考文檔:
http://blog.csdn.net/hackbuteer1/article/details/6684867