题目

实验I:体验神奇的算法
本实验要求根据给定的正整数n计算第n个斐波那契数。课堂上已经讲过4种不同的算法,请选择自己最熟悉的编程语言实现这些算法来体验算法求解问题的神奇,下列基本要求必须完成:
1、 设计一个交互界面(例如菜单)供用户选择,如果可能,最好是一个图形化用户界面。
image.png
2、 利用迭代算法寻找不超过编程环境能够支持的最大整数的斐波那契数是第几个斐波那契数。(Java: 231-1 for int, 263-1 for long)

  1. java int最大值是:2147483647
  2. int 类型数占4个byte.
  3. 1byte=8bit
  4. 也就是有32bit占位符
  1. //找到超过int最大值之前的一个斐波拉契数字是第几个
  2. public static int findMax(){
  3. int index = 2;
  4. int first = 1,second = 1;
  5. int cur = 0;
  6. while(first + second > 0){
  7. cur = first + second;
  8. first = second;
  9. second = cur;
  10. index++;
  11. }
  12. System.out.println("没超过int的最大斐波那契数字为:"+cur+"位于"+index);
  13. return index;
  14. }

image.png
3、 根据第二步计算的最大的斐波那契数序号n,采用递归方式计算第n个斐波那契数,看其是否可以在1分钟内完成。

  1. case 2:
  2. if(maxIndex == -1){
  3. System.out.println("请至少先运行第一个功能再来试试吧");
  4. break;
  5. }
  6. //long start = new Date().getTime();
  7. long start = System.currentTimeMillis();
  8. recur(maxIndex);
  9. long end = System.currentTimeMillis();
  10. System.out.println("用了" + (end - start) / 1000 + "秒");
  11. break;
  1. //递归求解需要多少秒呢?
  2. public static int recur(int index){
  3. if(index == 1 || index == 2){
  4. return 1;
  5. }
  6. return recur(index - 1) + recur(index - 2);
  7. }

image.png
4、 利用递归算法计算你的计算机能够在30秒内计算出的最大斐波那契数是第几个,计算出下一个斐波那契数的时间是多少,利用迭代算法求解同样的问题。

  1. case 3:
  2. int timeIndex = timeSearchForRecur();
  3. long s = System.currentTimeMillis();
  4. recur(timeIndex + 1);
  5. long e = System.currentTimeMillis();
  6. System.out.println("第" + (timeIndex + 1)+ "位斐波那契数递归用了" + (e - s) / 1000 + "秒 (超过了30s3)");
  7. break;
  1. //找到30s内可以递归出来的最大斐波那契数列(递归)
  2. public static int timeSearchForRecur(){
  3. int index = 1;
  4. while(true){
  5. long cur = System.currentTimeMillis();
  6. recur(index);
  7. long end = System.currentTimeMillis();
  8. if(end - cur >= 30 * 1000){
  9. System.out.println("(递归)30s内最大的斐波那契数位于" + (index - 1));
  10. return index - 1;
  11. }
  12. index++;
  13. }
  14. }

image.png
5、 利用公式F(n) = [fn/sqrt(5)]快速计算第n个斐波那契数,找出出现误差时的最小n值。
image.png

  1. //公式求解快速计算出第n个斐波那契数字
  2. public static void mathForFib(int index){
  3. long result = 1;
  4. float temp = (float) Math.sqrt(5.0);
  5. result = (long)((Math.pow((1 + temp) / 2, index) - Math.pow((1 - temp) / 2, index)) / temp);
  6. System.out.println("第" + index + "位的" + result);
  7. }

image.png

  1. //公式求解快速计算出第n个斐波那契数字,找出出现误差最小的n值
  2. public static void mathFib(){
  3. int index = 1;
  4. long result = 1;
  5. float temp = (float) Math.sqrt(5.0);
  6. while(true){
  7. result = (long)((Math.pow((1 + temp) / 2, index) - Math.pow((1 - temp) / 2, index)) / temp);
  8. if(result != fib(index)){
  9. break;
  10. }
  11. index++;
  12. }
  13. System.out.println("出现误差的最小n值为第" + index + "位的" + result);
  14. }

image.png
6、 利用矩阵相乘方法计算第n个斐波那契数。
image.png
复杂度分析
时间复杂度:O(\log n)O(logn)。
空间复杂度:O(1)O(1)。
image.png

参考代码:http://blog.sina.com.cn/s/blog_6ea2c6a20100x359.html

  1. static long matrix[][] = {{1,1},{1,0}};
  2. // 矩阵方式求斐波拉契数列
  3. public static long matrixForFib(int index){
  4. if (index <= 0) {
  5. throw new RuntimeException("输入参数小于1");
  6. }
  7. if (index == 1 || index == 2) {
  8. return 1;
  9. }
  10. long[][] tem = matrix;
  11. for (int i = 1; i < index - 2; i++) {
  12. //已知一开始tmp和matrix然后迭代计算到最后
  13. tem = doMatrix(tem,matrix);
  14. }
  15. return tem[0][0] + tem[1][0];
  16. }
  17. public static long[][] doMatrix(long[][] a,long[][] b){
  18. long[][] res = new long[2][2];
  19. res[0][0] = a[0][0] * b[0][0] + a[0][1] * b[1][0];
  20. res[0][1] = a[0][0] * b[0][1] + a[0][1] * b[1][1];
  21. res[1][0] = a[1][0] * b[0][0] + a[1][1] * b[1][0];
  22. res[1][1] = a[1][0] * b[0][1] + a[1][1] * b[1][1];
  23. return res;
  24. }

