《算法:第四版》課后練習 1.1 答案


以下答案純屬個人愚見,作為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 }
View Code

 

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     }
View Code

 

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     }
View Code

 

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     }
View Code

 

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 }
View Code

  測試命令: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 }
View Code

 

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     }
View Code

 

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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

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 }
View Code

此圖也反映了偽隨機數是均勻分布的。

 

1.1.33  矩陣庫。編寫一個Matrix 庫並實現以下API: <這道題考慮不周,請讀者先略過>

public class Matrix 

  1.  static double dot(double[ ] x, double[ ] y)                     向量點乘 
  2.  static double[ ][ ] multiple(double[ ][ ] a, double[ ][ ] b)      矩陣和矩陣之積
  3.  static double[ ][ ] transpose(double[ ][ ] a)                        轉置矩陣 
  4.  static double[ ] multiple(double[ ][ ] a, double[ ] x)            矩陣和向量之積 
  5.  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 }
View Code

 

1.1.34  過濾。以下哪些任務需要(在數組中,比如)(1)保存標准輸入中的所有值?哪些可以(2)被實現為一個過濾器且僅使用固定數量的變量和固定大小的數組(和N無關)?在每個

問題中,輸入都來自於標准輸入且含有N個0到1的實數。

  1.   打印出最大和最小的數                      (2)
  2.   打印出所有數的中位數                 (1)
  3.   打印出第 k 小的數,k 小於100         (2)
  4.   打印出所有數的平方和                      (2)
  5.   打印出 N 數的平均值                         (2)
  6.   打印出大於平均值的數的百分比        (1)
  7.   將 N 個數按照升序打印                     (1)
  8.   將 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.06 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 }
View Code

 

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 }
View Code

測試命令: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 }
View Code

 

本文原創,歡迎轉載,請注明本站地址:http://www.cnblogs.com/gotodsp/p/4319865.html,:)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM