算法系列:Objective-C四種演算法求斐波那契數列
什麼是斐波那契數列?
摘自《維基百科》
斐波那契數列(ofollow,noindex">義大利語 :Successione di Fibonacci),又譯為菲波拿契數列 、菲波那西數列 、斐波那契數列 、黃金分割數列 。
在數學 上,費波那契數列 是以遞迴 的方法來定義:F(1)=1,F(2)=1, F(3)=2,F(n)=F(n-1)+F(n-2)(n>=4,n∈N*)
用文字來說,就是費波那契數列由0和1開始,之後的費波那契係數就是由之前的兩數相加而得出。首幾個費波那契係數是:0 ,1 ,1 ,2 ,3 ,5 ,8 ,13 ,21 ,34 ,55 ,89 ,144 ,233 ……
摘自《百度百科》
斐波那契數列(Fibonacci sequence),又稱黃金分割 數列、因數學家 列昂納多·斐波那契(Leonardoda Fibonacci)以兔子繁殖為例子而引入,故又稱為“兔子數列 ”,指的是這樣一個數列:1、1、2、3、5、8、13、21、34、……在數學上,斐波納契數列以如下被以遞推的方法定義:F(1)=1,F(2)=1, F(3)=2,F(n)=F(n-1)+F(n-2)(n>=4,n∈N*)在現代物理、準晶體結構、化學等領域,斐波納契數列都有直接的應用,為此,美國數學會從1963年起出版了以《斐波納契數列季刊》為名的一份數學雜誌,用於專門刊載這方面的研究成果。
定義:
斐波那契數列指的是這樣一個數列 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368........
這個數列從第3項開始,每一項都等於前兩項之和。
斐波那契數列 的定義者,是義大利數學家列昂納多·斐波那契 (Leonardo Fibonacci),生於公元1170年,卒於1250年,籍貫是比薩 。他被人稱作“比薩的列昂納多 ”。1202年,他撰寫了《算盤全書》(Liber Abacci)一書。他是第一個研究了印度 和阿拉伯數學理論的歐洲人。他的父親被比薩的一家商業團體聘任為外交領事,派駐地點相當於今日的阿爾及利亞 地區,列昂納多因此得以在一個阿拉伯老師的指導下研究數學。他還曾在埃及 、敘利亞 、希臘、西西里 和普羅旺斯 等地研究數學。
看了這麼多簡介之後,其實說的簡單點斐波那契數列就是一系列數字,從第三項開始當前項等於前兩項之和。下面我用OC給出斐波那契數列
第一種方式:
斐波那契數列遞迴呼叫,指數級增長2^n。遞迴方式效率低,時間複雜度是指數級的。
/** 斐波那契數列遞迴呼叫,指數級增長2^n */ - (NSUInteger)Fibonacci:(NSUInteger)n { if (n < 2) { return n; } else { return [self Fibonacci:n-1] + [self Fibonacci:n-2]; } }
第二種方式:
用陣列儲存之前計算過的結果,減少大量重複計算,這樣演算法複雜度優化為 O(n)。
- (NSUInteger)Fibonacci2:(NSUInteger)n { if (n < 2) { return n; } else { NSMutableArray *array = [NSMutableArray arrayWithCapacity:n + 1]; array[0] = @(0); array[1] = @(1); for (int i = 2; i < array.count; i++) { array[i] = @([array[i - 1] unsignedIntegerValue] + [array[i - 2] unsignedIntegerValue]); } return [array[n] unsignedIntegerValue]; } }
第三種方式:
同樣是斐波那契數列,由於實際只有前兩個計算結果有用,我們可以使用中間變數來儲存,這樣就不用建立陣列以節省空間。同樣演算法複雜度優化為 O(n)。
- (NSUInteger)Fibonacci3:(NSUInteger)n { //NSDate *now1 = [NSDate date]; if (n < 2) { return n; } else { NSUInteger a = 0, b = 1; for (int i = 1; i < n; i++) { b = a + b; a = b - a; } //NSLog(@"fb3耗時:%lf", [now1 timeIntervalSinceNow]); return b; } }
第四種方式:
通過使用矩陣乘方的演算法來優化斐波那契數列演算法。演算法的時間複雜度可以優化為O(log n)。
// 計算二階矩陣的相乘 - (NSArray *)multiArrayA:(NSArray *)arrayA arrayB:(NSArray *)arrayB { // 定義一個空的二階矩陣 NSMutableArray *arrayC = [NSMutableArray arrayWithObjects:@[@0,@0].mutableCopy,@[@0,@0].mutableCopy, nil]; for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { for (int k = 0; k < 2; k++) { NSNumber *numA = arrayA[i][k]; NSNumber *numB = arrayB[k][j]; NSNumber *numC = arrayC[i][j]; // 新二階矩陣的值計算 numC = @(numC.unsignedIntegerValue + numA.unsignedIntegerValue * numB.unsignedIntegerValue); [arrayC[i] replaceObjectAtIndex:j withObject:numC]; } } } return arrayC; } - (NSUInteger)Fibonacci4:(NSUInteger)n { //NSDate *now2 = [NSDate date]; // 結果矩陣最開始的結果矩陣也可以看做是1,因為這個矩陣和任意二階A矩陣相乘結果都是A NSArray *arrayA = [NSArray arrayWithObjects:@[@1,@0],@[@0,@1], nil]; // 元矩陣,這裡可以把元矩陣看做是2**0=1 NSArray *arrayB = [NSArray arrayWithObjects:@[@1,@1],@[@1,@0], nil]; while (n) { // 取n的二進位制的最後一位和1做與運算,如果最後一位是1,則進入if體內部 if (n & 1) { // 如果在該位置n的二進位制為1,則計算ans和base矩陣 arrayA = [self multiArrayA:arrayA arrayB:arrayB]; } // base矩陣相乘,相當於初始base矩陣的冪*2 arrayB = [self multiArrayA:arrayB arrayB:arrayB]; // n的二進位制往右移一位 n >>= 1; } NSNumber *result = arrayA[0][1]; // 最後獲取到的二階矩陣的[0][1]即f(n)的值 //NSLog(@"fb4耗時:%lf", [now2 timeIntervalSinceNow]); return result.unsignedIntegerValue; }