image.png
7、 对于相同的输入n值,比较上述四种方法的基本操作次数,以掌握对数、线性和指数增长率的极大差别。

实现代码

  1. package week1;
  2. import java.util.Scanner;
  3. /**
  4. * @author newThread
  5. * @className Solution
  6. * @date 2021/10/15 16:10
  7. */
  8. class Solution {
  9. static int max = Integer.MAX_VALUE;
  10. //找到超过int最大值之前的一个斐波拉契数字是第几个
  11. public static int findMax(){
  12. int index = 2;
  13. int first = 1,second = 1;
  14. int cur = 0;
  15. while( first + second > 0){
  16. cur = first + second;
  17. first = second;
  18. second = cur;
  19. index++;
  20. }
  21. System.out.println("没超过int的最大斐波那契数字为:"+cur+"位于"+index);
  22. return index;
  23. }
  24. public static long fib(int index){
  25. if(index == 1 || index == 2){
  26. return 1;
  27. }
  28. long first = 1,second = 1;
  29. long cur = 0;
  30. index = index - 2;
  31. while(index > 0){
  32. cur = first + second;
  33. first = second;
  34. second = cur;
  35. index--;
  36. }
  37. return cur;
  38. }
  39. //递归求解需要多少秒呢?
  40. public static int recur(int index){
  41. if(index == 1 || index == 2){
  42. return 1;
  43. }
  44. return recur(index - 1) + recur(index - 2);
  45. }
  46. //找到30s内可以递归出来的最大斐波那契数列(递归)
  47. public static int timeSearchForRecur(){
  48. int index = 1;
  49. while(true){
  50. long cur = System.currentTimeMillis();
  51. recur(index);
  52. long end = System.currentTimeMillis();
  53. if(end - cur >= 30 * 1000){
  54. System.out.println("(递归)30s内最大的斐波那契数位于" + (index - 1));
  55. return index - 1;
  56. }
  57. index++;
  58. }
  59. }
  60. //找到30s内可以递归出来的最大斐波那契数列(递归)
  61. public static int timeSearchForDieDai(){
  62. int index = 1;
  63. while(true){
  64. long cur = System.currentTimeMillis();
  65. fib(index);
  66. long end = System.currentTimeMillis();
  67. if(end - cur >= 30 * 1000){
  68. System.out.println(end -cur);
  69. System.out.println("(迭代)30s内最大的斐波那契数位于" + (index - 1));
  70. return index - 1;
  71. }
  72. index++;
  73. }
  74. }
  75. //公式求解快速计算出第n个斐波那契数字,找出出现误差最小的n值
  76. public static void mathFib(){
  77. int index = 1;
  78. long result = 1;
  79. float temp = (float) Math.sqrt(5.0);
  80. while(true){
  81. result = (long)((Math.pow((1 + temp) / 2, index) - Math.pow((1 - temp) / 2, index)) / temp);
  82. if(result != fib(index)){
  83. break;
  84. }
  85. index++;
  86. }
  87. System.out.println("出现误差的最小n值为第" + index + "位的" + result);
  88. }
  89. //公式求解快速计算出第n个斐波那契数字
  90. public static void mathForFib(int index){
  91. long result = 1;
  92. float temp = (float) Math.sqrt(5.0);
  93. result = (long)((Math.pow((1 + temp) / 2, index) - Math.pow((1 - temp) / 2, index)) / temp);
  94. System.out.println("第" + index + "位的" + result);
  95. }
  96. static long matrix[][] = {{1,1},{1,0}};
  97. // 矩阵方式求斐波拉契数列
  98. public static long matrixForFib(int index){
  99. if (index <= 0) {
  100. throw new RuntimeException("输入参数小于1");
  101. }
  102. if (index == 1 || index == 2) {
  103. return 1;
  104. }
  105. long[][] tem = matrix;
  106. for (int i = 1; i < index - 2; i++) {
  107. //已知一开始tmp和matrix然后迭代计算到最后
  108. tem = doMatrix(tem,matrix);
  109. }
  110. return tem[0][0] + tem[1][0];
  111. }
  112. public static long[][] doMatrix(long[][] a,long[][] b){
  113. long[][] res = new long[2][2];
  114. res[0][0] = a[0][0] * b[0][0] + a[0][1] * b[1][0];
  115. res[0][1] = a[0][0] * b[0][1] + a[0][1] * b[1][1];
  116. res[1][0] = a[1][0] * b[0][0] + a[1][1] * b[1][0];
  117. res[1][1] = a[1][0] * b[0][1] + a[1][1] * b[1][1];
  118. return res;
  119. }
  120. public static void main(String[] args) {
  121. Scanner scanner = new Scanner(System.in);
  122. int maxIndex = -1;
  123. while(true){
  124. System.out.println("-------------------------实验I:体验神奇的算法----------------------------|\n");
  125. System.out.println("| 1.利用迭代算法找出不超过编程环境能够支持的最大整数的斐波那契数是第几个数。 | ");
  126. System.out.println("| 2.根据选项1的序号n,用递归计算第n个斐波那契数.看其是否可以在1分钟内完成。 | ");
  127. System.out.println("| 3.利用递归算法计算你的计算机能够在30秒内计算出的最大斐波那契数是第几个, |\n" +
  128. "| 并且计算出下一个斐波那契数的时间 |");
  129. System.out.println("| 4.利用公式计算第n个斐波那契数 | ");
  130. System.out.println("| 5.利用公式计算出现误差的最小斐波那契是第几个 | ");
  131. System.out.println("| 6.利用矩阵计算第n个斐波那契数 | ");
  132. System.out.println("| 7.退出系统 | ");
  133. System.out.println("|----------------------------------------------------------------------");
  134. System.out.println("请输入对应选项序号:");
  135. int choice = scanner.nextInt();
  136. switch (choice){
  137. case 1:
  138. maxIndex = findMax();
  139. break;
  140. case 2:
  141. if(maxIndex == -1){
  142. System.out.println("请至少先运行第一个功能再来试试吧");
  143. break;
  144. }
  145. //long start = new Date().getTime();
  146. long start = System.currentTimeMillis();
  147. recur(maxIndex);
  148. long end = System.currentTimeMillis();
  149. System.out.println("用了" + (end - start) / 1000 + "秒");
  150. break;
  151. case 3:
  152. int timeIndex = timeSearchForRecur();
  153. long s = System.currentTimeMillis();
  154. recur(timeIndex + 1);
  155. long e = System.currentTimeMillis();
  156. System.out.println("第" + (timeIndex + 1)+ "位斐波那契数递归用了" + (e - s) / 1000 + "秒 (超过了30s3)");
  157. break;
  158. case 4:
  159. System.out.println("您想求出第几位斐波那契数?");
  160. int index = scanner.nextInt();
  161. mathForFib(index);
  162. break;
  163. case 5:
  164. mathFib();
  165. break;
  166. case 6:
  167. System.out.println("您想求出第几位斐波那契数?");
  168. int matrixIndex = scanner.nextInt();
  169. long l = matrixForFib(matrixIndex);
  170. System.out.println("第"+ matrixIndex + "位的斐波那契数为" + l);
  171. break;
  172. case 7:
  173. return;
  174. default:
  175. System.out.println("请输入正确的选项");
  176. break;
  177. }
  178. }
  179. }
  180. }

