對於算法的運行時間表達式\(T(n)=30n^4+20n^3+40n^2+46n+100\),當問題規模足夠大的時候如n=100萬,則算法的運行時間主要取決於表達式的第一項,因而,運行時間能夠記爲:\(T(n) \approx n^4\),即\(T(n)= \theta(n^4)\)。html
\(\theta\)的數學含義在於:對於全部的\(n>n_0\)時,函數f(n)乘上一個常量因子可等於g(n),則稱g(n)是f(n)的一個漸進精確界。g(n)便可以表示上界也能夠表示下界。算法
定義:設f(n)和g(n)是定義域爲天然數集N上的函數。若存在正數c和\(n_0\),使得對一切\(n \geq n_0\)都有0≤f(n)≤cg(n)成立,則稱f(n)的漸進的上界是g(n),記做f(n)=O(g(n))。通俗的說n知足必定條件範圍內,函數f(n)的階不高於函數g(n)。數組
定義:設f(n)和g(n)是定義域爲天然數集N上的函數。若存在正數c和\(n_0\),使得對一切\(n \geq n_0\)都有0≤cg(n)≤f(n)成立,則稱f(n)的漸進的下界是g(n),記做f(n)=Ω(g(n))。通俗的說n知足必定條件範圍內,函數f(n)的階不低於函數g(n)。app
定義1:設f(n)和g(n)是定義域爲天然數集N上的函數。若對於任意正數c,都存在\(n_0\),使得對一切\(n \geq n_0\)都有0≤f(n)<cg(n)成立,則稱f(n)的漸進的非緊確上界是g(n),記做f(n)=o(g(n))。通俗的說n知足必定條件範圍內,函數f(n)的階低於函數g(n)。
定義2:設f(n)和g(n)是定義域爲天然數集合的函數。若是\(\lim_{x\to \infty} \frac{f(n)}{g(n)}=0\),那麼f(n)=o(g(n))。通俗理解爲f(n)低於g(n)的階。dom
定義1:設f(n)和g(n)是定義域爲天然數集N上的函數。若對於任意正數c,都存在\(n_0\),使得對一切\(n \geq n_0\)都有0≤cg(n)<f(n)成立,則稱f(n)的漸進的非緊確下界是g(n),記做f(n)=ω(g(n))。通俗的說n知足必定條件範圍內,函數f(n)的階高於函數g(n)。
定義2:設f(n)和g(n)是定義域爲天然數集合的函數。若是\(\lim_{x\to \infty} \frac{f(n)}{g(n)}=\infty\),那麼f(n)=o(g(n))。通俗理解爲f(n)高於g(n)的階。ide
ω(g(n))={ f(n): 對任意正常數c,存在常數n0>0,使對全部的n>=n0,有0<=cg(n)<f(n) }
例子:對於O來說,它是用來表示上界的,當用它做爲算法的最壞狀況運行時間的上界時,就有對任意輸入的運行時間的上界。咱們說「一個算法A的運行時間爲\(O(n^2)\),它表示的是說該算法運行時間的一個上界,適用於每一個輸入的運行時間。spa
package com.JackeyZz.day2; class Result{ int low; int high; int sum; } public class divide { public static void main(String[] args) { int n=(int) (Math.random()*10+1); int[] arr=new int[n]; System.out.println("產生的"+n+"個隨機數以下:"); for(int i=0;i<n;i++){ int x=(int) (Math.random()*10+1); arr[i]=(int) ((int) (Math.random()*100)*Math.pow(-1, x)); System.out.print(arr[i]+" "); } System.out.println(""); System.out.println("輸出的最大子數組爲:"); //int[] arr1={2,-1,8,9,-5,10,5,1,-8,4,-5,-6,-3,7}; //int[] arr1={5,-7,3,4,5,6,8,-10,-12}; Result result=new Result(); //result=find_max_crossing_subarray(arr1, 0, 6, arr1.length-1); result=find_maximum_subarray(arr, 0, arr.length-1); System.out.println(result.low+" "+result.high+" "+result.sum); } public static Result find_max_crossing_subarray(int[] arr, int low,int mid,int high){ Result result = new Result(); int left_sum=(int) Double.NEGATIVE_INFINITY; int right_sum=(int) Double.NEGATIVE_INFINITY; int sum = 0; for(int i=mid;i>=low;i--){ sum=sum+arr[i]; if(sum>left_sum){ left_sum=sum; result.low=i; } } sum=0; for(int j=mid+1;j<=high;j++){ sum=sum+arr[j]; if(sum>right_sum){ right_sum=sum; result.high=j; } } result.sum=left_sum+right_sum; return result; } public static Result find_maximum_subarray(int[] arr,int low,int high){ int mid; Result left_result = new Result(); Result right_result = new Result(); Result cross_result = new Result(); if(high==low){ left_result.low=low; left_result.high=low; left_result.sum=arr[low]; return left_result; } else{ mid=(low+high)/2; left_result=find_maximum_subarray(arr, low, mid); right_result=find_maximum_subarray(arr, mid+1, high); cross_result=find_max_crossing_subarray(arr, low, mid, high); if(left_result.sum>=right_result.sum && left_result.sum>=cross_result.sum){ return left_result; } else if(right_result.sum>=left_result.sum && right_result.sum>=cross_result.sum){ return right_result; } else{ return cross_result; } } } }
很容易就知道數組A的任何連續子數組A[i..j]必然處於如下三種狀況:.net
遞歸流程
程序先執行left遞歸,直至知足low==high,返回left_result,接着執行right,當即返回right_result,可能會執行cross,返回cross_result,最後比較三者大小,返回最大值。(這是left遞歸最底層的執行過程)接着返回到left的上一層遞歸,此時的left_result就是剛剛比較的最大值,緊接着執行這一層的right,此時須要執行right遞歸,最後返回這一層的right_result(一樣是比較的最大值),可能也要執行cross返回cross_result,最後再比較三者大小並返回left的再上一層遞歸。如此循環,最後返回的就是整個數組的最大連續子數組。code
package com.JackeyZz.day2; public class divide1 { public static void main(String[] args) { int[][] A=new int[4][4]; int[][] B=new int[4][4]; System.out.println("產生的8*8矩陣A:"); for(int i=0;i<A.length;i++){ for(int j=0;j<A[0].length;j++){ A[i][j]=(int) (Math.random()*10+1); System.out.print(A[i][j]+" "); } System.out.println(""); } System.out.println("產生的8*8矩陣B:"); for(int i=0;i<B.length;i++){ for(int j=0;j<B[0].length;j++){ B[i][j]=(int) (Math.random()*10+1); System.out.print(B[i][j]+" "); } System.out.println(""); } System.out.println("暴力法矩陣相乘結果:"); int[][] result1=new int[4][4]; result1=Matrix_multiply(A, B, 4); for(int i=0;i<4;i++){ for(int j=0;j<4;j++){ System.out.print(result1[i][j]+" "); } System.out.println(""); } System.out.println("分治法矩陣相乘結果:"); int[][] result=null; result=square_matrix_multiply_recursive(A, B, 4); for(int i=0;i<result.length;i++){ for(int j=0;j<result[0].length;j++){ System.out.print(result[i][j]+" "); } System.out.println(""); } } //暴力求解矩陣相乘 public static int[][] Matrix_multiply(int[][] A,int[][] B,int n){ int[][] result=new int[n][n]; for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ result[i][j]=0; for(int k=0;k<n;k++){ result[i][j]+=A[i][k]*B[k][j]; } } } return result; } //遞歸求解矩陣乘法 public static int[][] square_matrix_multiply_recursive(int[][] A, int[][] B,int n){ int[][] result=new int[n][n]; if(n==2){ result=Matrix_multiply(A, B, n); return result; } if(n>2){ int m=n/2; int[][] A11=QuarterMatrix(A, n, 1); int[][] A12=QuarterMatrix(A, n, 2); int[][] A21=QuarterMatrix(A, n, 3); int[][] A22=QuarterMatrix(A, n, 4); int[][] B11=QuarterMatrix(B, n, 1); int[][] B12=QuarterMatrix(B, n, 2); int[][] B21=QuarterMatrix(B, n, 3); int[][] B22=QuarterMatrix(B, n, 4); int[][] result11=QuarterMatrix(result, n, 1); int[][] result12=QuarterMatrix(result, n, 2); int[][] result21=QuarterMatrix(result, n, 3); int[][] result22=QuarterMatrix(result, n, 4); //result=square_matrix_multiply_recursive(A11, B11, m); result11=AddMatrix(square_matrix_multiply_recursive(A11, B11, m), square_matrix_multiply_recursive(A12, B21, m), m); result12=AddMatrix(square_matrix_multiply_recursive(A11, B12, m), square_matrix_multiply_recursive(A12, B22, m), m); result21=AddMatrix(square_matrix_multiply_recursive(A21, B11, m), square_matrix_multiply_recursive(A22, B21, m), m); result22=AddMatrix(square_matrix_multiply_recursive(A21, B12, m), square_matrix_multiply_recursive(A22, B22, m), m); result=TogetherMatrix(result11, result12, result21, result22, m); } return result; } //獲取矩陣的四分之一,並返回子矩陣 public static int[][] QuarterMatrix(int[][] P,int n,int num){ int row=n/2; int cols=n/2; int[][] result=new int[row][cols]; switch(num){ case 1:{ for(int i=0;i<row;i++){ for(int j=0;j<cols;j++){ result[i][j]=P[i][j]; } } break; } case 2:{ for(int i=0;i<row;i++){ for(int j=0;j<n-cols;j++){ result[i][j]=P[i][j+cols]; } } break; } case 3:{ for(int i=0;i<n-row;i++){ for(int j=0;j<cols;j++){ result[i][j]=P[i+row][j]; } } break; } case 4:{ for(int i=0;i<n-row;i++){ for(int j=0;j<n-cols;j++){ result[i][j]=P[i+row][j+cols]; } } break; } default: break; } return result; } //矩陣相加 public static int[][] AddMatrix(int[][] A,int[][] B,int n){ int[][] result=new int[n][n]; for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ result[i][j]=A[i][j]+B[i][j]; } } return result; } //整合矩陣 public static int[][] TogetherMatrix(int[][] a,int[][] b, int[][] c,int[][] d,int n){ int[][] result=new int[2*n][2*n]; for(int i=0;i<2*n;i++){ for(int j=0;j<2*n;j++){ if(i<n){ if(j<n){ result[i][j]=a[i][j]; } else{ result[i][j]=b[i][j-n]; } } else{ if(j<n){ result[i][j]=c[i-n][j]; } else{ result[i][j]=d[i-n][j-n]; } } } } return result; } }
Strassen算法的核心思想是令遞歸樹稍微不那麼茂盛一點,即只遞歸進行7次而不是8次n/2*n/2矩陣的乘法,所以前兩種算法的運行時間是\(O(n^3)\),而Strassen算法的運行時間是\(O(n^{2.81})\)。
Strassen算法的公式以下:
\(S_1=B_{12}-B_{22}\) \(S_2=A_{11}+A_{12}\) \(S_3=A_{21}+A_{22}\)
\(S_4=B_{21}-B_{11}\) \(S_5=A_{11}+A_{22}\) \(S_6=B_{11}+B_{22}\)
\(S_7=A_{12}-A_{22}\) \(S_8=B_{21}+B_{22}\) \(S_9=A_{11}-A_{21}\)
\(S_{10}=B_{11}+B_{12}\)
\(P_1=A_{11}*S_1\) \(P_2=S_{2}*B_{22}\) \(P_3=S_3*B_{11}\) \(P_4=A_{22}*S_4\) \(P_5=S_5*S_6\) \(P_6=S_7*S_8\) \(P_7=S_9*S_{10}\)
\(C_{11}=P_5+P_4-P_2+P_6\) \(C_{12}=P_1+P_2\) \(C_{21}=P_3+P_4\) \(C_{22}=P_5+P_1-P_3-P_7\)
實現的代碼以下:
package com.JackeyZz.day2; public class divide2 { public static void main(String[] args) { int[][] A=new int[4][4]; int[][] B=new int[4][4]; System.out.println("產生隨機矩陣A:"); for(int i=0;i<A.length;i++){ for(int j=0;j<A[0].length;j++){ A[i][j]=(int) (Math.random()*10+1); System.out.print(A[i][j]+" "); } System.out.println(""); } System.out.println("產生的隨機矩陣B:"); for(int i=0;i<B.length;i++){ for(int j=0;j<B[0].length;j++){ B[i][j]=(int) (Math.random()*10+1); System.out.print(B[i][j]+" "); } System.out.println(""); } System.out.println("暴力法矩陣相乘結果:"); int[][] result1=new int[4][4]; result1=matrix_multiply(A, B, 4); for(int i=0;i<4;i++){ for(int j=0;j<4;j++){ System.out.print(result1[i][j]+" "); } System.out.println(""); } System.out.println("Strassen產生的矩陣相乘:"); int[][] result=null; result=strassen_matrix(A, B, 4); for(int i=0;i<4;i++){ for(int j=0;j<4;j++){ System.out.print(result[i][j]+" "); } System.out.println(""); } } public static int[][] strassen_matrix(int[][] A,int[][] B,int n){ int[][] result=new int[n][n]; if(n==2){ result=matrix_multiply(A, B, n); return result; } if(n>2){ int m=n/2; int[][] S1=null; int[][] S2=null; int[][] S3=null; int[][] S4=null; int[][] S5=null; int[][] S6=null; int[][] S7=null; int[][] S8=null; int[][] S9=null; int[][] S10=null; int[][] P1=null; int[][] P2=null; int[][] P3=null; int[][] P4=null; int[][] P5=null; int[][] P6=null; int[][] P7=null; int[][] A11=QuarterMatrix(A, n, 1); int[][] A12=QuarterMatrix(A, n, 2); int[][] A21=QuarterMatrix(A, n, 3); int[][] A22=QuarterMatrix(A, n, 4); int[][] B11=QuarterMatrix(B, n, 1); int[][] B12=QuarterMatrix(B, n, 2); int[][] B21=QuarterMatrix(B, n, 3); int[][] B22=QuarterMatrix(B, n, 4); int[][] result1=QuarterMatrix(result, n, 1); int[][] result2=QuarterMatrix(result, n, 2); int[][] result3=QuarterMatrix(result, n, 3); int[][] result4=QuarterMatrix(result, n, 4); S1=Addmatrix(B12, B22, m, -1); S2=Addmatrix(A11, A12, m, 1); S3=Addmatrix(A21, A22, m, 1); S4=Addmatrix(B21, B11, m, -1); S5=Addmatrix(A11, A22, m, 1); S6=Addmatrix(B11, B22, m, 1); S7=Addmatrix(A12, A22, m, -1); S8=Addmatrix(B21, B22, m, 1); S9=Addmatrix(A11, A21, m, -1); S10=Addmatrix(B11, B12, m, 1); P1=strassen_matrix(A11, S1, m); P2=strassen_matrix(S2, B22, m); P3=strassen_matrix(S3, B11, m); P4=strassen_matrix(A22, S4, m); P5=strassen_matrix(S5, S6, m); P6=strassen_matrix(S7, S8, m); P7=strassen_matrix(S9, S10, m); result1=Addmatrix(Addmatrix(Addmatrix(P5, P4,m, 1), P2, m, -1), P6, m, 1); result2=Addmatrix(P1, P2, m, 1); result3=Addmatrix(P3, P4, m, 1); result4=Addmatrix(Addmatrix(Addmatrix(P5, P1,m, 1), P3, m, -1), P7, m, -1); result=TogetherMatrix(result1, result2, result3, result4, m); } return result; } //暴力矩陣相乘 public static int[][] matrix_multiply(int[][] A,int[][] B,int n){ int[][] result=new int[n][n]; for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ result[i][j]=0; for(int k=0;k<n;k++){ result[i][j]+=A[i][k]*B[k][j]; } } } return result; } //劃分子矩陣 public static int[][] QuarterMatrix(int[][] p,int n,int num){ int rows=n/2; int cols=n/2; int[][] result=new int[rows][cols]; switch(num){ case 1:{ for(int i=0;i<rows;i++){ for(int j=0;j<cols;j++){ result[i][j]=p[i][j]; } } break; } case 2:{ for(int i=0;i<rows;i++){ for(int j=0;j<n-cols;j++){ result[i][j]=p[i][j+cols]; } } break; } case 3:{ for(int i=0;i<n-rows;i++){ for(int j=0;j<cols;j++){ result[i][j]=p[i+rows][j]; } } break; } case 4:{ for(int i=0;i<n-rows;i++){ for(int j=0;j<n-cols;j++){ result[i][j]=p[i+rows][j+cols]; } } break; } default:break; } return result; } //矩陣相加減 public static int[][] Addmatrix(int[][] A,int[][] B,int n,int Bmark){ int[][] result=new int[n][n]; for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ result[i][j]=A[i][j]+B[i][j]*Bmark; } } return result; } //整合矩陣 public static int[][] TogetherMatrix(int[][] a,int[][] b, int[][] c,int[][] d,int n){ int[][] result=new int[2*n][2*n]; for(int i=0;i<2*n;i++){ for(int j=0;j<2*n;j++){ if(i<n){ if(j<n){ result[i][j]=a[i][j]; } else{ result[i][j]=b[i][j-n]; } } else{ if(j<n){ result[i][j]=c[i-n][j]; } else{ result[i][j]=d[i-n][j-n]; } } } } return result; } }