目录
一、题目分析
任务如下:
将学生成绩存放在一个矩阵中,其中行表示学生,列表示科目(例如:第1行即表示第一个学生的语文、数学、英语成绩),要求:
- 进行学生成绩的随机生成,成绩区间为[50,100]
- 找出成绩最好、最差的同学,但有挂科的同学不参加评比
1.存放学生成绩
根据题目可知,首先我们需要创建一个二维数组来存放学生的成绩,由于题目要求行代表学生,列代表科目,并且每位同学都统计的是语文、数学、英语三个科目,所以可以创建一个n * 3的二维数组(n代表学生人数)。
2.随机生成成绩
完成数组创建后,下一步就是进行学生成绩的随机生成。随机生成一个学生成绩,其实就是进行随机数生成,而在java中可以通过不同的方式(例如:Math.random类、Random类、SecureRandom类等)来完成随机数生成,这里我们用到的是java中的Random类。
先来了解一下Random类:
(1)Random类有两种构造方式
- Random():使用一个与当前系统时间对应的数字作为种子数
- Random(long seed):直接传入一个种子数
注:种子数是生成随机数的第一次使用值,机制是通过一个函数,将种子值转化为随机数空间中的某一个点上,并且产生的随机数均匀散布在空间中。种子数只是随机算法的起源数字,与生成的随机数的范围区间没有任何关系。
(2)Random类提供不同的方法来生成不同种类的随机数
- nextInt()方法:生成整型的随机数
- nextDouble()方法:生成实型的随机数
注:在nextInt()、nextDouble()中,小括号里边写生成随机数的范围右边界值(左边界值均为0),特别需要注意这个范围区间是“左闭右开”的,即包括左边界值,不包括右边界值。比如下面的代码所生成的随机数范围就是[0,10)。
Random r = new Random();
int tempNmuber = r.nextInt(10);
如果想要生成范围为[a,b)的随机数,代码如下:
Random r = new Random(); int tempNmuber = a + r.nextInt(b - a);
显然,r.nextInt(b - a)生成随机数的范围为[0,b-a),所以a + r.nextInt(b - a)生成随机数的范围就是[a,b)。
基本了解Random类后,就可以利用其进行学生成绩的随机生成了。
3.找出成绩最好、最差的学生
由于每位学生都有三个科目的成绩,并且同一个学生的三个科目成绩不一定满足正比关系,所以显然我们不能单独用某一个科目的分数高低来评判成绩的好坏。根据经验,利用三个科目的总分或者平均分来进行评比会比较合适,这里我们选择使用的是三个科目的总分。
二、代码实现
1.创建数组,生成随机成绩
由于我们后面需要用到Arrays类与Random类,所以在package语句后,要先进行导包,如下:
package basic;
import java.util.Arrays;
import java.util.Random;
然后,开始创建二维数组data,并利用Random类生成随机成绩,由于这里存放学生成绩的是一个二维数组,所以显然需要用一个两层for循环为每位同学的每科成绩进行生成,如下:
// Step 1. Generate the data with n students and m courses.
// Set these values by yourself.
int n = 10;
int m = 3;
int lowerBound = 50;
int upperBound = 101; // Should be 100.Just use this value for testing.
int 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 i
可以看到,在上述代码中设置了upperBound = 101,那为什么这里不设为100而要设为101呢?
这是因为,题目要求的区间为[50,100],左右均为闭区间,但是在上面介绍Random类时,我们已经知道利用Random类生成的随机数的范围是“左闭右开”的,所以为了与题目要求契合,我们将右边界值设为101,使得:
- tempRandom.nextInt(upperBound - lowerBound)生成随机数的范围 = [0,51)
- lowerBound + tempRandom.nextInt(uuperBound - lowerBound)生成随机数的范围 = [50,101)
再加上使用的是Random类中的nextInt()方法,它只生成整型的随机数,所以生成的随机数最大只能取到100,这样就可以很好地满足题目所要求的区间[50,100]。
最后,再利用Arrays.deepToString输出该二维数组,如下:
System.out.println("The data is:\r\n" + Arrays.deepToString(data));
2.计算每位同学的总分
首先,需要创建一个一维数组totalScores用于存放每位同学的总分,显然一位同学只有一个总分值,所以该数组totalScores的长度等于data数组的行数n,如下:
int[] totalScores = new int[n];
然后通过两层for循环,计算每位同学的总分,需要注意题目规定有挂科的同学不参加评比,所以为了方便后续操作,这里利用if语句将有挂科的同学的总分赋值为0,如下:
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (data[i][j] < threshold) {
totalScores[i] = 0;
break;
} // of if
totalScores[i] += data[i][j];
} // of for j
} // of for i
- j逐一取值:实现三个科目相加得到总分
- i逐一取值:得到每位同学的总分
3.找出成绩最好、最差的学生
首先,我们设置两个无效值tempBestIndex、tempWorstIndex,因为总成绩数组totalScores是从0开始索引的,所以令这两个无效值为-1,如果最后这两个变量仍然为-1,则说明找不出成绩最好、最差的学生,代码如下:
// Typical initialization for index:invalid value.
int tempBestIndex = -1;
int tempWorstIndex = -1;
然后,对总成绩最好值、总成绩最差值进行初始化,由于之前将有挂科的同学的总成绩赋值为了0,所以总成绩最好值最小最小可以取到0,而总成绩最差值最大可以取到m * upperBound(即三个科目均拿到满分),因此只需要保证
- tempBestScore <= 能取到的最小值0
- tempWorstScore >= 能取到的最大值m * upperBound
初始化设置如下:
// Typical initialization for best and worst values.
// They must be replaced by valid values.
int tempBestScore = 0;
int tempWorstScore = m * upperBound + 1;
再利用for循环对总成绩数组totalScores进行遍历:
- 将总成绩数组中的值依次与tempBestScore进行比较,并将较大者赋给tempBestScore,较大者在总成绩数组中对应的索引值赋给tempBestIndex
if (tempBestScore < totalScores[i]) {
tempBestScore = totalScores[i];
tempBestIndex = i;
} //of if
- 将总成绩数组中的值依次与tempWorstScore进行比较,并将较小者赋给tempWorstScore,较小者在总成绩数组中对应的索引值赋给tempWorstIndex
if (tempWorstScore > totalScores[i]) {
tempWorstScore = totalScores[i];
tempWorstIndex = i;
} // of if
注意,如果遇到某位同学的总成绩totalScores[ i ]等于0,则需要利用continue跳过本次循环,进入下一次循环,如下:
if (totalScores[i] == 0) {
continue;
} // of if
最后,输出成绩最好、最差的学生的总分以及在总成绩数组中的索引值,如下:
// 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 if
if (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
三、Task1.java
完整的程序代码:
package basic;
import java.util.Arrays;
import java.util.Random;
/**
*This is the tenth code, also the first task.
*
*@auther Xin Lin 3101540094@qq.com.
*/
public class Task1 {
/**
*********************
*The entrance of the program.
*
* @param args Not used now.
*********************
*/
public static void main(String[] args) {
task1();
}//of main
/**
*********************
* Method unit test.
*********************
*/
public static void task1() {
// Step 1. Generate the data with n students and m courses.
// Set these values by yourself.
int n = 10;
int m = 3;
int lowerBound = 50;
int upperBound = 101; // Should be 100.Just use this value for testing.
int 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 i
System.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 if
totalScores[i] += data[i][j];
} // of for j
} // of for i
System.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 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 if
if (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 worst.
// I found this bug while setting upperBound = 63.
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 if
if (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
运行结果:
从以上运行结果可以看到,正如我们预想的那样,对于有挂科的同学其总成绩为0。
通过一系列数据测试,我发现如果将upperBound设为63(使得生成的随机数有更大的几率小于60),那么每个同学都有挂科,每个同学的总分都为0,所以最后的运行结果就是不能找出成绩最好、最差的学生。
总结
今天通过解决一个综合任务,回顾了if语句、for语句、break语句、continue语句等,同时也学习了生成随机数的方法。今天求成绩最好、最差的处理方式,是将tempBestScore令为可取得的最小值,将tempWorstScore令为可取得的最大值,但是貌似我们也可以直接将总成绩数组中的第一个值令为初始值,再从总成绩数组中的第二个值开始遍历比较,这样好像也可以实现。总之,还是那句话,对于实际的问题,一定要学会拆分问题,分步骤进行代码实现。