1.矩阵求解斐波那契数列

1次平方阶

image.png
1
image.png
image.png
2.斐波那契数列原则
image.png
3.继续推演

image.png
image.png
image.png
image.png
结论:
image.png

2次平方阶

image.png
1.
image.png
2.
image.png
结论:
image.png

3次平方阶

image.png
1.
image.png
image.png
结论:
image.png

4次平方阶

image.png

1.
image.png
image.png
结论:
image.png
image.png

n次平方阶

image.png
1.
image.png
image.png
image.png
结论:
image.png
最终结论:

image.png =image.png=image.png
image.png
C语言代码:

  1. //通过矩阵相乘来计算
  2. long matx(int n)//利用矩阵相乘方法计算第n个斐波那契数 n-->索引值+1
  3. {
  4. //x=f0,y=f1,t=f2-->x=fn-1,y=fn,t=fn-1(推演下一次x的位置),p-->斐波那契数列的第几个,n是最终值,p是推演具体值(对应左上角fn+1)
  5. int x = 0, y = 1, t = 1, p = 2;
  6. //i是起始值:i=1,从第一个斐波那契数列开始
  7. int i;
  8. //temp1暂存fn-1的值
  9. //temp2暂存fn+1的值
  10. int temp1 = 0;
  11. int temp2 = 0;
  12. for (i = 1; i < n; i++) {
  13. //1.求矩阵右下角
  14. //fn-1的值赋值给temp1
  15. temp1 = x;
  16. //fn-2*0+fn-1*1=fn-1
  17. x = x * 0 + y * 1;
  18. //2.求矩阵左下角、右上角
  19. //1*fn-1+1*fn-2=fn
  20. y = temp1 * 1 + y * 1;
  21. //求矩阵左上角
  22. //fn+1的值暂存给temp2
  23. temp2 = t;
  24. //fn-1*0+fn=fn+1
  25. t = t * 0 + p * 1;
  26. //fn-1*1+fn*1=fn+1
  27. p = temp2 * 1 + p * 1;
  28. }
  29. return y;
  30. }

此题结构如下:
1、实验I:体验神奇的算法 - 图42
image.pngimage.pngimage.pngimage.png