本篇文章解決的問題來源於算法設計與分析課程的課堂做業,主要是運用多種方法來計算斐波那契數。具體問題及解法以下:html
問題描述:利用迭代算法尋找不超過編程環境可以支持的最大整數的斐波那契數是第幾個斐波那契數。(Java: 231-1 for int, 263-1 for long)java
解決方案:針對問題1,此處要使用迭代法來解決,具體實現代碼以下:算法
//用迭代法尋找編程環境支持的最大整數(int型)的斐波那契數是第幾個斐波那契數 public static int max_int_iteration(){ int a = 1,b = 1,c = 2; int count = 3; for( ;b < c; ){ //一旦c達到編程環境最大斐波那契數,便會產生內存溢出,從而變成一個負數,到此循環結束 a = b; b = c; c = a + b; count++; } return count; } //用迭代法尋找編程環境支持的最大整數(long型)的斐波那契數是第幾個斐波那契數 public static long max_long_iteration(){ long a = 1,b = 1,c = 2; long count = 3; for( ;b<c; ){ //一旦c達到編程環境最大斐波那契數,便會產生內存溢出,從而變成一個負數,到此循環結束 a = b; b = c; c = a + b; count++; } return count; }
問題描述:根據問題1中計算的最大的斐波那契數序號n,採用遞歸方式計算第n個斐波那契數,看其是否能夠在3分鐘內完成。編程
解決方案:針對問題2,此處要使用遞歸法來解決,問題1實際運行結果爲:支持的最大整數(int型)值爲47,支持的最大整數(long型)值爲93。使用遞歸法計算第47個斐波那契數實際所需時間爲138秒左右(此處本人使用WIN7系統運行所得),具體實現代碼以下:數組
//遞歸法 public static long recursion(long n){ long result = 0; //最後一個斐波那契數及存儲中間斐波那契數的變量 if(n <= 0) result = 0; if(n == 1 || n == 2) result = 1; if(n > 2) { result = recursion(n-1) + recursion(n-2); //System.out.print(result+" "); } return result; } //具體實現就只需使用System.currentTimeMillis()方法獲取系統當前時間,就能夠你計算出計算第47個斐波那契數所需時間
問題描述:利用遞歸算法計算你的計算機可以在1,5,10,50秒內計算出的最大斐波那契數是第幾個,利用迭代算法求解一樣的問題。ide
解決方案:針對問題3,此處先要了解得到系統當前時間的相關方法及用法,而後計算規定時間內計算出的最大斐波那契數就很簡單啦,具體實現代碼以下:post
//在1,5,10,50秒內使用迭代法算出的最大斐波那契數是第幾個 public static void time_iteration(){ int a = 1,b = 1,c = 2; long count = 3; long a1 = 0,a2 = 0,a3 = 0,a4 = 0; long t1 = System.currentTimeMillis(); long t2 = System.currentTimeMillis(); for( ;t2-t1 < 60000; ){ a = b; b = c; c = a + b; count++; t2 = System.currentTimeMillis(); if(t2-t1 == 1000) a1 = count; //System.out.println("1秒內最大斐波那契數是第:"+count+"個 "); if(t2-t1 == 5000) a2 = count; //System.out.println("5秒內最大斐波那契數是第:"+count+"個 "); if(t2-t1 == 10000) a3 = count; //System.out.println("10秒內最大斐波那契數是第:"+count+"個 "); if(t2-t1 == 50000) a4 = count; //System.out.println("50秒內最大斐波那契數是第:"+count+"個 "); } System.out.println("迭代法1秒內最大斐波那契數是第:"+a1+"個 "); System.out.println("迭代法5秒內最大斐波那契數是第:"+a2+"個 "); System.out.println("迭代法10秒內最大斐波那契數是第:"+a3+"個 "); System.out.println("迭代法50秒內最大斐波那契數是第:"+a4+"個 "); } //遞歸法 public static long recursion(long n){ long result = 0; //最後一個斐波那契數及存儲中間斐波那契數的變量 if(n <= 0) result = 0; if(n == 1 || n == 2) result = 1; if(n > 2) { result = recursion(n-1) + recursion(n-2); //System.out.print(result+" "); } return result; } //規定時間內,遞歸法計算出的最大斐波那契數是第幾個 public static int recursion_time(long time){ long starttime_dg=System.currentTimeMillis(); int i=3; long endtime_dg=0; while(endtime_dg<starttime_dg+time*1000){ endtime_dg=System.currentTimeMillis(); i++; recursion(i); } return i; } //遞歸法在1,5,10,50秒內算出的最大斐波那契數是第幾個 public static void fbnq_recursion_time(){ System.out.println("1秒內最大斐波那契數是第:"+recursion_time(1)+"個 "); System.out.println("5秒內最大斐波那契數是第:"+recursion_time(5)+"個 "); System.out.println("10秒內最大斐波那契數是第:"+recursion_time(10)+"個 "); System.out.println("50秒內最大斐波那契數是第:"+recursion_time(50)+"個 "); }
問題描述:利用公式F(n) = [fn/sqrt(5)]快速計算第n個斐波那契數,找出出現偏差時的最小n值。其具體公式描述見下圖:測試
解決方案:針對問題4,只須要將上述公式用代碼來描述就能夠完成,具體實現代碼以下:url
//直接求值法(利用公式F(n) = [@n/sqrt(5)]快速計算第n個斐波那契數) public static double formula(int n){ double result = 0; double temp = Math.sqrt(5.0); result = (1/temp)*(Math.pow((1+temp)/2,n)-Math.pow((1-temp)/2, n)); return result; }
問題描述:利用矩陣相乘方法計算第n個斐波那契數。spa
解決方案:對於矩陣相乘法,首先得對矩陣相乘的法則要熟悉,現簡單說明一下矩陣相乘求斐波那契數的思想及原理:
(1)矩陣定義
進一步,能夠得出直接推導公式:
具體實現代碼以下:
// 關聯矩陣 private static final int[][] UNIT = { { 1, 1 }, { 1, 0 } }; //定義一個上面公式中須要計算的二維數組 // 全0矩陣 private static final int[][] ZERO = { { 0, 0 }, { 0, 0 } }; //定義一個元素均爲0的二維數組 /** * 求斐波那契數列 * * @param n * @return */ public static int[][] fb(int n) { if (n == 0) { //指數n爲0時返回該數組 return ZERO; } if (n == 1) { //指數n爲1時返回該數組 return UNIT; } // n是偶數 if ((n & 1) == 0) { //把(n&1) == 0換成(n%2) == 0等價 , 惟一區別在於(n&1) == 0計算效率高 int[][] matrix = fb(n >> 1); //n >> 1意思是指將n的二進制數向右移動1位,最高位補0。至關於把n除以2 return matrixMultiply(matrix, matrix); } // n是奇數 int[][] matrix = fb((n - 1) >> 1); return matrixMultiply(matrixMultiply(matrix, matrix), UNIT); } /** * 矩陣相乘 * * @param m * r1*c1 * @param n * c1*c2 * @return 新矩陣,r1*c2 */ public static int[][] matrixMultiply(int[][] m, int[][] n) { int rows = m.length; int cols = n[0].length; int[][] r = new int[rows][cols]; for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { r[i][j] = 0; for (int k = 0; k < m[i].length; k++) { r[i][j] += m[i][k] * n[k][j]; } } } return r; } //具體實現矩陣相乘算法 public static int matrix(int n){ int[][] m = fb(n); return m[0][1]; }
問題描述:對於相同的輸入n值,比較上述四種方法的基本操做次數,以掌握對數、線性和指數增加率的極大差異。
解決方案:此處本人只是把上述所用的四種方法運行了一遍,計算各個相應方案計算第n個斐波那契數所花時間,由於具體算法相應的基本操做次數,用代碼來計算有點麻煩,因此此處就不羅列了。
這是解決斐波那契數的一種新算法,該算法的時間和空間效率都優於上面四種算法,該算法所用公式以下圖所示:
具體實現代碼以下:
//新算法法 public static int new_way(int n){ //int a = 1,b = 1,c = 2,d = 3; int result = 0; //定義最後一個斐波那契數 //根據輸入n,求出最後一個斐波那契數 if(n == 0) result = 0; else if(n == 1 || n == 2) result = 1; else if(n == 3) result = 2; else if(n >= 4){ //若n大於4返回resul int a1 = n/4; int b1 = n%4; int a = new_way(a1); int b = new_way((a1+1)); int c = new_way((a1-1)); int d = new_way((a1+2)); if(b1 == 0) result = (int) ((Math.pow(b,2) - Math.pow(c,2))*(Math.pow(c, 2) + 2*Math.pow(a, 2) + Math.pow(b,2))); if(b1 == 1) result = (int) (Math.pow((Math.pow(b,2) - Math.pow(c,2)),2) + Math.pow((Math.pow(a, 2) + Math.pow(b,2)),2)); if(b1 == 2) result = (int) ((Math.pow(a, 2) + Math.pow(b,2))*(3*Math.pow(b,2)+Math.pow(a, 2)-2*Math.pow(c,2))); if(b1 == 3) result = (int) (Math.pow((Math.pow(a, 2) + Math.pow(b,2)),2) + Math.pow((Math.pow(d,2)-Math.pow(a,2)),2)); } return result; }
代碼以下:
1 package com.liuzhen.ex_one; 2 3 public class Fibonacci { 4 5 //迭代法 6 public static int iteration(int n){ /*此處(包含下面全部方法)聲明爲靜態方法,緣由是在本類main()方法中調用 7 類中方法,對於通常的非static成員變量或方法,須要有一個對象的實例才能調用,因此要先生成對象的實例,他們纔會實際的分配內存空間。 8 而對於static的對象或方法,在程序載入時便已經分配了內存空間,他只和特定的類想關聯,無需實例化 */ 9 int result = 1; //最後一個斐波那契數 10 int a[] = new int[n+1]; //存放斐波那契數,初始值爲空,默認全爲0 11 a[0] = 0; 12 a[1] = 1; 13 //System.out.println("迭代法計算斐波那契數結果:"); 14 //System.out.print(a[0]+" "+a[1]+" "); 15 for(int i = 2;i < n+1;i++){ 16 a[i] = a[i-1] + a[i-2]; 17 //result = a[i]; 18 //System.out.print(result+" "); //打印斐波那契數 19 } 20 //System.out.println(); 21 result=a[n]; 22 return result; //返回最後一個斐波那契數 23 } 24 25 //用迭代法尋找編程環境支持的最大整數(int型)的斐波那契數是第幾個斐波那契數 26 public static int max_int_iteration(){ 27 int a = 1,b = 1,c = 2; 28 int count = 3; 29 for( ;b < c; ){ //一旦c達到編程環境最大斐波那契數,便會產生內存溢出,從而變成一個負數,到此循環結束 30 a = b; 31 b = c; 32 c = a + b; 33 count++; 34 } 35 return count; 36 } 37 38 39 //用迭代法尋找編程環境支持的最大整數(long型)的斐波那契數是第幾個斐波那契數 40 public static long max_long_iteration(){ 41 long a = 1,b = 1,c = 2; 42 long count = 3; 43 for( ;b<c; ){ //一旦c達到編程環境最大斐波那契數,便會產生內存溢出,從而變成一個負數,到此循環結束 44 a = b; 45 b = c; 46 c = a + b; 47 count++; 48 } 49 return count; 50 } 51 52 //在1,5,10,50秒內使用迭代法算出的最大斐波那契數是第幾個 53 public static void time_iteration(){ 54 int a = 1,b = 1,c = 2; 55 long count = 3; 56 long a1 = 0,a2 = 0,a3 = 0,a4 = 0; 57 long t1 = System.currentTimeMillis(); 58 long t2 = System.currentTimeMillis(); 59 for( ;t2-t1 < 60000; ){ 60 a = b; 61 b = c; 62 c = a + b; 63 count++; 64 t2 = System.currentTimeMillis(); 65 if(t2-t1 == 1000) 66 a1 = count; 67 //System.out.println("1秒內最大斐波那契數是第:"+count+"個 "); 68 if(t2-t1 == 5000) 69 a2 = count; 70 //System.out.println("5秒內最大斐波那契數是第:"+count+"個 "); 71 if(t2-t1 == 10000) 72 a3 = count; 73 //System.out.println("10秒內最大斐波那契數是第:"+count+"個 "); 74 if(t2-t1 == 50000) 75 a4 = count; 76 //System.out.println("50秒內最大斐波那契數是第:"+count+"個 "); 77 } 78 System.out.println("迭代法1秒內最大斐波那契數是第:"+a1+"個 "); 79 System.out.println("迭代法5秒內最大斐波那契數是第:"+a2+"個 "); 80 System.out.println("迭代法10秒內最大斐波那契數是第:"+a3+"個 "); 81 System.out.println("迭代法50秒內最大斐波那契數是第:"+a4+"個 "); 82 } 83 84 //遞歸法 85 public static long recursion(long n){ 86 long result = 0; //最後一個斐波那契數及存儲中間斐波那契數的變量 87 if(n <= 0) 88 result = 0; 89 if(n == 1 || n == 2) 90 result = 1; 91 if(n > 2) 92 { 93 result = recursion(n-1) + recursion(n-2); 94 //System.out.print(result+" "); 95 } 96 return result; 97 } 98 99 //規定時間內,遞歸法計算出的最大斐波那契數是第幾個 100 public static int recursion_time(long time){ 101 long starttime_dg=System.currentTimeMillis(); 102 int i=3; 103 long endtime_dg=0; 104 while(endtime_dg<starttime_dg+time*1000){ 105 endtime_dg=System.currentTimeMillis(); 106 i++; 107 recursion(i); 108 } 109 return i; 110 } 111 112 //遞歸法在1,5,10,50秒內算出的最大斐波那契數是第幾個 113 public static void fbnq_recursion_time(){ 114 115 System.out.println("1秒內最大斐波那契數是第:"+recursion_time(1)+"個 "); 116 117 System.out.println("5秒內最大斐波那契數是第:"+recursion_time(5)+"個 "); 118 119 System.out.println("10秒內最大斐波那契數是第:"+recursion_time(10)+"個 "); 120 121 System.out.println("50秒內最大斐波那契數是第:"+recursion_time(50)+"個 "); 122 123 124 } 125 126 //測試遞歸法在1,5,10,50秒內使用迭代法算出的最大斐波那契數是第幾個 127 public static void time_recursion_test(){ 128 long t1 = System.currentTimeMillis(); 129 long t2 = 0; 130 int i = 3; 131 for(;t2-t1 > 60000;){ 132 recursion(i); 133 i++; 134 t2 = System.currentTimeMillis(); 135 if(t2-t1 == 1000) 136 System.out.println("1秒內最大斐波那契數是第:"+i+"個 "); 137 if(t2-t1 == 5000) 138 System.out.println("5秒內最大斐波那契數是第:"+i+"個 "); 139 if(t2-t1 == 10000) 140 System.out.println("10秒內最大斐波那契數是第:"+i+"個 "); 141 if(t2-t1 == 50000) 142 System.out.println("50秒內最大斐波那契數是第:"+i+"個 "); 143 144 } 145 } 146 147 //直接求值法(利用公式F(n) = [@n/sqrt(5)]快速計算第n個斐波那契數) 148 public static double formula(int n){ 149 double result = 0; 150 double temp = Math.sqrt(5.0); 151 result = (1/temp)*(Math.pow((1+temp)/2,n)-Math.pow((1-temp)/2, n)); 152 return result; 153 } 154 155 156 //利用直接求值法,出現偏差時最小的n值 157 public static int min_formula(){ 158 double result_fn=1; 159 int i=1; 160 while(result_fn-(double)iteration(i)<1){ 161 result_fn=formula(i); 162 i++; 163 } 164 return i; 165 } 166 167 //新算法法 168 public static int new_way(int n){ 169 //int a = 1,b = 1,c = 2,d = 3; 170 int result = 0; //定義最後一個斐波那契數 171 //根據輸入n,求出最後一個斐波那契數 172 if(n == 0) 173 result = 0; 174 else if(n == 1 || n == 2) 175 result = 1; 176 else if(n == 3) 177 result = 2; 178 else if(n >= 4){ //若n大於4返回resul 179 int a1 = n/4; 180 int b1 = n%4; 181 int a = new_way(a1); 182 int b = new_way((a1+1)); 183 int c = new_way((a1-1)); 184 int d = new_way((a1+2)); 185 if(b1 == 0) 186 result = (int) ((Math.pow(b,2) - Math.pow(c,2))*(Math.pow(c, 2) + 2*Math.pow(a, 2) + Math.pow(b,2))); 187 if(b1 == 1) 188 result = (int) (Math.pow((Math.pow(b,2) - Math.pow(c,2)),2) + Math.pow((Math.pow(a, 2) + Math.pow(b,2)),2)); 189 if(b1 == 2) 190 result = (int) ((Math.pow(a, 2) + Math.pow(b,2))*(3*Math.pow(b,2)+Math.pow(a, 2)-2*Math.pow(c,2))); 191 if(b1 == 3) 192 result = (int) (Math.pow((Math.pow(a, 2) + Math.pow(b,2)),2) + Math.pow((Math.pow(d,2)-Math.pow(a,2)),2)); 193 194 } 195 return result; 196 } 197 198 199 // 關聯矩陣 200 private static final int[][] UNIT = { { 1, 1 }, { 1, 0 } }; 201 // 全0矩陣 202 private static final int[][] ZERO = { { 0, 0 }, { 0, 0 } }; 203 /** 204 * 求斐波那契數列 205 * 206 * @param n 207 * @return 208 */ 209 public static int[][] fb(int n) { 210 if (n == 0) { 211 return ZERO; 212 } 213 if (n == 1) { 214 return UNIT; 215 } 216 // n是奇數 217 if ((n & 1) == 0) { 218 int[][] matrix = fb(n >> 1); 219 return matrixMultiply(matrix, matrix); 220 } 221 // n是偶數 222 int[][] matrix = fb((n - 1) >> 1); 223 return matrixMultiply(matrixMultiply(matrix, matrix), UNIT); 224 } 225 226 /** 227 * 矩陣相乘 228 * 229 * @param m 230 * r1*c1 231 * @param n 232 * c1*c2 233 * @return 新矩陣,r1*c2 234 */ 235 public static int[][] matrixMultiply(int[][] m, int[][] n) { 236 int rows = m.length; 237 int cols = n[0].length; 238 int[][] r = new int[rows][cols]; 239 for (int i = 0; i < rows; i++) { 240 for (int j = 0; j < cols; j++) { 241 r[i][j] = 0; 242 for (int k = 0; k < m[i].length; k++) { 243 r[i][j] += m[i][k] * n[k][j]; 244 } 245 } 246 } 247 return r; 248 } 249 250 //具體實現矩陣相乘算法 251 public static int matrix(int n){ 252 int[][] m = fb(n); 253 return m[0][1]; 254 } 255 256 public static void main(String[] args){ 257 System.out.print(max_int_iteration()); 258 System.out.println(); 259 System.out.print(max_long_iteration()); 260 System.out.println(); 261 System.out.println(); 262 long t1 = System.currentTimeMillis(); 263 long a = recursion(47); 264 long t2 = System.currentTimeMillis(); 265 System.out.println("遞歸法求斐波那契數:"); 266 System.out.println(a); 267 System.out.println("遞歸算法Time is: " + (t2 - t1)/1000.0+"秒"); 268 269 //此處下面能夠直接經過給相關類傳遞參數,實現相應功能,上面的中代碼僅僅提供示例 270 } 271 272 }
PS:運行部分結果截圖
對於本問題,我本身用安卓作了一個簡單的展現界面(具體介紹請參考個人另外一篇博客:用安卓實現斐波那契數和最近點對問題):
參考資料:
一、斐波那契數列解析