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

本文详细描述了如何使用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: sdut-oop-8 分数四则运算(类和对象)是一道面向对象编程的目,要求实现分数的加减乘除四则运算。具体实现方式是通过定义一个分数类,包含分子和分母两个属性,以及加减乘除四个方法,实现分数的四则运算。在使用时,需要创建分数对象,调用相应的方法进行运算。这道目考察了面向对象编程的基本概念和实现方法,对于提高编程能力和理解面向对象编程思想有很大的帮助。 ### 回答2: 分数四则运算是中学数学中一个非常常见且重要的知识点。在程序开发中,我们也需要实现分数的四则运算。本要求我们使用面向对象的思想来设计实现一个分数类,能够支持分数的加、减、乘、除等运算。 首先,我们可以定义一个Fraction类,其中包含分子numerator和分母denominator两个成员变量,以及足够的构造函数、析构函数和成员函数,来分别实现加、减、乘、除等操作。 在分数加法操作中,我们需要将两个分数的分子分母求最小公倍数,然后将两个数的分子乘以公倍数再相加,最后将结果分子分母同时约分即可。在分数减法和乘法操作中,可以采用类似的方法。而在除法操作中,我们需要将除数转成倒数,然后进行分数乘法操作,最后还要注意特殊情况(如除数为0)的处理。 除了上述基本的四则运算,我们还可以在Fraction类中加入其他的成员函数,如约分、比较大小、转换为小数等。为了保证程序的健壮性,我们还可以加入一些异常处理机制,如除数为0时抛出异常,分子或分母为负数时修复符号等。 最后,在使用Fraction类进行分数四则运算时,我们需要注意运算符重载的使用。这样既方便了程序员的操作,也使得程序的可读性和可维护性更高。 综上所述,分数四则运算的实现需要考虑到各种特殊情况,并采用面向对象的思想,合理设计类的成员函数和运算符重载,从而实现分数的加、减、乘、除等操作。 ### 回答3: 目描述: 编程序实现分数的四则运算,包括加、减、乘、除。每个分数由分子和分母两部分组成,分数的运算结果应该以最简分数形式输出。 思路: 首先需要定义一个Fraction类,其中包括分子和分母两个成员变量,以及相应的构造函数、成员函数和友元函数。 构造函数需要初始分子和分母,默认为1和1,可以通过传入分子和分母的值来实现初始。 成员函数需要实现分数的加、减、乘、除运算,以及求最大公约数的函数。具体实现方法如下: 加法运算:将两个分数的分子分母通分后相加,再将结果简为最简分数。 减法运算:将两个分数为同分母后相减,再将结果简为最简分数。 乘法运算:将两个分数的分子相乘,分母相乘,再将结果简为最简分数。 除法运算:将除数取倒数后再与被除数相乘,再将结果简为最简分数。 最大公约数的求法:使用辗转相除法,在分子和分母之间进行循环操作,直到较小的一个数为0,此时较大的数就是最大公约数。 友元函数需要实现输入输出运算符的重载,以实现输入和输出分数的操作。 代码实现如下: #include<iostream> #include<cmath> using namespace std; //Fraction类定义 class Fraction { public: Fraction(int num = 1, int deno = 1) : numerator(num), denominator(deno) {}//构造函数,默认为1/1 Fraction operator + (Fraction& x);//加法运算重载 Fraction operator - (Fraction& x);//减法运算重载 Fraction operator * (Fraction& x);//乘法运算重载 Fraction operator / (Fraction& x);//除法运算重载 friend ostream& operator << (ostream& os, Fraction& x);//输出运算符重载 friend istream& operator >> (istream& is, Fraction& x);//输入运算符重载 int gcd(int a, int b);//求最大公约数 private: int numerator;//分子 int denominator;//分母 }; //加法运算 Fraction Fraction::operator + (Fraction& x) { Fraction result; result.numerator = numerator * x.denominator + x.numerator * denominator; result.denominator = denominator * x.denominator; int g = gcd(result.numerator, result.denominator); result.numerator /= g; result.denominator /= g; return result; } //减法运算 Fraction Fraction::operator - (Fraction& x) { Fraction result; result.numerator = numerator * x.denominator - x.numerator * denominator; result.denominator = denominator * x.denominator; int g = gcd(result.numerator, result.denominator); result.numerator /= g; result.denominator /= g; return result; } //乘法运算 Fraction Fraction::operator * (Fraction& x) { Fraction result; result.numerator = numerator * x.numerator; result.denominator = denominator * x.denominator; int g = gcd(result.numerator, result.denominator); result.numerator /= g; result.denominator /= g; return result; } //除法运算 Fraction Fraction::operator / (Fraction& x) { Fraction result; result.numerator = numerator * x.denominator; result.denominator = denominator * x.numerator; int g = gcd(result.numerator, result.denominator); result.numerator /= g; result.denominator /= g; return result; } //输出运算符重载 ostream& operator << (ostream& os, Fraction& x) { os << x.numerator << '/' << x.denominator; return os; } //输入运算符重载 istream& operator >> (istream& is, Fraction& x) { is >> x.numerator >> x.denominator; return is; } //求最大公约数 int Fraction::gcd(int a, int b) { if (b == 0) return a; else return gcd(b, a % b); } 测试代码如下: int main() { Fraction a, b; cin >> a >> b; cout << "a+b=" << a + b << endl; cout << "a-b=" << a - b << endl; cout << "a*b=" << a * b << endl; cout << "a/b=" << a / b << endl; return 0; } 运行结果如下: 2 3 1 6 a+b=11/6 a-b=7/6 a*b=1/9 a/b=4/1
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值