以下答案純屬個人愚見,作為IT新手,算法代碼中難免有邏輯漏洞和其他不足之處,歡迎朋友你點評拍磚,交流爭辯能極大開闊思維,願一起加油進步!^_^
1.1.19 在計算機上運行以下程序:
1 public class Fibonacci { 2 3 public static long F(int N) { 4 if(0 == N) 5 return 0; 6 if(1 == N) 7 return 1; 8 return F(N - 1) + F(N - 2); 9 } 10 11 public static void main(String[] args) { 12 for(int N = 0; N < 100; ++N) 13 StdOut.println(N + " " + F(N)); 14 } 15 16 }
計算機用這段程序在一個小時之內能夠得到F(N) 結果的最大N 值是多少?開發F(N) 的一 個更好的實現,用數組保存已經計算過的值。

1 public class Fibonacci { 2 3 // Fibonacci數列計算,時間空間復雜度優化版 4 private static int M = 100; 5 private static long[] fib = new long[M]; 6 public static long fibonacciOptimization(int N) { 7 if(0 == N) 8 fib[0] = 0; 9 else if(1 == N) 10 fib[1] = 1; 11 else 12 fib[N] = fib[N - 1] + fib[N -2]; 13 return fib[N]; 14 } 15 16 public static void main(String[] args) { 17 for(int N = 0; N < 100; ++N) { 18 fib[N] = fibonacciOptimization(N); 19 StdOut.println(N + "\t" + fib[N]); 20 } 21 } 22 23 }
1.1.20 編寫一個遞歸的靜態方法計算 ln( N! ) 的值。

1 public static long factorial(int M) { 2 if(0 == M || 1 == M) 3 return 1; 4 else 5 return M * factorial(M - 1); 6 } 7 8 public static double ln(int N) { 9 return Math.log(factorial(N)); 10 } 11 12 public static void main(String[] args) { 13 double val = ln(4); 14 StdOut.println(val); 15 }
1.1.21 編寫一段程序,從標准輸入按行讀取數據,其中每行都包含一個名字和兩個整數。然后用printf() 打印一張表格,每行的若干列數據包括名字、兩個整數和第一個整數除以第二個整數的結果,精確到小數點后三位。可以用這種程序將棒球球手的擊球命中率或者學生的考試分數制成表格。

1 public static void main(String[] args) { 2 int M = 3; 3 int index = 0; 4 String[] strs = new String[M]; 5 while(index < M) 6 strs[index++] = StdIn.readLine(); 7 for(int i = 0; i < strs.length; ++i) { 8 String[] arr = strs[i].split("\\s+"); 9 double temp = Double.parseDouble(arr[1]) / Double.parseDouble(arr[2]); 10 StdOut.printf("%-10s %-10s %-10s %-13.3f\n", arr[0], arr[1], arr[2], temp); 11 }; 12 }
1.1.22 使用1.1.6.4 節中的rank() 遞歸方法重新實現BinarySearch 並跟蹤該方法的調用。每當該方法被調用時,打印出它的參數lo 和hi 並按照遞歸的深度縮進。提示:為遞歸方法添加一個參數來保存遞歸的深度。

1 /** 2 * 遞歸查找關鍵詞的索引 3 * @param key 4 * @param arr 5 * @param low 6 * @param high 7 * @return 8 */ 9 public static int rank(int key, int[] arr, int low, int high, int depth) { 10 printCallInfo(low, high, depth); 11 if(low > high) 12 return -1; 13 int mid = low + ((high - low) >> 1); 14 if(key < arr[mid]) 15 return rank(key, arr, low, mid - 1, depth + 1); 16 else if(key > arr[mid]) 17 return rank(key, arr, mid + 1, high, depth + 1); 18 else 19 return mid; 20 } 21 22 /** 23 * 二分查找 : 遞歸描述 24 * @param key 25 * @param arr 26 * @return 27 */ 28 public static int binarySearch(int key, int[] arr, int depth) { 29 return rank(key, arr, 0, arr.length - 1, depth); 30 } 31 32 /** 33 * 打印縮進 34 * @param indents 縮進數 35 */ 36 private static void printIndent(final int indents) { 37 for(int i = 0; i < indents; ++i) 38 StdOut.print("----------"); 39 } 40 41 /** 42 * 打印調用信息 43 * @param low 44 * @param high 45 * @param depth 46 */ 47 private static void printCallInfo(int low, int high, int depth) { 48 StdOut.print(depth + "\t"); 49 printIndent(depth); 50 StdOut.println(low + "\t" + high); 51 } 52 53 public static void main(String[] args) { 54 int N = 1024; 55 int[] arr = new int[N]; 56 for(int i = 0; i < N; ++i) 57 arr[i] = StdRandom.uniform(N * 50); 58 // 排序 59 Arrays.sort(arr); 60 // 從隨機數組中隨機抽取一個元素作為關鍵字 61 // 輸出隨機數組 62 StdOut.print("seq = "); 63 for(int i = 0 ; i < N; ++i) 64 StdOut.print(arr[i] + "\t"); 65 int key = arr[StdRandom.uniform(N)]; 66 StdOut.println("\nkey = " + key); 67 StdOut.println("---------------------------------------------------------------------------------------"); 68 binarySearch(key, arr, 0); 69 }
1.1.23 為BinarySearch 的測試用例添加一個參數:+ 打印出標准輸入中不在白名單上的值;-,則打印出標准輸入中在白名單上的值。

1 /** 2 * 二分查找 : 非遞歸描述 3 * @param key 關鍵字 4 * @param arr 數組 5 * @return 若找到則返回true,否則返回false 6 */ 7 public static boolean BinaryLookup(int key, int[] arr) { 8 int low = 0; 9 int high = arr.length - 1; 10 while(low <= high) { 11 int mid = low + ((high - low) >> 1); 12 if(key < arr[mid]) 13 high = mid - 1; 14 else if(key > arr[mid]) 15 low = mid + 1; 16 else 17 return true; 18 } 19 return false; 20 } 21 22 public static void main(String[] args) { 23 // “ + ” --> 打印出標准輸入中不在白名單上的值, 24 // “ - ” --> 打印出標准輸入中在白名單上的值 25 char symbol = '-'; 26 int[] whitelist = new In(args[0]).readAllInts(); 27 Arrays.sort(whitelist); 28 while(!StdIn.isEmpty()) { 29 int key = StdIn.readInt(); 30 boolean found = BinaryLookup(key, whitelist); 31 if('+' == symbol && !found) 32 StdOut.println(key); 33 if('-' == symbol && found) 34 StdOut.println(key); 35 } 36 }
測試命令:java xxx tinyW.txt < tinyT.txt
1.1.24 給出使用歐幾里德算法計算105 和24 的最大公約數的過程中得到的一系列p 和q 的值。擴展該算法中的代碼得到一個程序Euclid,從命令行接受兩個參數,計算它們的最大公約數並打印出每次調用遞歸方法時的兩個參數。使用你的程序計算1 111 111 和1 234 567 的最大公約數。

1 /** 2 * 打印縮進 3 * @param indents 縮進數 4 */ 5 private static void printIndents(final int indents) { 6 for(int i = 0; i < indents; ++i) 7 StdOut.print("----------"); 8 } 9 10 /** 11 * 打印調用信息 12 * @param p 13 * @param q 14 * @param depth 15 */ 16 private static void printCallInfo(int p, int q, int depth) { 17 StdOut.print(depth + "\t"); 18 printIndents(depth); 19 StdOut.println(p + " " + q); 20 } 21 22 /** 23 * 使用2300多年前的歐幾里得算法求解兩數的最大公約數 24 * @param p 數一 25 * @param q 數二 26 * @return 最大公約數 27 */ 28 public static int Euclid(int p, int q, int depth) { 29 printCallInfo(p, q, depth); 30 if(q == 0) 31 return p; 32 int r = p % q; 33 return Euclid(q, r, depth+1); 34 } 35 36 public static void main(String[] args) { 37 int p = Integer.parseInt(args[0]); 38 int q = Integer.parseInt(args[1]); 39 int gcd = Euclid(p, q, 0); 40 StdOut.println("\n" + p + " 和 " + q + " 的最大公約數是: " + gcd); 41 }
1.1.25 使用數學歸納法證明歐幾里德算法能夠計算任意一對非負整數p 和q 的最大公約數。
提高題
1.1.26 將三個數字排序。假設a、b、c 和t 都是同一種原始數字類型的變量。證明以下代碼能夠將a、 b、c 按照升序排列:
if (a > b) { t = a; a = b; b = t; } // 保證a為a、b兩數的較小者
if (a > c) { t = a; a = c; c = t; } // 保證a為a、b、c三數中的最小者
if (b > c) { t = b; b = c; c = t; } // 保證b為比a大的b、c兩數的較小者,從而必有c為三數中的最大者
1.1.27 二項分布。估計用以下代碼計算binomial(100, 50) 將會產生的遞歸調用次數:
1 public static double binomial(int N,int k, double p) { 2 if(N == 0 && k == 0) 3 return 1.0; 4 if(N < 0 || k < 0) 5 return 0.0; 6 return (1.0 - p) * binomial(N-1, k, p) + p * binomial(N-1, k-1, p); 7 }
將已經計算過的值保存在數組中並給出一個更好的實現。
思路:用二維數組存儲已計算的概率,下次需要時則直接取出即可,免去了再花時間去完成同樣的計算,例如:binom[N][k] = (1.0 - p) * binomial(N - 1, k, p) + p * binomial(N - 1, k - 1, p);

1 private static int binom_N = 100; 2 3 private static int binom_k = 50; 4 5 private static double[][] binom = new double[binom_N + 1][binom_k + 1]; 6 7 private static double binomial(int N, int k, double p) { 8 if(N < 0 || k < 0) { 9 return 0.0; 10 } else if(N == 0 && k == 0) { 11 if(binom[N][k] == -1.0) 12 binom[N][k] = 1.0; 13 } else { 14 if (binom[N][k] == -1.0) 15 binom[N][k] = (1.0 - p) * binomial(N - 1, k, p) + p * binomial(N - 1, k - 1, p); 16 } 17 return binom[N][k]; 18 } 19 20 public static void main(String[] args) { 21 // 數組binom初始化 22 for(int i = 0; i < binom_N + 1; ++i) 23 for(int j = 0; j < binom_k + 1; ++j) 24 binom[i][j] = -1.0; 25 // 計算概率 26 double res = binomial(binom_N, binom_k, 0.25); 27 StdOut.println(res); 28 }
1.1.28 刪除重復元素。修改BinarySearch 類中的測試用例來刪去排序之后白名單中的所有重復元素。

1 /** 2 * 統計數組arr中重復元素個數 3 * @param arr 4 * @return 重復元素個數 5 */ 6 public static int countRepeated(int[] arr) { 7 int cnt = 0; 8 for (int i = 0; i < (arr.length - 1); ++i) 9 if (arr[i + 1] == arr[i]) 10 ++ cnt; 11 return cnt; 12 } 13 14 /** 15 * 去掉重復的元素,並拷貝其余元素 16 * @param original 原始數組 17 * @param repeatedCnt 重復的元素個數 18 * @return 去重后的數組拷貝 19 */ 20 public static int[] copy(int[] original, int repeatedCnt) { 21 int[] res = new int[original.length - repeatedCnt]; 22 int cntIdx = 0; 23 res[cntIdx ++] = original[0]; 24 for(int i = 1; i < original.length; ++i) 25 if(original[i] == original[i-1]) 26 continue; 27 else 28 res[cntIdx ++] = original[i]; 29 return res; 30 } 31 32 public static void main(String[] args) { 33 int[] whitelist = new In(args[0]).readAllInts(); 34 // 白名單排序並輸出 35 Arrays.sort(whitelist); 36 for(int i = 0; i < whitelist.length; ++i) 37 StdOut.print(whitelist[i] + "\t"); 38 StdOut.println(); 39 // 統計重復元素個數並去重 40 int cnt = countRepeated(whitelist); 41 int[] res = copy(whitelist, cnt); 42 // 輸出去重后的數組 43 for(int i = 0; i < res.length; ++i) 44 StdOut.print(res[i] + "\t"); 45 }
1.1.29 等值鍵。為BinarySearch 類添加一個靜態方法rank(),它接受一個鍵和一個整型有序數組(可能存在重復鍵)作為參數並返回數組中小於該鍵的元素數量,以及一個類似的方法count() 來返回數組中等於該鍵的元素的數量。注意:如果i 和j 分別是rank(key,a) 和count(key,a)的返回值,那么a[i..i+j-1] 就是數組中所有和key 相等的元素。看法:只有當key在數組中時才這樣子。

1 /** 2 * 二分查找統計 3 * @param key 待查找關鍵字 4 * @param arr 待查找數組 5 * @return 返回小於key的個數即比等於key的第一個元素的索引值,若找不到則返回-1 6 */ 7 private static int countLowers(int key, int[] arr) { 8 int low = 0; 9 int high = arr.length - 1; 10 while(low <= high) { 11 int mid = low + ((high - low) >> 1); 12 if(key < arr[mid]) 13 high = mid - 1; 14 else if(key > arr[mid]) 15 low = mid + 1; 16 else { 17 while(mid > 0 && arr[mid] == arr[mid - 1]) // 注意判斷條件的先后順序 18 -- mid; 19 return mid; 20 } 21 } 22 return low; // -1; 根據算法原理可知low是小於key的個數 23 } 24 25 /** 26 * 統計與key相等的個數 27 * @param key 待查找關鍵字 28 * @param arr 待查找數組 29 * @return 返回與key相等的個數 30 */ 31 private static int countEquals(int key, int[] arr) { 32 int lowers = countLowers(key, arr); 33 int idx = lowers; 34 if(idx == arr.length || key != arr[idx]) // 注意判斷條件的先后順序 35 return 0; 36 37 int cnt = 1; 38 while((idx < arr.length - 1) && (arr[idx] == arr[idx + 1])) { // 注意判斷條件的先后順序 39 ++ cnt; 40 ++ idx; 41 } 42 return cnt; 43 } 44 45 public static void main(String[] args) { 46 // 從文件讀取數據 47 In in = new In("./data/tinyW.txt"); 48 int[] whiteList = in.readAllInts(); 49 // 排序並打印輸出 50 Arrays.sort(whiteList); 51 for(int idx = 0; idx < whiteList.length; ++idx) 52 StdOut.print(whiteList[idx] + "\t"); 53 StdOut.println(); 54 // 從控制台讀取關鍵字 55 int key = StdIn.readInt(); 56 int lowers = countLowers(key, whiteList); 57 StdOut.println("小於\t" + key + "\t的個數是:\t" + lowers); 58 int equals = countEquals(key, whiteList); 59 StdOut.println("等於\t" + key + "\t的個數是:\t" + equals); 60 }
1.1.30 數組練習。編寫一段程序,創建一個N×N 的布爾數組a[][]。其中當i 和j 互質時(沒有相同因子),a[i][j] 為true,否則為false。

1 /** 2 * 判斷兩數是否互質,若兩數的最大公約數是1則兩數互質 3 * @param a 4 * @param b 5 * @return 若互質則true,否則false 6 */ 7 private static boolean isCoprime(int a, int b) { 8 for(int i = 2; i < Math.sqrt(a); ++i) { 9 if(a % i == 0 && b % i == 0) 10 return false; 11 } 12 return true; 13 } 14 15 /** 16 * 使用2300多年前的歐幾里得算法求解兩數的最大公約數 17 * @param p 數一 18 * @param q 數二 19 * @return 最大公約數 20 */ 21 private static int gcd(int p, int q) { 22 if(q == 0) 23 return p; 24 int r = p % q; 25 return gcd(q, r); 26 } 27 28 private static boolean[][] boolArray(int N) { 29 // 創建NxN的布爾二維數組 30 boolean[][] boolArr = new boolean[N][N]; 31 for(int i = 1; i <= N; ++i) 32 for(int j = 1; j <= N; ++j) 33 if(1 == gcd(i, j)) 34 boolArr[i - 1][j - 1] = true; 35 else 36 boolArr[i - 1][j - 1] = false; 37 return boolArr; 38 } 39 40 public static void main(String[] args) { 41 int N = 5; 42 boolean[][] boolArr = boolArray(N); 43 for(int i = 0; i < N; ++i) { 44 for (int j = 0; j < N; ++j) 45 StdOut.print(boolArr[i][j] + "\t"); 46 StdOut.println(); 47 } 48 }
1.1.31 隨機連接。編寫一段程序,從命令行接受一個整數N 和double 值p(0 到1 之間)作為參數,在一個圓上畫出大小為0.05 且間距相等的N 個點,然后將每對點按照概率p 用灰線連接。

1 /** 2 * 畫圓 3 * @param x 圓心x坐標 4 * @param y 圓心y坐標 5 * @param r 半徑r 6 */ 7 private static void drawCircle(double x, double y, double r) { 8 StdDraw.setXscale(0, 2 * x); 9 StdDraw.setYscale(0, 2 * y); 10 StdDraw.setPenRadius(0.003); 11 StdDraw.setPenColor(StdDraw.BOOK_LIGHT_BLUE); 12 StdDraw.circle(x, y, r); 13 } 14 15 /** 16 * 在圓上描點 17 * @param x0 圓心x坐標 18 * @param y0 圓心y坐標 19 * @param r 半徑r 20 * @param N N個點 21 */ 22 private static double[][] drawPoints(double x0, double y0, double r, int N) { 23 double[][] points = new double[N][2]; 24 StdDraw.setPenRadius(0.005); 25 StdDraw.setPenColor(StdDraw.BOOK_RED); 26 for(int idx = 0; idx < N; ++idx) { 27 double x = x0 + r * Math.cos(2 * Math.PI * idx / N); 28 double y = y0 + r * Math.sin(2 * Math.PI * idx / N); 29 StdDraw.point(x, y); 30 points[idx][0] = x; 31 points[idx][1] = y; 32 } 33 return points; 34 } 35 36 /** 37 * 以概率p隨機連接頂點集points中的點 38 * @param points 點集 39 * @param p 概率p 40 */ 41 private static void randomLinkPoints(double[][] points, double p) { 42 StdDraw.setPenRadius(0.002); 43 StdDraw.setPenColor(StdDraw.LIGHT_GRAY); 44 int length = points.length; 45 for(int i = 0; i < length; ++i) 46 for(int j = 0; j < length; ++j) 47 if(true == StdRandom.bernoulli(p)) 48 StdDraw.line(points[i][0], points[i][1], points[j][0], points[j][1]); // 應該再建立一個包含x坐標和y坐標的數據結構 49 } 50 51 /** 52 * 在圓上畫N個點然后每兩點間以概率p連接 53 * @param N N個點 54 * @param p 概率p 55 */ 56 private static void randomLink(int N, double p) { 57 double x = 10.0; 58 double y = 10.0; 59 double r = 9.0; 60 drawCircle(x, y, r); 61 double[][] points = drawPoints(x, y, r, N); 62 randomLinkPoints(points, p); 63 } 64 65 public static void main(String[] args) { 66 randomLink(20, 0.2); 67 }
1.1.32 直方圖。假設標准輸入流中含有一系列double 值。編寫一段程序,從命令行接受一個整數N 和兩個double 值l 和r。將(l,r) 分為N 段並使用StdDraw 畫出輸入流中的值落入每段的數量的直方圖。

1 /** 2 * 數據柱狀圖 3 * @param N 4 * @param l 5 * @param r 6 * @param arr 7 */ 8 private static void dataHistogram(int N, double l, double r, double[] arr) { 9 int length = arr.length; 10 int[] statistics = new int[N]; 11 double interval = (r - l) / N; 12 // 統計數據分布 13 for(int i = 0; i < length; ++i) { 14 double remain = arr[i] - l; 15 int idx = (int)(remain / interval); 16 ++ statistics[idx]; 17 } 18 // 查找統計結果中最大值,用於繪制直方圖時計算柱狀圖高時 19 double max = statistics[0]; 20 for(int i = 1; i < statistics.length; ++i) { 21 if(max < statistics[i]) 22 max = statistics[i]; 23 } 24 // 繪制直方圖 25 StdDraw.setXscale(l, r); 26 StdDraw.setPenColor(StdDraw.BOOK_BLUE); 27 double x0 = l + interval / 2.0; 28 for(int i = 0; i < statistics.length; ++i) { 29 double x = x0 + i * interval; 30 double y = statistics[i] / (max + 1) / 2.0; 31 double hw = 0.99 * interval / 2.0; 32 double hh = y; 33 StdDraw.filledRectangle(x, y, hw, hh); 34 } 35 } 36 37 public static void main(String[] args) { 38 In in = new In("./data/largeW.txt"); 39 double[] whiteList = in.readAllDoubles(); 40 double min = Double.POSITIVE_INFINITY; 41 double max = Double.NEGATIVE_INFINITY; 42 for(int i = 0; i < whiteList.length; ++i) { 43 if(min > whiteList[i]) 44 min = whiteList[i]; 45 if(max < whiteList[i]) 46 max = whiteList[i]; 47 } 48 // 從控制台讀取應該將數據分割的段數 49 StdOut.print("將數據分割的段數:"); 50 int N = StdIn.readInt(); 51 dataHistogram(N, min, max + 10.0, whiteList); 52 }
此圖也反映了偽隨機數是均勻分布的。
1.1.33 矩陣庫。編寫一個Matrix 庫並實現以下API: <這道題考慮不周,請讀者先略過>
public class Matrix
- static double dot(double[ ] x, double[ ] y) 向量點乘
- static double[ ][ ] multiple(double[ ][ ] a, double[ ][ ] b) 矩陣和矩陣之積
- static double[ ][ ] transpose(double[ ][ ] a) 轉置矩陣
- static double[ ] multiple(double[ ][ ] a, double[ ] x) 矩陣和向量之積
- static double[ ] multiple(double[ ] y, double[ ][ ] a) 向量和矩陣之積
編寫一個測試用例,從標准輸入讀取矩陣並測試所有方法。

1 /** 2 * 向量點乘 3 * @param x x向量 4 * @param y y向量 5 * @return 向量點乘 6 */ 7 public static double dot(double[] x, double[] y) { 8 // 點乘必須是向量a的長度等於向量b的長度才能運算 9 if(x.length != y.length) 10 System.exit(-1); 11 double res = 0.0; 12 for(int i = 0; i < x.length; ++i) 13 res += x[i] * y[i]; 14 return res; 15 } 16 17 /** 18 * 矩陣和矩陣之積 19 * @param a 20 * @param b 21 * @return 22 */ 23 public static double[][] multiple(double[][] a, double[][] b) { 24 // 只有矩陣a的列數等於矩陣b的行數時,相乘才有意義 25 if(a[0].length != b.length) 26 System.exit(-1); 27 double[][] matrix = new double[a.length][b[0].length]; 28 for (int i = 0; i < a.length; ++i) 29 for (int j = 0; j < b[0].length; ++j) 30 for (int k = 0; k < b.length; ++k) 31 matrix[i][j] += a[i][k] * b[k][j]; 32 return matrix; 33 } 34 35 /** 36 * 矩陣和向量之積 37 * @param a 38 * @param x 39 * @return 40 */ 41 public static double[] multiple(double[][] a, double[] x) { 42 if(a[0].length != x.length) 43 System.exit(-1); 44 double[] matrix = new double[x.length]; 45 for(int i = 0; i < a.length; ++i) 46 for(int j = 0; j < x.length; ++j) 47 matrix[i] += a[i][j] * x[j]; 48 return matrix; 49 } 50 51 /** 52 * 向量和矩陣之積 53 * @param y 54 * @param a 55 * @return 56 */ 57 public static double[] multiple(double[] y, double[][] a) { 58 double[] matrix = new double[y.length]; 59 for(int i = 0; i < y.length; ++i) 60 for(int j = 0; j < a[i].length; ++j) 61 matrix[i] += y[j] * a[j][i]; 62 return matrix; 63 } 64 65 /** 66 * 轉置矩陣 67 * @param a 68 * @return 69 */ 70 public static double[][] transpose(double[][] a) { 71 for(int i = 0; i < a.length; ++i) 72 for(int j = 0; j < i; ++j) { 73 double temp = a[i][j]; 74 a[i][j] = a[j][i]; 75 a[j][i] = temp; 76 } 77 return a; 78 } 79 80 public static void main(String[] args) { 81 StdOut.println("-------- 向量點乘 ---------"); 82 double[] a0 = {1, 2, 3}; 83 double[] b0 = {4, 5, 6}; 84 double res0 = dot(a0, b0); 85 StdOut.println(res0); 86 87 StdOut.println("-------- 矩陣乘法 ---------"); 88 double[][] a1 = { 89 {1, 2}, 90 {3, 4}, 91 {5, 6} 92 }; 93 double[][] b1 = { 94 {1, 2, 3}, 95 {4, 5, 6} 96 }; 97 double[][] res1 = multiple(a1, b1); 98 for(int i = 0; i < res1.length; ++i) { 99 for (int j = 0; j < res1[i].length; ++j) 100 StdOut.printf("%-10.3f", res1[i][j]); 101 StdOut.println(); 102 } 103 104 StdOut.println("-------- 矩陣轉置 ---------"); 105 double[][] a2 = { 106 {1, 2, 3}, 107 {4, 5, 6}, 108 {7, 8, 9} 109 }; 110 double[][] c2 = transpose(a2); 111 for(int i = 0; i < a2.length; ++i) { 112 for (int j = 0; j < a2[i].length; ++j) 113 StdOut.printf("%-10.3f", a2[i][j]); 114 StdOut.println(); 115 } 116 117 StdOut.println("----- 矩陣和向量之積 ------"); 118 double[][] a3 = { 119 {1, 2, 3}, 120 {4, 5, 6}, 121 {7, 8, 9} 122 }; 123 double[] b3 = {1, 2, 3}; 124 double[] c3 = multiple(a3, b3); 125 for(int i = 0; i < c3.length; ++i) 126 StdOut.printf("%-10.3f\n", c3[i]); 127 128 StdOut.println("----- 向量和矩陣之積 ------"); 129 double[] a4 = {1, 2, 3}; 130 double[][] b4 = { 131 {1, 2, 3}, 132 {4, 5, 6}, 133 {7, 8, 9} 134 }; 135 double[] c4 = multiple(a4, b4); 136 for(int i = 0; i < c4.length; ++i) 137 StdOut.printf("%-10.3f", c4[i]); 138 }
1.1.34 過濾。以下哪些任務需要(在數組中,比如)(1)保存標准輸入中的所有值?哪些可以(2)被實現為一個過濾器且僅使用固定數量的變量和固定大小的數組(和N無關)?在每個
問題中,輸入都來自於標准輸入且含有N個0到1的實數。
- 打印出最大和最小的數 (2)
- 打印出所有數的中位數 (1)
- 打印出第 k 小的數,k 小於100 (2)
- 打印出所有數的平方和 (2)
- 打印出 N 數的平均值 (2)
- 打印出大於平均值的數的百分比 (1)
- 將 N 個數按照升序打印 (1)
- 將 N 個數按照隨機順序打印 (1)
實驗題
1.1.35 模擬擲骰子。以下代碼能夠計算每種兩個骰子之和的准確概率分布:
1 int SIDES = 6; 2 double[] dist = new double[2 * SIDES + 1]; 3 for(int i = 1; i <= SIDES; i++) 4 for(int j = 1; j <= SIDES; j++) 5 dist[i+j] += 1.0; 6 for (int k = 2; k <= 2*SIDES; k++) 7 dist[k] /= 36.0;
dist[i] 的值就是兩個骰子之和為i 的概率。用實驗模擬N 次擲骰子,並在計算兩個1 到 6 之間的隨機整數之和時記錄每個值的出現頻率以驗證它們的概率。N 要多大才能夠保證你的經驗數據和准確數據的吻合程度達到小數點后三位?

1 // 色子有六面,數值分別是1、2、3、4、5、6 2 private static int sides = 6; 3 4 /** 5 * 打印 6 * @param dist 待輸出數組 7 */ 8 private static void print(double[] dist) { 9 for(int i = 2; i <= 2 * sides; ++i) 10 StdOut.println(dist[i]); 11 StdOut.println("-------------------------"); 12 } 13 14 /** 15 * 根據統計數據計算概率值 16 * @param dist 統計數據數組 17 * @return 概率數組 18 */ 19 private static double[] computeProbability(double[] dist, int testTimes) { 20 for(int i = 2; i <= 2 * sides; ++i) 21 dist[i] /= (1.0 * testTimes); 22 return dist; 23 } 24 25 /** 26 * 兩個色子之和的理論概率值 27 * @return 理論概率值 28 */ 29 private static double[] theoreticalValue() { 30 double[] dist = new double[2 * sides + 1]; 31 // 統計值出現的理論次數 32 for(int i = 1; i <=sides; ++i) 33 for(int j = 1; j <= sides; ++j) 34 dist[i+j] += 1.0; 35 // 計算理論概率 36 dist = computeProbability(dist, 36); 37 return dist; 38 } 39 40 /** 41 * 用隨機數模擬擲色子並統計求出試驗概率 42 * @param N 拋擲次數 43 * @return 試驗概率 44 */ 45 private static double[] simulate(int N) { 46 double[] dist = new double[2 * sides + 1]; 47 // 做N次隨機試驗模擬拋色子,並統計數據 48 for(int i = 0; i < N; ++i) { 49 int a = StdRandom.uniform(1, 6 + 1); 50 int b = StdRandom.uniform(1, 6 + 1); 51 dist[a + b] += 1.0; 52 } 53 // 計算試驗概率值 54 dist = computeProbability(dist, N); 55 return dist; 56 } 57 58 /** 59 * 試驗概率值能否與理論概率值至少匹配到小數點后三位數 60 * @param dist0 理論概率值 61 * @param dist1 試驗概率值 62 * @return 能匹配到小數點后三位數則返回true,否則返回false 63 */ 64 private static boolean isMatch(double[] dist0, double[] dist1) { 65 for(int i = 2; i <= 2 * sides; ++i) 66 if(Math.abs(dist0[i] - dist1[i]) >= 0.0001) 67 return false; 68 return true; 69 } 70 71 /** 72 * 測試得到符合要求的試驗次數N 73 * @param initTimes 試驗初始次數值 74 * @param dist0 理論概率 75 * @return 符合要求的試驗次數 76 */ 77 private static int testGetN(int initTimes, double[] dist0) { 78 int N = initTimes; 79 boolean match = false; 80 while(!match) { 81 double[] dist1 = simulate(N); 82 match = isMatch(dist0, dist1); 83 if(match) 84 print(dist1); 85 // 當前N不合要求,則將N擴大10倍 86 N *= 10; 87 } 88 return N; 89 } 90 91 public static void main(String[] args) { 92 double[] dist0 = theoreticalValue(); 93 print(dist0); 94 int initTimes = 1000000; 95 int N = testGetN(initTimes, dist0); 96 StdOut.println("至少試驗次數的數量級: o(" + N + ")"); 97 }
1.1.36 亂序檢查。通過實驗檢查表1.1.10 中的亂序代碼是否能夠產生預期的效果。編寫一個程序ShuffleTest,接受命令行參數M 和N,將大小為M 的數組打亂N 次且在每次打亂之前都將數組重新初始化為a[i] = i。打印一個M×M 的表格,對於所有的列j,行i 表示的是i 在打亂后落到j 的位置的次數。數組中的所有元素的值都應該接近於N/M。
1.1.37 糟糕的打亂。假設在我們的亂序代碼中你選擇的是一個0 到N-1 而非i 到N-1 之間的隨機整數。證明得到的結果並非均勻地分布在N! 種可能性之間。用上一題中的測試檢驗這個版本。
1.1.38 二分查找與暴力查找。根據1.1.10.4 節給出的暴力查找法編寫一個程序bruteForceSearch,在你的計算機上比較它和BinarySearch 處理largeW.txt 和largeT.txt 所需的時間。

1 /** 2 * 二分查找 : 非遞歸描述 3 * @param key 關鍵字 4 * @param arr 數組 5 * @return 若找到則返回true,否則返回false 6 */ 7 public static boolean BinaryLookup(int key, int[] arr) { 8 int low = 0; 9 int high = arr.length - 1; 10 while(low <= high) { 11 int mid = low + ((high - low) >> 1); 12 if(key < arr[mid]) 13 high = mid - 1; 14 else if(key > arr[mid]) 15 low = mid + 1; 16 else 17 return true; 18 } 19 return false; 20 } 21 22 /** 23 * 暴力查找 24 * @param key 25 * @param arr 26 * @return 在數組arr中找到key則返回true,否則返回false 27 */ 28 public static boolean bruteForceSearch(int key, int[] arr) { 29 for(int i = 0; i < arr.length; ++i) 30 if(key == arr[i]) 31 return true; 32 return false; 33 } 34 35 public static void main(String[] args) { 36 int[] whiteList = new In(args[0]).readAllInts(); 37 long time0 = new Date().getTime(); 38 // Arrays.sort(whiteList); // 暴力破解無需排序 39 while(!StdIn.isEmpty()) { 40 int key = StdIn.readInt(); 41 boolean find = bruteForceSearch(key, whiteList); 42 if(!find) 43 StdOut.println(key); 44 } 45 long time1 = new Date().getTime(); 46 long elapsedTime = time1 - time0; 47 StdOut.println("用時: " + elapsedTime + " ms"); 48 }
測試命令:java xxx largeW.txt < largeT.txt > bruteForceSearch.txt
測試結果數據:http://files.cnblogs.com/files/gotodsp/TestResults.zip
1.1.39 隨機匹配。編寫一個使用BinarySearch 的程序,它從命令行接受一個整型參數T,並會分別針對N=10^3、10^4、10^5 和10^6 將以下實驗運行 T 遍:生成兩個大小為N 的隨機6 位正整數數組並找出同時存在於兩個數組中的整數的數量。打印一個表格,對於每個N,給出T 次實驗中該數量的平均值。

1 /** 2 * 返回長度為length的隨機6位正整數數組 3 * @param length 數組長度 4 * @return 隨機6位正整數數組 5 */ 6 private static int[] randomArray(int length) { 7 int[] arr = new int[length]; 8 for(int i = 0; i < length; ++i) 9 arr[i] = StdRandom.uniform(100000, 1000000); 10 return arr; 11 } 12 13 /** 14 * 遞歸查找關鍵詞的索引 15 * @param key 關鍵字 16 * @param arr 數組 17 * @param low 當前查找段的起始索引 18 * @param high 當前查找段的最終索引 19 * @return 若查找到則返回關鍵字key的索引值,找不到則返回-1 20 */ 21 public static int rank(int key, int[] arr, int low, int high) { 22 if(low > high) 23 return -1; 24 int mid = low + ((high - low) >> 1); 25 if(key < arr[mid]) 26 return rank(key, arr, low, mid - 1); 27 else if(key > arr[mid]) 28 return rank(key, arr, mid + 1, high); 29 else 30 return mid; 31 } 32 33 /** 34 * 查找關鍵key是否在數組arr中 35 * @param key 關鍵字 36 * @param arr 數組 37 * @return 若找到則返回索引值,否則返回-1 38 */ 39 private static int binarySearch(int key, int[] arr) { 40 return rank(key, arr, 0, arr.length - 1); 41 } 42 43 /** 44 * 統計兩個數組中的相同元素個數 45 * @param arr1 46 * @param arr2 47 * @return 48 */ 49 private static int countCommonElements(int[] arr1, int[] arr2) { 50 int cnt = 0; 51 for(int i = 0; i < arr1.length; ++i) { 52 int idx = binarySearch(arr1[i], arr2); 53 if(-1 != idx) 54 ++ cnt; 55 } 56 return cnt; 57 } 58 59 /** 60 * 隨機匹配 61 * @param T 次數 62 */ 63 private static void randomMatch(int T) { 64 for(int N = 1000; N <= 1000000; N *= 10) { 65 int cnt = 0; 66 for(int i = 0; i < T; ++i) { 67 int[] arr1 = randomArray(N); 68 int[] arr2 = randomArray(N); 69 cnt += countCommonElements(arr1, arr2); 70 } 71 double avg = 1.0 * cnt / N; 72 StdOut.println(N + " " + avg); 73 } 74 } 75 76 public static void main(String[] args) { 77 randomMatch(10000); 78 }
本文原創,歡迎轉載,請注明本站地址:http://www.cnblogs.com/gotodsp/p/4319865.html,:)