嗨喽~小伙伴们,大家早上好,中午好,晚上好呀,
记得最初学习C++ 的时候,用的是谭浩强教授的教材,上面有很多非常棒的习题,对于练习C++非常有帮助(话说,我没打广告🤣......),这些编程练习,实际上也可以用Java来实现。
基础练习,虽然起点低,但留给我们思考的东西,可不止于实现功能。
这种练习网上一大堆,但我想写的是,通过这些练习来学习如何增强我们程序的健壮性。
现举例若干,以飨读者:
一:
给出一个百分制的成绩,
要求输出成绩等级 'A', 'B', 'C', 'D', 'E',
90分以上为'A', 80~90分为'B', 70~80分为'C', 60~70分为'D',60分以下为'E'。
学习过编程的小伙伴们应该都练习过这道题,咱用Java实现如下:
import java.util.Scanner;
/**
*
* @author sixibiheye
* @date 2021/10/12
* @apiNote Java基础练习一:
* 给出一个百分制的成绩,要求输出成绩等级'A','B','C','D','E',
* 90分以上为'A',
* 80~90分为'B',
* 70~80分为'C',
* 60~70分为'D',
* 60分以下为'E'。
*
*/
public class Question11 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int grade;
System.out.print("请输入您的成绩:");
grade = scanner.nextInt();
int gradeRank = grade / 10;
// 0~59分统一设定为50分
if(gradeRank <= 5){
gradeRank = 5;
}
switch (gradeRank){
case 10:
case 9:
System.out.println("您的成绩等级为:A"); break;
case 8:
System.out.println("您的成绩等级为:B"); break;
case 7:
System.out.println("您的成绩等级为:C"); break;
case 6:
System.out.println("您的成绩等级为:D"); break;
case 5:
System.out.println("您的成绩等级为:E"); break;
default:
System.out.println("您的输入有误!!!"); break;
}
}
}
如果,我们输入:87,运行结果如下:
看似没有问题,但是,这个程序有着致命的问题。
如果你作为一个用户,用这个程序去实现一个业务,如果你不小心输入了150,或者输入的不是整数92.7,甚至输入了字符串 ‘Haha’ ,咱这个程序还能运行吗?
很明显可以看到,程序抛出了异常-----InputMismatchException ,从用户角度看,那就是软件卡死,使用不了了。
所以,我们要保证,用户输入的任何内容,我们都能做出相应处理。即咱写的程序要有良好的健壮性。
针对此题,对于用户输入不是整数的Bug。咱可以尝试捕获异常。一旦用户输入的不是整数,就提醒用户重新输入。
还有一个Bug,如果用户输入的是整数,但不是0~100,尽管语法没错,但结果没有任何意义(逻辑错误),这个Bug咱也需要解决。
综上,我们可以改善上述程序如下:
import java.util.InputMismatchException;
import java.util.Scanner;
/**
*
* @author sixibiheye
* @date 2021/10/12
* @apiNote Java基础练习一:
* 给出一个百分制的成绩,要求输出成绩等级'A','B','C','D','E',
* 90分以上为'A',
* 80~90分为'B',
* 70~80分为'C',
* 60~70分为'D',
* 60分以下为'E'。
*
*/
public class Question11 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int grade;
while(true){
System.out.print("请输入您的成绩:");
try {
grade = scanner.nextInt();
if(grade <= 100 && grade >= 0){
break;
}
System.out.println("输入有误,成绩范围为0~100!");
} catch (InputMismatchException e){
System.out.println("您输入的成绩不是整数!");
/*
* 前面的nextInt()读取数据后指针仍在当前行,
* 如果再执行grade = scanner.nextInt();
* grade读取到的是回车符,
* 此处用scanner.next()"吃掉"用户按下的回车键,保证下一次的
* grade = scanner.nextInt() 读取的是用户输入的内容,而不是回车符
*
*/
scanner.next();
}
}
int gradeRank = grade / 10;
// 0~59分统一设定为50分
if(gradeRank <= 5){
gradeRank = 5;
}
switch (gradeRank){
case 10:
case 9:
System.out.println("您的成绩等级为:A"); break;
case 8:
System.out.println("您的成绩等级为:B"); break;
case 7:
System.out.println("您的成绩等级为:C"); break;
case 6:
System.out.println("您的成绩等级为:D"); break;
case 5:
System.out.println("您的成绩等级为:E"); break;
default:
System.out.println("您的输入有误!!!"); break;
}
}
}
这样,不管用户输入啥,咱都能做出“友好”的处理:
二:
给一个不多于5位的正整数, 要求:
①输出它是几位数
②分别打印出每一位数字
③逆序打印出各位数字,例如原数为321,应输出123
程序本身比较简单,实现如下:
import java.util.InputMismatchException;
import java.util.Scanner;
/**
*
* @author sixibiheye
* @date 2021/10/12
* @apiNote Java练习二:
* 给一个不多于5位的正整数,要求:
* ①求出它是几位数:
* ②分别打印出每一位数字
* ③逆序打印出各位数字,例如原数为321,应输出123
*/
public class Question12 {
public static void main(String[] args) {
// 用一个数组存储个位,十位,百位,千位,万位
int[] number = new int[5];
// 位数
int places;
Scanner scanner = new Scanner(System.in);
long num;
while(true){
System.out.print("请输入一个整数(0~99999):");
try {
num = scanner.nextLong();
if(num <= 99999 && num >= 0){
break;
}
System.out.println("输入有误,整数范围为0~100!");
} catch (InputMismatchException e){
System.out.println("您输入的不是整数!");
scanner.next();
}
}
if(num > 9999) {
places = 5;
} else if(num > 999) {
places = 4;
} else if(num > 99) {
places = 3;
} else if(num > 9) {
places = 2;
} else {
places = 1;
}
System.out.print(num + "是一个" + places + "位数,");
// 计算第一位
number[0] = (int)num/10000;
// 计算第二位
number[1] = (int)(num - number[0]*10000)/1000;
// 计算第三位
number[2] = (int)(num - number[0]*10000 - number[1]*1000)/100;
// 计算第四位
number[3] = (int)(num - number[0]*10000 - number[1]*1000 -number[2]*100)/10;
// 计算第五位
number[4] = (int)(num - number[0]*10000 - number[1]*1000 -number[2]*100 -number[3]*10);
// 输出结果
System.out.print("该数的各位数字分别为:");
switch (places){
case 5:
for(int i = 0; i < 5; i++){
System.out.print(number[i]);
System.out.print(' ');
}
System.out.print(",逆序输出结果:");
for(int i = 4; i >= 0; i--){
System.out.print(number[i]);
}
break;
case 4:
for(int i = 1; i < 5; i++){
System.out.print(number[i]);
System.out.print(' ');
}
System.out.print(",逆序输出结果:");
for(int i = 4; i >= 1; i--){
System.out.print(number[i]);
}
break;
case 3:
for(int i = 2; i < 5; i++){
System.out.print(number[i]);
System.out.print(' ');
}
System.out.print(",逆序输出结果:");
for(int i = 4; i >= 2; i--){
System.out.print(number[i]);
}
break;
case 2:
for(int i = 3; i < 5; i++){
System.out.print(number[i]);
System.out.print(' ');
}
System.out.print(",逆序输出结果:");
for(int i = 4; i >= 3; i--){
System.out.print(number[i]);
}
break;
case 1:
for(int i = 4; i < 5; i++){
System.out.print(number[i]);
System.out.print(' ');
}
System.out.print(",逆序输出结果:");
for(int i = 4; i >= 4; i--){
System.out.print(number[i]);
}
break;
default:
System.out.println("你输入的不合规范!");
}
}
}
同样的,我们在while循环里对可能出现的Bug做了处理,保证程序的健壮性:
程序中最后的输出代码段略显冗杂,小伙伴们可以尝试着做出改善。
三:
企业发放的奖金来自利润提成。
利润 i ≤ 100000的,可提成10%为奖金,
100000< i ≤200000时,低于10万元的部分么时候按10%提成,高于10万元的部分可提成7.5%,
200000< i ≤400000时,低于20万的部分仍按上述办法提成(下同),高于20万元的部分按5%提成,
400000< i ≤600000时,高于40万元的部分按3%提成,
600000< i ≤1000000时,高于60万元的部分按1.5%提成,
i > 1000000时超过100万元的部分按1%提成,
从键盘输入当月利润 i ,输出应发奖金总数。
要求:(1)用if语句编程序; (2)用switch语句编程序
只需要理清奖金的计算方式,很容易写出程序:
import java.util.Formatter;
import java.util.Scanner;
/**
*
* @author sixibiheye
* @date 2021/10/12
* @apiNote Java基础练习三
* 企业发放的奖金来自利润提成,
* 利润i≤100000的,可提成10%为奖金,
* 100000<i≤200000时,低于10万元的部分么时候按10%提成,高于10万元的部分可提成7.5%,
* 200000<i≤400000时,低于20万的部分仍按上述办法提成(下同),高于20万元的部分按5%提成,
* 400000<i≤600000时,高于40万元的部分按3%提成,
* 600000<i≤1000000时,高于60万元的部分按1.5%提成,
* i>10000时超过100万元的部分按1%提成,
* 从键盘输入当月利润i,求应发奖金总数。
* 要求:(1)用if语句编程序;
* (2)用switch语句编程序
*/
public class Question13 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 定义常量
final double RATE_1 = 0.1f;
final double RATE_2 = 0.075f;
final double RATE_3 = 0.05f;
final double RATE_4 = 0.03f;
final double RATE_5 = 0.015f;
final double RATE_6 = 0.01f;
System.out.print("请输入当月利润:");
double i = scanner.nextDouble();
// 缩小量级
i = i / 100000;
// 方法一:用if语句
// 奖金总数
double y1;
if (i <= 1) {
y1 = i * RATE_1;
} else if (i <= 2) {
y1 = 1 * RATE_1 + (i - 1) * RATE_2;
} else if (i <= 4) {
y1 = 1 * RATE_1 + 1 * RATE_2 + (i - 2) * RATE_3;
} else if (i <= 6) {
y1 = 1 * RATE_1 + 1 * RATE_2 + 2 * RATE_3 + (i - 4) * RATE_4;
} else if (i <= 10) {
y1 = 1 * RATE_1 + 1 * RATE_2 + 2 * RATE_3 + 4 * RATE_4 + (i - 6) * RATE_5;
} else {
y1 = 1 * RATE_1 + 1 * RATE_2 + 2 * RATE_3 + 4 * RATE_4 + 6 * RATE_5 + (i - 10) * RATE_6;
}
// 保留两位小数
String revenue1 = new Formatter().format("%.2f",y1*100000).toString();
// 输出结果
System.out.println("当月利润为" + i*100000 + "元的时候,奖金总数为:" + revenue1 + "元。");
// 方法二:用switch语句
// 奖金总数,先赋值为0
double y2 = 0;
int c = (int) i;
if (c > 10){
c = 10;
}
switch(c){
case 0 : y2 = i * RATE_1; break;
case 1 : y2 = 1 * RATE_1 + (i - 1) * RATE_2; break;
case 2 :
case 3 : y2 = 1 * RATE_1 + 1 * RATE_2 + (i - 2) * RATE_3; break;
case 4 :
case 5 : y2 = 1 * RATE_1 + 1 * RATE_2 + 2 * RATE_3 + (i - 4) * RATE_4; break;
case 6 :
case 7 :
case 8 :
case 9 : y2 = 1 * RATE_1 + 1 * RATE_2 + 2 * RATE_3 + 4 * RATE_4 + (i - 6) * RATE_5; break;
case 10 : y2 = 1 * RATE_1 + 1 * RATE_2 + 2 * RATE_3 + 4 * RATE_4 + 6 * RATE_5 + (i - 10) * RATE_6; break;
default:
System.out.println("输入错误!"); break;
}
// 保留两位小数
String revenue2 = new Formatter().format("%.2f",y2*100000).toString();
// 输出结果
System.out.println("当月利润为" + i*100000 + "元的时候,奖金总数为:" + revenue2 + "元。");
}
}
此程序中的代码稍显冗杂,也可以考虑一下如何精简我们的代码。
运行结果:
此处用户有输入,所以为了保证程序的健壮性,同前面一样,可以对输入的数据进行限制,留给小伙伴们思考吧。
四:
输入四个整数,要求按从小到大的顺序输出
排序算法是算法领域最基础的一种算法,此处提供四种基础算法:
- 插入排序
- 选择排序
- 冒泡排序
- 快速排序
算法的原理在代码中说明:
import java.util.Scanner;
/**
*
* @author sixibiheye
* @date 2021/10/13
* @apiNote Java练习四:
* 输入四个整数,要求按从小到大的顺序输出
*
*
*/
public class Question14 {
/**
* 插入排序算法
*
* 思想:
* 假定第一个元素有序,
* 取一个临时变量存放第二个元素的值,
* 将第二个元素与第一个元素比较,
* 如果第二个元素大于第一个元素,两个元素交换位置,否者,不做移动。
* 继续,将临时变量的值改为第三个元素的值,
* 第三个元素与第二个比较,
* 如果小于第二个,将第二个元素向右移动,再与第一个元素比较,
* 如果小于第一个,将第一个元素向右移,最后将临时变量的值放入第一个元素位置。
*
*
*/
public static void insertSort (int[] numbers) {
int size = numbers.length;
for (int i = 1; i < size; i++) {
// 保存每次需要插入的那个数
int temp = numbers[i];
int j;
//把大于临时变量temp的数往后移动,不大于就不移动,最后空的位置就是temp的位置
for (j = i; j > 0 && numbers[j - 1] > temp; j--) {
numbers[j] = numbers[j - 1];
}
// 将temp放入这个位置
numbers[j] = temp;
}
System.out.print("这四个数从小到大的顺序为:");
for (int value : numbers) {
System.out.print(value);
System.out.print(' ');
}
}
/**
*
* 选择排序算法
*
* 思想:
* 对于第一个数,从后面的元素中找到最小的数与之交换
* 再对第二个数,从后面的元素中找到最小的数与之交换
* 直到最后一个数
*
*/
private static void selectSort (int[] numbers){
int size = numbers.length;
for(int i = 0; i < size; i++){
for (int j = i + 1; j < numbers.length; j++) {
if(numbers[i] > numbers[j]){
// 交换
int temp = numbers[i];
numbers[i] = numbers[j];
numbers[j] = temp;
}
}
}
System.out.print("这四个数从小到大的顺序为:");
for (int value : numbers) {
System.out.print(value);
System.out.print(' ');
}
}
/**
*
* 冒泡排序算法
*
* 思想:
* 从第一个数开始,将自己与自己右边的一个数比较大小,如果大于右边的数,两个数交换位置,否则不交换,
* 第一次外层循环结束,会找到最大的数,且被交换到最后一个位置
* 第二次外城循环结束,会找到第二大的数,且被交换到倒数第二位置,
* 如此下去,直到第一个数
*
*
*/
private static void bubbleSort (int[] numbers) {
int size = numbers.length;
//外层循环控制排序多少趟
for (int i = 0; i < size-1; i++) {
//内层循环控制每趟排序多少次
for (int j = 0; j < size-1-i; j++) {
if(numbers[j] > numbers[j+1]) {
// 交换
int temp = numbers[j];
numbers[j] = numbers[j+1];
numbers[j+1] = temp;
}
}
}
System.out.print("这四个数从小到大的顺序为:");
for (int value : numbers) {
System.out.print(value);
System.out.print(' ');
}
}
/**
*
* 快速排序算法
*
* 思想:
* 1. 将第一个数存入一个临时变量,作为基准数,
* 2. 先从后往前,找到第一个比基准数小的数(记录该数的位置,我们用R表示),
* 3. 再从前往后,找到第一个比基准数大的值(记录该数的位置,我们用L表示),
* 4. 交换R和L位置对应的数
* 5. 重复2,3,4步
*
* 直到 L=R,结束该次循环,此时将基准数放到L(R)位置,
*
* 这时基准数左边的数都比基准数小,右边的数都比基准数大,
* 把基准数左边的数看作一个新的数组,重复 1,2,3,4 步
* 把基准数右边的数看作一个新的数组,重复 1,2,3,4 步 (利用递归)
*
*
*/
// 先排序
private static void quick(int[] numbers,int l ,int r) {
int start = l;
int end = r;
if (l >= r) {
return;
}
// 基准数
int key = numbers[l];
// 如果L和R不相同
while (l < r) {
// 从后往前,找到第一个比基准数小的数,记录该数的位置
while (l < r && key <= numbers[r]) {
r -= 1;
}
// 交换这两的位置
numbers[l]=numbers[r];
// 从前往后,找到第一个比基准数大的值,记录该数的位置
while (l < r && key >= numbers[l]) {
l += 1;
}
// 交换这两的位置
numbers[r] = numbers[l];
}
// 将基准数放到L(R)位置,注意此时numbers[r] = numbers[l]
numbers[l] = key;
// 一直递归即可
quick(numbers, start, l - 1);
quick(numbers, l + 1, end);
}
// 再输出结果
private static void quickSort(int[] numbers,int l ,int r){
quick(numbers,l,r);
System.out.print("这四个数从小到大的顺序为:");
for (int value : numbers) {
System.out.print(value);
System.out.print(' ');
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 用数组存储这四个数
int[] numbers = new int[4];
// 用户输入
System.out.print("比较大小,请输入第一个整数:");
numbers[0] = scanner.nextInt();
System.out.print("比较大小,请输入第二个整数:");
numbers[1] = scanner.nextInt();
System.out.print("比较大小,请输入第三个整数:");
numbers[2] = scanner.nextInt();
System.out.print("比较大小,请输入第四个整数:");
numbers[3] = scanner.nextInt();
//方法一:插入排序
insertSort(numbers); System.out.println(); //换行
//方法二:选择排序
selectSort(numbers); System.out.println(); //换行
//方法三:冒泡排序
bubbleSort(numbers); System.out.println(); //换行
//方法四:快速排序
quickSort(numbers,0,numbers.length - 1); System.out.println(); //换行
}
}
同样的,可以对用户输入作出限制,小伙伴们自己实现吧。运行结果如下:
五:
输人两个正整数m和n,求其最大公约数和最小公倍数
一道典型练习,此处提供三种思路:
import java.util.ArrayList;
import java.util.Scanner;
/**
* @author sixibiheye
* @date 2021/10/13
* @apiNote Java练习五:
* 输人两个正整数m和n,求其最大公约数和最小公倍数
*/
public class Question15 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("求最大公约数和最小公倍数,请输入第一个数:");
int a = scanner.nextInt();
System.out.print("第二个数:");
int b = scanner.nextInt();
// 计算二者乘积,用于计算最小公倍数
int muti = a * b;
// 方法一:穷举法
int gcd1 = getGCD1(a,b);
int lcm1 = muti / gcd1;
System.out.println("穷举法求这两个数的最大公约数是:" + gcd1 + ",最小公倍数是:" + lcm1 + '。');
// 方法二:质因数法
int gcd2 = getGCD2(a,b);
int lcm2 = muti / gcd1;
System.out.println("质因数法求这两个数的最大公约数是:" + gcd2 + ",最小公倍数是:" + lcm2 + '。');
// 方法三:辗转相除法
int gcd3 = getGCD3(a,b);
int lcm3 = muti / gcd1;
System.out.println("辗转相除法求这两个数的最大公约数是:" + gcd3 + ",最小公倍数是:" + lcm3 + '。');
}
/**
* @description
* 穷举, 从较小的数往前穷举, 速度较慢
*
* @param num1 第一个数
* @param num2 第二个数
* @return 最大公约数
*
*/
public static int getGCD1(int num1, int num2) {
// 支持负数
num1 = Math.abs(num1);
num2 = Math.abs(num2);
// 找到小的那个数穷举, 直接从较小数开始
int gcd = Math.min(num1, num2);
while (gcd > 1) {
// 如果 gcd 能被两个数同时约分,则就是最大公约数,注意是往前穷举
if (num1 % gcd == 0 && num2 % gcd == 0) {
// 直接返回最大公约数
return gcd;
}
// 否则 gcd 继续减小,往前穷举
gcd--;
}
return gcd;
}
/**
*
* @return 返回一个数的质因子序列(数组形式), 这里忽略1
*
*/
public static ArrayList<Integer> getPrimeFactors(int num) {
// 创建一个数组用于存储所有的质因子
ArrayList<Integer> factorList = new ArrayList<>();
int i = 2;
while (i <= num) {
// 如果i是num的因子
if (num % i == 0) {
// 就加入序列
factorList.add(i);
// 下一个质因子一定小于num / i
num /= i;
// i从2开始
i = 2;
} else {
// 否则继续
i++;
}
}
return factorList;
}
/**
*
* @description
* 质因数分解法,将两个数的所有质因子分解出来,
* 然后将公共的因子相乘,得到的就是最大公约数,速度比较慢
*
* @param num1 第一个数
* @param num2 第二个数
* @return 最大公约数
*
*
*/
public static int getGCD2(int num1, int num2) {
// 支持负数
num1 = Math.abs(num1);
num2 = Math.abs(num2);
// 获得两个数的质因子序列
ArrayList<Integer> factors1 = getPrimeFactors(num1);
ArrayList<Integer> factors2 = getPrimeFactors(num2);
// 先初始化
int gcd = 1;
// 从头开始找公共的质因子,相乘起来
for (Integer factor : factors1) {
int size = factors2.size();
for (int j = 0; j < size; j++) {
if (factor.equals(factors2.get(j))) {
// 将公共的质因子累乘
gcd *= factor;
// num2的质因子序列要去掉已经找到的,防止重复找
factors2.remove(j);
// 此处一次只找一对公共的质因子,减少循环次数
j = factors2.size();
}
}
}
return gcd;
}
/**
* @description
* 辗转相除法, 高效稳定 <br>
* 原理: gcd(a, b) = gcd(b, a mod b)
* @param num1 第一个数
* @param num2 第二个数
* @return 最大公约数
*
*/
public static int getGCD3(int num1, int num2) {
// 支持负数
num1 = Math.abs(num1);
num2 = Math.abs(num2);
// 先使得num1小于num2,便于使用辗转相除法
if(num1 > num2){
int temp = num1;
num1 = num2;
num2 = temp;
}
while(num1 != 0){
//余数
int r = num2 % num1;
num2 = num1;
num1 = r;
}
return num2;
}
}
思路很简单,但需要注意一些细节,在注释中都有说明。运行结果如下:
六:
输入一行字符,分别统计出其中英文字母、空格、数字和其他字符的个数
此处如何统计应该是本题的一个难点。笔者暂时还没有找到更好的办法,只能使用正则表达式,提供如下思路:
import java.util.*;
/**
*
* @author sixibiheye
* @date 2021/10/13
* @apiNote Java练习六:
* 输入一行字符,分别统计出其中英文字母、空格、数字和其他字符的个数
*
*/
public class Question16 {
public static void main(String[] args) {
System.out.print("请输入一串字符:");
Scanner scan = new Scanner(System.in);
String str = scan.nextLine();
// 统计
count(str);
}
private static void count(String str) {
// 利用正则表达式
String E1 = "[a-zA-Z]";
String E2 = "[0-9]";
//空格
String E3 = "\\s";
int countLetter = 0;
int countNumber = 0;
int countSpace = 0;
int countOther = 0;
// 若含有汉字,需将字符串转化为字符数组,再添加进一个字符串数组中
char[] arrayChar = str.toCharArray();
String[] arrayString = new String[arrayChar.length];
// System.out.println(Arrays.toString(arrayString));
// 将 "我是Grace!" 转化为 ['我','是','G','r','a','c','e','!']
for(int i=0;i<arrayChar.length;i++) {
arrayString[i] = String.valueOf(arrayChar[i]); // String.valueOf()用于转化为字符串
}
//遍历字符串数组中的元素
for (String s : arrayString) {
if (s.matches(E1)) {
countLetter++;
} else if (s.matches(E2)) {
countNumber++;
} else if (s.matches(E3)) {
countSpace++;
} else {
countOther++;
}
}
// 输出统计结果
System.out.println("输入的字母个数:" + countLetter + ',');
System.out.println("输入的数字个数:" + countNumber + ',');
System.out.println("输入的空格个数:" + countSpace + ',');
System.out.println("输入的其它字符个数:" + countOther + '。');
}
}
此处用到了许多零碎的知识,比如正则表达式,String的valueOf()方法,toCharArray()方法等,请小伙伴们好好回顾一下这些零碎的东西。
小伙伴们如果有其他更好的方法,欢迎评论。
运行结果如下:
七:
求Sn=a+aa+aaa+…+aaa...(n个a)之值 , 其中a是一个数字。
例如:2 + 22 + 222 + ... +222...(n个2) = ?
程序编写本身不会有很大的问题,如下:
import java.math.BigInteger;
import java.util.InputMismatchException;
import java.util.Scanner;
/**
* @author sixibiheye
* @date 2021/10/13
* @apiNote Java练习七:
* 求Sn=a+aa+aaa+…+aaa...(n个a)之值,其中a是一个数字。
* 例如:2 + 22 + 222 + ... (n个2)
*
*/
public class Question17 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int a , n;
// 输入控制
while(true){
try {
System.out.print("请输入数字a:");
a = scanner.nextInt();
if(a < 0 || a >9){
System.out.println("数字,只能是0~9!");
continue;
}
System.out.print("请输入整数n:");
n = scanner.nextInt();
break;
} catch (InputMismatchException e){
System.out.println("输入有误,请输入一个整数!");
scanner.next();
}
}
// sn为总和,tn为aaa...
long sn = 0, tn =0;
for (int i = 0; i < n; i++) {
tn += a;
sn += tn;
a *= 10;
}
System.out.println("a+aa+aaa+...+aaa...(n个a) = " + sn);
}
}
我们对用户的输入做了简单的控制,运行如下:
但是,很显然,这个程序对于n非常大的情况就无能为力了,比如:
根本原因在于,100个4连起来的数已经超过了Java中Long型数据的最大值()了,一个解决方式为,将变量sn , tn改为float类型(范围大约为:
)或者double类型(范围大约为:
),改成double类型后,尝试运行如下:
确实要比Long类型的范围大许多,但是,如果我们追求完美的话,当n > 1000时,这个结果又出Bug了。
对于整数的最大值,利用Java中的基础数据类型显然是无法解决上述问题的。
其实,Java中的 math 库提供了一个 BigInteger 类专门用于处理大数。有多大呢?理论上,只要你的内存够大,它可以装下任何一个整数。
简单介绍一下这个类。它的构造方法传入一个字符串:
BigInteger bigInt1 = new BigInteger("10000000000000000");
BigInteger bigInt2 = new BigInteger("20000000000000000");
此时对象bigInt1便储存了10000000000000000这个数据,而这个数据可以非常大,比如 。
注意此处的 bigInt1 , bigInt2 的类型是 BigInteger ,因此无法使用简单的
,
,
,
,
等运算符号对bigInt1 , bigInt2进行运算,但在这个类中提供了加减乘除等基础运算对应的方法:
import java.math.BigInteger;
public class BigIntegerDemo {
public static void main(String[] args) {
BigInteger bi1 = new BigInteger("123456789") ; // 声明BigInteger对象
BigInteger bi2 = new BigInteger("987654321") ; // 声明BigInteger对象
System.out.println("加法操作:" + bi2.add(bi1)) ; // 加法操作
System.out.println("减法操作:" + bi2.subtract(bi1)) ; // 减法操作
System.out.println("乘法操作:" + bi2.multiply(bi1)) ; // 乘法操作
System.out.println("除法操作:" + bi2.divide(bi1)) ; // 除法操作
System.out.println("最大数:" + bi2.max(bi1)) ; // 求出最大数
System.out.println("最小数:" + bi2.min(bi1)) ; // 求出最小数
BigInteger result[] = bi2.divideAndRemainder(bi1) ; // 求出余数的除法操作
System.out.println("商是:" + result[0] +
";余数是:" + result[1]) ;
}
}
利用 BigInteger 类,上述问题便可以更为完美地解决,实现如下:
import java.math.BigInteger;
import java.util.InputMismatchException;
import java.util.Scanner;
/**
* @author sixibiheye
* @date 2021/10/13
* @apiNote Java练习七:
* 求Sn=a+aa+aaa+…+aaa...(n个a)之值,其中a是一个数字。
* 例如:2 + 22 + 222 + ... (n个2)
*
*/
public class Question17 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int a , n;
while(true){
try {
System.out.print("请输入数字a:");
a = scanner.nextInt();
if(a < 0 || a >9){
System.out.println("数字,只能是0~9!");
continue;
}
System.out.print("请输入整数n:");
n = scanner.nextInt();
break;
} catch (InputMismatchException e){
System.out.println("输入有误,请输入一个整数!");
scanner.next();
}
}
// sn为总和,tn为aaa...
BigInteger sn = new BigInteger("0");
BigInteger tn = new BigInteger("0");
BigInteger ten = new BigInteger("10"); // 常量10
// String.valueOf(a) 用于将a转化为字符串类型
BigInteger aa = new BigInteger(String.valueOf(a));
for (int i = 0; i < n; i++) {
tn = tn.add(aa);
sn = sn.add(tn);
aa = aa.multiply(ten);
}
System.out.println("a+aa+aaa+...+aaa...(n个a) = " + sn);
}
}
输入a = 5 , n = 1000,看运行结果:
也许,生活中用不到那么大的数,但在科学计算中,其还是有一定作用的。
八:
求以下和式的值:
![]()
这题比较容易,实现如下:
/**
* @author sixibiheye
* @date 2021/10/13
* @apiNote Java练习八:
* 求∑n!, n从1到20, (即求 1! + 2! + 3! + ... + 20!)
*/
public class Question18 {
public static void main(String[] args) {
// 求和结果可能大于long类型的最大值2^63-1,此处改用声明为float(38位),或者double(300多位)
float s = 0, t =1;
int n;
for (n = 1; n <= 20; n++) {
t *= n; // 求 n!
s += t; // 累加
}
System.out.println("1!+2!+3!+...+20!=" + s);
}
}
运行结果如下:
九:
输出所有的“水仙花数“。
所谓“水仙花数”是指一个3位数 , 其各位数字的立方和等于该数本身,
例如,153是一水仙花数,因为
。
关键在于获取这个3位数的各个位上的数字:
/**
* @author sixibiheye
* @date 2021/10/13
* @apiNote Java练习九:
* 输出所有的“水仙花数“。
* 所谓“水仙花数”是指一个3位数,其各位数字的立方和等于该数本身,
* 例如,153是一水仙花数,因为153 = 1^3 + 5^3 + 3^3
*/
public class Question19 {
public static void main(String[] args) {
int i,j,k,n;
System.out.print("所有'水仙花数'为:");
for(n = 100; n < 1000; n++){
i = n / 100; // 百位
j = n / 10 - i * 10; // 十位
k = n % 10; // 个位
if(n == i * i *i + j * j * j + k * k * k){
System.out.print(n);
System.out.print(' ');
}
}
}
}
运行结果:
十:
一个数如果恰好等于它的因子之和, 这个数就称为“完全数”,
例如, 6的因子为1,2,3, 而6=1+2+3, 因此6是“完全数”,
编程序找出1000之内的所有完全数,
并按下面格式输出其因子: 6, its factors are 1,2,3
关键在于如何找出一个数的因子:
/**
*
* @author sixibiheye
* @date 2021/10/13
* @apiNote Java练习十:
* 一个数如果恰好等于它的因子之和, 这个数就称为“完数”,
* 例如,6的因子为1,2,3, 而6=1+2+3,因此6是“完数”,
* 编程序找出1000之内的所有完数,并按下面格式输出其因子:
* 6, its factors are 1,2,3
*
*/
public class Question20 {
public static void main(String[] args) {
int m, s, i;
for (m = 2; m <= 1000; m++) {
s = 0;
for (i = 1; i < m; i++) {
if ((m % i) == 0) {
// 因子的和
s = s + i;
}
}
if (s == m) {
System.out.print(m + ",its factors are:");
for (i = 1; i < m; i++) {
if (m % i == 0) {
System.out.print(i);
System.out.print(',');
}
}
// 换行输出下一个完全数
System.out.println();
}
}
}
}
运行结果:
十一:
有一分数序列
求出这个数列的前20项之和。
找规律:
1. 前一项分子分母之和为下一项的分子,
2. 前一项的分子作为下一项的分母。
实现如下:
/**
* @author sixibiheye
* @date 2021/10/13
* @apiNote Java练习十一:
* 有一分数序列
* 2/1, 3/2, 5/3, 8/5, 13/8, 21/13
* 出这个数列的前20项之和
*
*
* 求
*/
public class Question21 {
public static void main(String[] args) {
int i, t;
double a = 2, b = 1, s = 0;
for (i = 0; i < 20; i++) {
s += (a/b);
t = (int) a;
// 前一项分子分母之和为下一项的分子
a = a + b;
// 前一项的分子作为下一项的分母
b = t;
}
System.out.println("2/1+3/2+5/3+8/5+...(20项) = " + s);
}
}
运行结果:
十二:
猴子吃桃问题。
猴子第1天摘下若干个桃子, 当即吃了一半, 还不过瘾, 又多吃了一个,
第2天早上又将剩下的桃子吃掉一半,又多吃了一个,
以后每天早上都吃了前一天剩下的一半另加一个,
到第10天早上想再吃时,就只剩一个桃子了,
求第1天共摘了多少个桃子。
显然,我们只能从第10天往前推:
第K天的桃子数 = ( 第K+1天桃子数 + 1 ) * 2
/**
* @author sixibiheye
* @date 2021/10/13
* @apiNote Java练习十二:
* 猴子吃桃问题。
* 猴子第1天摘下若干个桃子, 当即吃了一半, 还不过瘾, 又多吃了一个,
* 第2天早上又将剩下的桃子吃掉一半,又多吃了一个,
* 以后每天早上都吃了前一天剩下的一半另加一个,
* 到第10天早上想再吃时,就只剩一个桃子了,
* 求第1天共摘了多少个桃子。
*/
public class Question22 {
public static void main(String[] args) {
int day,x1 = 0,x2;
day = 9;
x2 = 1;
// 迭代
while(day > 0){
x1 = (x2 + 1) * 2;
x2 = x1;
day--;
}
System.out.println("猴子第一天共摘了" + x1 + "个桃子。");
}
}
运行结果:
十三:
用迭代法求
,
迭代公式为:
,
要求:前后两次求出的x的差的绝对值小于
。
(......不知道为什么使用LaTex产生的数学式子,会往上偏一点😅 )
此题难点在于递推公式如何使用:
思路:
1. 设定一个x的初值x0;
2. 用以上递推公式求出x的下一个值x1;
3. 再将x1代入右边,求出x2
4. 如此继续下去,直到前后两次的x差值小于1e-5
实现如下:
import java.util.Scanner;
/**
* @author sixibiheye
* @date 2021/10/13
* @apiNote Java练习十三:
* 用迭代法求 x = √a (根号a),迭代公式为:
* x(n+1) = 0.5*(x(n) + a/x(n))
* 要求:前后两次求出的x的差的绝对值小于1e-5
*
* 思路:
* 1. 设定一个x的初值x0;
* 2. 用以上递推公式求出x的下一个值x1;
* 3. 再将x1代入右边,求出x2
* 4. 如此继续下去,直到前后两次的x差值小于1e-5
*/
public class Question23 {
public static void main(String[] args) {
System.out.print("请输入一个正整数:");
Scanner scanner = new Scanner(System.in);
float a = scanner.nextFloat();
float x0 = a / 2;
//计算左边的x
float x1 = (x0 + a / x0) / 2;
// 至少计算一次,此处用do...while循环
do {
x0 = x1;
x1 = (x0 + a / x0) / 2;
} while (Math.abs(x1 - x0) > 1e-5);
System.out.println(a + "的算术平方根为:" + x1 + '。');
}
}
运行结果:
好啦~今天的练习就分享到这了,虽然挺基础的几个练习,但也有着许多我们曾忽略过的东西,希望大家看完之后都有所收获。最后,喜欢的小伙伴们点个赞鼓励支持一下吧~