2.4算法分析-运行时间计算

一般法则

  1. 一个 for 循环的运行时间至多是该for循环内部语句的运行时间乘以迭代次数
  2. 从里向外分析循环,一组嵌套循环内部语句的运行时间为该语句的运行时间乘以该组所有的 for 循环的大小的乘积。
  3. 将各顺序语句的运行时间求和
  4. if/else语句 不超过判断的运行时间加上,两部分中运行时间长者

下方代码表面是递归实际上是一个简单的循环,从而其运行时间为O(N)

   public static long factorial(int n) {
         if (n <= 1) {
             return 1;
         } else {
             return n * factorial(n - 1);
         }
     }

递归正常使用时,将其转换成一个循环结构是很困难的。

     public long fib(int n) {
         if (n <= 1) {
             return 1;
         } else {
             return fib(n - 1) + fib(n - 2);
         }
     }
 
     public long arr(int n) {
         long[] array = new long[n + 1];
         if (n <= 1) {
             return 1;
         } else {
             array[0] = 1;
             array[1] = 1;
             for (int i = 2; i <= n; i++) {
                 array[i] = array[i - 1] + array[i - 2];
             }
             return array[n];
         }
     }

以上例子中同样是计算斐波那契数列第n项
使用数学归纳法易证得 \(fib(n) = \Omega((3/2)^n) (n>4) ,fib(n) = O((5/3)^n)\) , 而 \(arr(n) = O(n)\),显然 \(fib(n)\) 需要花更多的时间


具有对数特点的算法规律:如果一个算法用常数时间(O(1))将问题的大小削减为其一部分(通常是1/2,且没有重复计算的部分),那么该算法就是 O(log N)。另一方面,如果使用常数时间只是把问题减少一个常数的数量(如将问题减少1),那么这种算法就是O(N)的。

我们谈到O(log N)算法时,通常假设输入数据已经提前读入,否则将数据读入的时间就必须耗费Ω(N)的时间量。

以下是具有对数特点的三个例子。

一、折半查找

给定一个整数 \(X\) 和整数 \(A_0 ,A_1 ,\ldots, A_{N-1}\) ,后者已经预先排序并在内存汇总,求下标 \(i\) 使得 \(A_i = X\),如果 \(X\) 不在数据中,则返回 \(I = -1\)

首先验证 \(X\) 是否居中的元素。如果是,答案找到。如果 \(X\) 小于居中元素,那么可以应用同样的策略于居中元素左边已排序的子序列;同理如果 \(X\) 大于居中元素,可以检查数据右半部分。

     int binarySearch(int[] a, int x) {
         int low = 0;
         int high = a.length - 1;
         while (low <= high) {
             int mid = (low + high) / 2;
             if (a[mid] < x) {
                 low = mid + 1;
             } else if (a[mid] > x) {
                 high = mid - 1;
             } else {
                 return mid;
             }
         }
         return -1;
     }

运行时间是 \(O(log)\)

二、欧几里得算法

计算两个数的最大公约数

public long gcd (long m, long n){
         while (n != 0){
             long temp = m%n;
             m = n;
             n = temp;
         }
         return m;
     }

在两次迭代后,余数最多是原始值的一半。因此迭代次数最多是 2logN = O(logN)

欧几里得算法平均情况下的性能需要大量篇幅的高度复杂的数学分析,其迭代的平均次数约为 \((12\cdot ln2\cdot lnN) / \pi ^2 + 1.47\)

定理2.1 如果 \(M > N\) ,则 \(M\ mod\ < M / 2\)

三、幂运算

计算某整数的幂

     public long pow(long x, int n) {
         if (n == 0) {
             return 1;
         }
         if (n == 1) {
             return x;
         }
         if (n % 2 == 0) {
             return pow(x * x, n / 2);
         } else {
             return pow(x * x, n / 2) * x;
         }
     }

计算某整数的幂

计算 \(X^N\) 可以使用递归算法,若 \(N\) 为偶数,\(X^N = X^{N/2} \times X^{N/2}\) ;若 \(N\) 为奇数,\(X^N = X^{(N-1)/2} \times X^{(N-1)/2} \times X\)

运行时间同样为 \(O(logN)\)

posted @ 2020-07-18 20:41  PotatoTed  阅读(375)  评论(0)    收藏  举报