题目
输入多组数据,每组输入整数a和b,(0<=a<=3000, 1<=b<=3000),输出a/b的循环小数表示以及循环字节长度。
样例输入
76 25
5 43
1 397
样例输出
76/25 = 3.04(0)
1 = number of digits in repeating cycle
(空行)
5/43 = 0.(116279069767441860465)
21 = number of digits in repeating cycle
(空行)
1/397 = 0.(00251889168765743073047858942065491183879093198992...)
99 = number of digits in repeating cycle
思路
非常惭愧,我想了很多天才想明白这个题怎么做。
列竖式是怎么计算小数的
在计算两数相除的时候,幼儿园老师教我们应该列竖式计算。其实追究其本质,我们是怎样计算一个小数的?
以5/7
为例:
- 计算
5/7
,结果是当前位的数值(这是第一次计算,所以这里说的当前位其实是整数部分,后面就不是了) - 计算
5%7
,得出余数m(此时m=5) - 令
a=m*10
,即计算50
和7
,重复上面的两步 - 如此下去,就能通过第一步一直得到小数各位
这里有几个细节:
- 为啥第三步要算
m*10
呢?其实这就相当于列竖式的时候,得出来余数以后,添个0,继续往后算,比如下面这个图片,加上这个红色的0就相当于m*10
的功能
- 第一次计算的时候
a/b
得出的是整数部分数值,但是后面的话是十分位、百分位、千分位等等,肯定是只有一位数字的。这是因为,我们以前经常会遇到遍历整数各个位置,常用的方法是用a%10来取一个数字的个位,其实,第三步就是这种计算的逆运算,运算结果应当也是只有一个位的数字才对。
本题的大致思路
其实,如果我们要是在上面的方法中,计算余数的时候,如果计算出的余数前面出现过,我们就不需要再算了,因为重复出现同一个余数,后面算的数字都一样,试一试就明白了。
也就是说,循环节是两次余数相等的位置中间的数字才对。
一个想法就是,用数组当表,下标代表余数,存储第一次算出这个余数的位置。
第二个问题就是,余数的取值范围肯定是0到b,能取到0,不能取到b,大于等于b是不可能的,这是余数的性质。
进一步的推广一下,循环节的长度不可能超过b。
实现
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
shit(scanner.nextInt(), scanner.nextInt());
}
}
public static void shit(int a, int b) {
int[] arr = new int[3005]; //余数表
int[] fuck = new int[3005];//小数各位数字
System.out.printf("%d/%d = ", a, b);
int y = a / b;
for (int i = 1; i <= b; i++) {
a = (a % b) * 10;
int r = a % b;
int x = a / b;
fuck[i] = x;
if (arr[r] == 0 || (arr[r] != 0 && fuck[arr[r]] != fuck[i])) {
arr[r] = i;
} else {
int t = i - arr[r];
System.out.printf("%d.", y);
for (int j = 1; j < arr[r]; j++)
System.out.printf("%d", fuck[j]);
System.out.printf("(");
for (int j = arr[r]; j < Math.min(i, arr[r] + 50); j++) {
System.out.printf("%d", fuck[j]);
}
if (t > 50) System.out.print("...");
System.out.printf(")\n");
System.out.printf(" %d = number of digits in repeating cycle\n\n", t);
break;
}
}
}
}