目錄
Day 10:綜合任務 1
?一、題目分析
1. 數據結構
2. 相關函數基本知識
二、模塊介紹
1. 初始化與成績矩陣的構建
2. 創建總成績數組
3. 尋找成績極值
三、代碼與測試
小結
拓展:關于求極值的相關算法
Day 10:綜合任務 1
Task:
????????學生的成績存放于一個矩陣,其中行表示學生,列表示科目。如:第 0 行表示第 0 個學生的數學、語文、英語成績。要求:
????????進行學生成績的隨機生成, 區間為 [50, 100].
????????找出成績最好、最差的同學。但有掛科的同學不參加評比.
- 實際代碼中,for 和 if 是最常見的, switch 和 while 使用少得多.
- 使用了 continue, 它是指繼續跳過本次循環后面的代碼,直接進入下一次循環. 而 break 是跳出整個循環體.
- 為了隨機數,迫不得已提前使用了 new 語句生成對象.
- 通過數據測試找出程序中的 bug.
?一、題目分析
1. 數據結構
????????每個學生的科目都是三科,不會多不會少,因此多個學生之間的記錄是定長記錄,因此可以考慮使用固定的一個m*3二維數組來記錄所有學生的成績。此外,題目要求記錄成績最好和最差的同學,因此,可以考慮使用一個數組記錄每個同學的成績和,然后通過成績統計最高成績與最低成績。
????????注:就實現統計最好最差同學來說,這里的求和數組不是必要的,可以在設計學生成績時就完成相關統計。因此代碼是可以簡化的,只是此處為了練習java代碼內容故盡可能讓數據結構完善。
2. 相關函數基本知識
1) Random.nextlnt()
????????為避免問題模擬時考慮設置數據的麻煩,這里使用了數據數生成的基本方法。
????????java提供了Random庫,此庫中包含了一些系列可用的隨機數生成工具。今天我們將使用如下函數:
java Random.nextInt()方法
public int nextInt(int n)
????????該方法的作用是生成一個隨機的int值,該值介于[0,n)的區間,也就是0到n之間的隨機int值,包含0而不包含n
????????有幾個注意點是使用這個方法要注意的。
- 此隨機數的生成前閉后開的區間是,因此是無法生成n的
- 此數據生成最低值是0,若要完成指定[a, b)的生成需要額外設計代碼:a + temRandom.nextInt(b - a);
- 數據是整型,不包含浮點(雖然成績兼容浮點值,我們這暫時就整型來說明)
2)關于循環語句
? ? ? ? 參考:【Java 專題補充】流程控制語句-CSDN博客
3)關于 continue 和 break
? ? ? ? 參考:?【Java 專題補充】流程控制語句-CSDN博客
二、模塊介紹
1. 初始化與成績矩陣的構建
????????初始化定義的上限不是100而是101是因為隨機函數的右界是個開區間,因此要取滿需要額外加一。
????????此外,考慮到學生數量是不確定的,且考慮到成績表的可擴展性,這里采用的是動態分配的方法創建矩陣。
// Step 1. Generate the date with n student and m course.// Set these value by yourself.int n = 10;int m = 3;int lowerBound = 50;int upperBound = 101;int threshold = 60;// Here we have to use an object to generate random numbersRandom temRandom = new Random();int[][] data = new int[n][m];for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {data[i][j] = lowerBound + temRandom.nextInt(upperBound - lowerBound);} // Of for j} // Of for i
2. 創建總成績數組
????????創建總成績數組。再次強調,這個數組不是必須的,此處是為了區分過程為了理清楚原理特別使用的一個存儲空間。本質上可以通過成績矩陣直接獲得這一步的結果。
????????當某個學生成績低于及格線時,其總分無效,依據題意,這里令其總成績為0。
????????這里的0更多起了標記這個學生成績無效的作用。
// Step 2.Compute the total score of each student.int[] totalScores = new int[n];for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {if (data[i][j] < threshold) {totalScores[i] = 0;break;} // Of iftotalScores[i] += data[i][j];} // Of for j} // Of for i
3. 尋找成績極值
分析代碼如下:
????????這里統計時將總分數為0(標記的不及格/通過)的學生跳過統計,符合題意要求
????????這里使用了編程中常見得不能再常見的求極值方案——通過給預求極值設定一個反方向的最極限值(說人話就是給最大值設定一個能取的最小值為初始值,給最小值設定一個能取的最大值為初始值。這是一個求極值很常見的方法)
????????這里能取的最小值就是0,最大值就是m * upperBound,因此只要保證最大值初始值小于等于0即可,最小值初始值大于等于m * upperBound即可。
????????但是要說明,這個最值的含義只有在元素集的數目大于0才有意義。
// Step 3. Find the best and worst student.// Typical initialization for index: invalid value.int tempBestIndex = -1;int tempWorstIndex = -1;// Typical initialization for best and worst values.// They must be replaced by valid values.int tempBestScore = 0;int tempWorstScore = m * upperBound + 1;for (int i = 0; i < n; i++) {// Do not consider failed students.if (totalScores[i] == 0) {continue;} // Of ifif (tempBestScore < totalScores[i]) {tempBestScore = totalScores[i];tempBestIndex = i;} // Of if// Attention: This if statememt cannot be combined with the last one// using "else if",because a student can be both the best and the// worst. I found this bug while setting upperBound = 65.if (tempWorstScore > totalScores[i]) {tempWorstScore = totalScores[i];tempWorstIndex = i;} // Of if} // Of for i
三、代碼與測試
package basic;import java.util.Arrays;
import java.util.Random;/*** The usage of sth.** @author: Changyang Hu joe03@foxmail.com* @date created: 2025-05-10*/
public class Task1 {/*** ********************** @Title: main* @Description: The entrance of the program.** @param args Not used now.* @return void **********************/public static void main(String args[]) {task1();}// Of main/*** ********************** @Title: task1* @Description: Method unit test.* * @return void **********************/public static void task1() {// Step 1. Generate the data with n student and m courses.// Set these values by yourselfint n = 10;int m = 3;int lowerBound = 50;int upperBound = 101; // Should be 100. I use this value for testingint threshold = 60;// Here we have to use an object to generate random numbers.Random tempRandom = new Random();int[][] data = new int[n][m];for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {data[i][j] = lowerBound + tempRandom.nextInt(upperBound - lowerBound);} // Of for j} // Of for iSystem.out.println("The data is:\r\n" + Arrays.deepToString(data));// Step 2. Compute the total score of each student.int[] totalScores = new int[n];for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {if (data[i][j] < threshold) {totalScores[i] = 0;break;} // Of iftotalScores[i] += data[i][j];} // Of for j} // Of for iSystem.out.println("The total scores are:\r\n" + Arrays.toString(totalScores));// Step 3. Find the best and worst student.// Typical initialization for index: invalid value.int tempBestIndex = -1;int tempWorstIndex = -1;// Typical initialization for best and worst value.// They must be replaced by valid values.int tempBestScore = 0;int tempWorstScore = m * upperBound + 1;for (int i = 0; i < n; i++) {// Do not consider failed students.if (totalScores[i] == 0) {continue;} // Of ifif (tempBestScore < totalScores[i]) {tempBestScore = totalScores[i];tempBestIndex = i;} // Of if// Attention: This if statement cannot be combined with the last one// using "else if", because a student can be both the best and the// worst. I found this bug while setting upperBound = 65.if (tempWorstScore > totalScores[i]) {tempWorstScore = totalScores[i];tempWorstIndex = i;} // Of if} // Of for i// Step 4. Output the student number and score.if (tempBestIndex == -1) {System.out.println("Cannot find best student. ALL students have failed.");} else {System.out.println("The best student is No." + tempBestIndex + " with scores: "+ Arrays.toString(data[tempBestIndex]));} // Of ifif (tempWorstIndex == -1) {System.out.println("Cannot find worst student. All students have failed.");} else {System.out.println("The worst student is No." + tempWorstIndex + " with scores: "+ Arrays.toString(data[tempWorstIndex]));} // Of if}// Of task1}// Of class Task1
?第一次運行結果:upperBound = 101
在一次運行后數據如我們預期那樣,對于不及格的學生,其總分是按照0處理的。
為了驗證我們預先的特殊情況,我們先將隨機生成的分數上限下調到66,讓我們的分數隨機數更容易落到不及格:upperBound = 66
可以發現,因為每個人存在不及格學科(由于是隨機生成,所以也存在第一張圖片所示,仍然有結果),因此大家總分都不及格,因此,這里大家的分數在比較時都跳過了,出現了“無最高分,無最低分”的人為規定條件。?
小結
????????本篇展示了數據處理中很多比較常見的方法,包括總分求和、極值的計算與分析等。要注意的點就是靈活對于數據進行分析,合理利用數據結構即可,屬于是基礎的表格數據的應用。
????????但是引起我思考的是其中求極值的方法。本篇代碼中我們使用的是一種給極值賦相反的極限值的一種無效賦值法,我提到了“ 數據集合元素要大于0時,極值才有意義 ”。
拓展:關于求極值的相關算法
????????任何最大值最小值的含義都是在元素集大于0才有意義,這個不言而喻。
????????也是因為這個原因,還有另一種設置初值的方案是——將第一個元素的值設定給初始極值,并由此衍生——只記錄最大值的下標而不記錄最大值本身,然后下標初始值為0(就是默認為第一個元素的值)。如果這么做了,我們設定的變量似乎就更少了呢,而且,對于隨機存取的數組來說,下標的信息熵比元素本身要多***(知道極值下標的話,在知道數據順序之余還能以O(1)速度立馬知道數據,而只知道數據最大值本身的話,那也就知道數據本身而已了)***。
????????但是事物要辯證看待,本代碼使用的無效賦值方法其實可以讓數據本身起到一種標簽的作用,若比大小時候有特殊處理時(比如這里的0分不統計),通過判斷極值是否有效也可以用以斷定“ 是不是所有大小判斷都已經跳過 ”這種極特殊情景。
? ? ? ? 除此之外,一般常見的求極值的算法還有:三分、二元函數、退火求極值的方法。更詳細的可以參考如下博文:
優化算法:一元與二元函數極值求解與退火方法-CSDN博客