一道有趣的题 -- 将分数转成小数

本文详细描述了如何使用Java编程语言实现将分数转换为小数,包括竖式除法的思路,循环部分的识别,以及如何使用数据结构如HashMap和ArrayList来跟踪商和余数,最终提供了一个完整的FractionToDecimal类的示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一道有趣的题 – 将分数转成小数

题目

写一个函数,入参是一个分子和一个分母,都是int型,返回值是一个String类型,代表该分数的小数。
若该小数有循环部分,则循环部分用括号括起来。
假设分母必定不为0.

public String fractionToDecimal(int a, int b)

举例

假设a是分子,b是分母
Case-1

Input: a=1, b=2
Output: "0.5"

Case-2

Input: a=2, b=1
Output: "2"

Case-3

Input: a=4, b=333
Output: "0.(012)"

思考

这道题首先应该思考的是,在数学上是怎么实现分数转小数的,即,竖式除法是怎么做的。然后再把它转成程序。
这里有个难点是循环部分怎么识别和处理?所以我们先不看这个case,先从简单的开始,即Case-1.
对于竖式除法,不够除就填0,然后相当于把被除数扩大10倍再接着除;等到够除了,就先填一个商,然后求出余数,接下来再把余数乘以10,再接着除。一直除到余数为0,或余数出现循环部分为止。

说起来似乎有点赘述的感觉。画个表格就很好理解了。

r * 10被除数 a除数 b商 c余数 r
r * 101201
r * 1010250

通过上表,我们把商的部分连接起来,就是"0.5"了。

好了,工具有了,现在来看那个循环的例子 Case-3

r * 10被除数 a除数 b商 c余数 r
r * 10433304
r * 1040333040
r * 10400333167
r * 1067033324
r * 1040333040
r * 10400333167
r * 1067033324

从上表中, 把商的数字从上往下连起来,就是0012012…,写成无限循环小数就是 “0.(012)”.

怎么决定循环部分呢?
仔细观察上表,会发现,当余数第一次出现和之前重复的时候,就是出现循环了。
那么这个循环的起始位置是在哪里呢?
还是仔细观察上表:循环的起点位置是在所重复的余数第一次出现的位置下一个位置(即上表的第2行),循环的终点位置是所重复的余数第二次出现的位置(即上表的第4行)。

由以上分析,我们也就能够得知,画这个表是什么时候结束了。两种情况皆可结束:一是余数为0;二是余数第一次出现重复的时候。

画上表的过程也就是程序处理的过程。
先写一点伪代码。

int a = xxx, b = yyy;

c = a / b;
r = a - b * c;

if (r == 0) {
  // generate result
  ...
  return result; 
}

while (true) {
  a = r * 10;
  c = a / b; 
  r = a - b * c;
  if (r == 0 || r occurs again) {
    // generate result 
    ...
    return result;
  }
}

最后一个小问题,也是上述伪代码没有解答的问题:怎么知道r重复了呢?又怎么知道上一个一样的r的位置呢(用来写左括号)?
稍有点编程功力的应该很容易想到:

  1. 用一个列表记录下上表中的所有商c
  2. 用一个HashMap记录下所有余数r以及当时它们在表中的位置(即处于第几行),即key为r,value为位置

好了,主要问题都解决了。还有什么遗漏呢?
如果按照这样写出程序了,至少还有2点遗漏:

  1. 正负问题:所返回的String可能应该带一个负号
  2. 溢出问题:因为a = r * 10, 这就可能造成被除数a超出了int型的范围

这2个问题都很好解决。不再赘述。
完整的实现代码如下:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class FractionToDecimal {
    public String fractionToDecimal(int numerator, int denominator) {
        long a = numerator, b = denominator;
        boolean positive = (a>=0 && b>0) || (a<=0 && b<0);
        if (a < 0) a = -a;
        if (b < 0) b = -b;

        long c = a / b;
        long r = a - b * c;
        if (r == 0) {
            return positive ? Long.toString(c) : "-" + c;
        }

        int index = 0;
        List<Long> quotientList = new ArrayList<>(List.of(c));
        Map<Long, Integer> remainderMap = new HashMap<>(Map.of(r, index));  // r -> index of c

        while (true) {
            a = r * 10;
            c = a / b;
            r = a - b * c;
            quotientList.add(c);
            index ++;
            if (r == 0 || remainderMap.containsKey(r)) break;
            remainderMap.put(r, index);
        }

        StringBuilder sb = new StringBuilder();
        if (!positive) sb.append("-");
        sb.append(Long.toString(quotientList.get(0)))
                .append(".");

        int firstLoopRemainderIndex = -1;
        if (r != 0) {
            firstLoopRemainderIndex = remainderMap.get(r);
            if (firstLoopRemainderIndex == 0) {
                sb.append("(");
            }
        }
        for(int i=1; i<quotientList.size(); i++) {
            sb.append(Long.toString(quotientList.get(i)));
            if (i == firstLoopRemainderIndex) {
                sb.append("(");
            }
        }
        if (r!=0) sb.append(")");
        return sb.toString();
    }

    public static void main(String[] args) {
        FractionToDecimal ftd = new FractionToDecimal();
        int a = 1;
        int b = 2;
        System.out.printf("%d / %d = %s%n", a, b, ftd.fractionToDecimal(a, b));

        a = 4;
        b = 333;
        System.out.printf("%d / %d = %s%n", a, b, ftd.fractionToDecimal(a, b));

        a = 40;
        b = 333;
        System.out.printf("%d / %d = %s%n", a, b, ftd.fractionToDecimal(a, b));

        a = -6;
        b = 5;
        System.out.printf("%d / %d = %s%n", a, b, ftd.fractionToDecimal(a, b));

        a = 1;
        b = 6;
        System.out.printf("%d / %d = %s%n", a, b, ftd.fractionToDecimal(a, b));

        a = 10;
        b = 23;
        System.out.printf("%d / %d = %s%n", a, b, ftd.fractionToDecimal(a, b));
    }
}

运行结果为:

1 / 2 = 0.5
4 / 333 = 0.(012)
40 / 333 = 0.(120)
-6 / 5 = -1.2
1 / 6 = 0.1(6)
10 / 23 = 0.(4347826086956521739130)

(END)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值