一个来自于C++程序设计的经典问题。如何定义一个分数类,实现分数的约分化简,分数之间的加法、减法、乘法、除法四则运算?

1.初见

刚看到这道题的时候,第一感觉是挺简单的啊,就是基本的面向对象,定义对应的加减乘除类就可以了啊,然而到了实现的时候才发现许多问题是说起来容易做起来难,在实现的过程中,发现了许多的注意点,以及算法。

最终得出结论:这个问题着实是考察程序员基本功的一道好题。

2.整体思路

分数类设计的总体思路如下:

首先是分数的表示,这就需要利用两个变量保存分数的分子和分母;

其次是约分和通分,由于分数四则运算中需要借助约分和通分来实现,因此必须先考虑实现这两个算法。

加法和乘法的实现。利用约分和通分就可以轻松实现。

减法和除法的实现。是加法和乘法的逆运算。和直接转化为加法和乘法。

3.注意事项

负数问题。这个问题十分重要,在我们的算法中,都规定分母为正数,如果出现了分母为负数的情况,就分子分母同时乘以-1,把负数运算放在分子上。

函数的副作用(side effect)。尽量不要在方法中改变原来分数的值,否则会产生副作用,导致后面的运算出错,在代码中会说明。

静态方法和动态方法。和上一点是一样的问题,要确定方式是属于具体的对象,还是属于一个类。

除法运算中,除数不能等于0

4.代码实现

4.1 属性和构造方法

构造方法中有三个细节:一是使用了参数默认值,默认不写参数时,让分子分母都等于1;二是在构造方法中进行了分母合法性的验证,分母等于0时直接返回错误信息;三是对负值进行了处理,使分数的分母永远为正数,方便后续运算。

/**

* 分数运算类

*/

class Fraction

{

//定义分子和分母

public $fenzi;

public $fenmu;

//构造函数

function __construct($fenzi = 1,$fenmu = 1)

{

if ($fenmu == 0) {

return "分母不能为0";

}

if ($fenmu < 0) {

$fenmu = -$fenmu;

$fenzi = -$fenzi;

}

$this->fenzi = $fenzi;

$this->fenmu = $fenmu;

}

}

4.2 最大公约数和最小公倍数

为了后续的约分和通分,必须先求出最大公约数和最小公倍数。求最大公约数采用辗转相除法,而最小公倍数由以下公式可求:

最小公倍数 = (数A * 数B)/ 最大公约数

//求最大公约数用于约分

private static function _getmax($a, $b)

{

if($a < 0) $a = -$a;

if($b < 0) $b = -$b;

$tmp = $a % $b;

while($tmp != 0) {

$a = $b;

$b = $tmp;

$tmp = $a % $b;

}

return $b;

}

//求最小公倍数用于通分

private static function _getmin($a, $b)

{

if($a < 0) $a = -$a;

if($b < 0) $b = -$b;

$max = self::_getmax($a,$b);

$min = intval(($a * $b) / $max);

return $min;

}

/**

* 约分运算,基本算法为分子分母同时除以最大公约数;

* @return void 将对象的分子分母约分为最简形式

*/

public function reduction()

{

$max = $this->_getmax($this->fenzi,$this->fenmu);

$this->fenzi = intval($this->fenzi / $max);

$this->fenmu = intval($this->fenmu / $max);

}

这两个方法全部都定义为静态私有方法,只在类内调用且不需实例化。求最大公约数和最小公倍数的算法其实还有很多种,@烬酱采用了另外一种方法,C++代码如下:

/**

* 求最大公约数并进行约分

* @return void

*/

int reduction()

{

int i,comdiv,small,max;

if(above

{

small=above;

max=below;

}

else

{

small=below;

max=above;

}

for(i=small;i>1;i--)

{

if(small%i==0 &max%i==0 )

break;

}

comdiv=i; //最大公约数

if(i!=0)

{

above/=i;

below/=i;

}

return 0;

}

这种方法的本质就穷尽法,核心思想在于for循环当中。同样的,也可用此法求最小公倍数。

4.3 分数加减

分数加法的算法如下:

/**

* 加法运算,写成静态方法,需要传递两个分数对象实例。加法的基本步骤为:

* 1. 求两个分母的最小公倍数;

* 2. 利用最小公倍数进行通分,此时分母就是最小公倍数,第一个分数的分子等于原来的分子*(最小公倍数/原来的分母),第二个分数的分子同理;

* 3. 分母不变,分子相加;

* 4. 对结果进行约分;

*

* @param fraction $fra1 分数相加的加数1

* @param fraction $fra2 分数相加的加数2

* @return fraction $fra 分数相加的计算结果

*/

public static function add($fra1, $fra2)

{

$fra = new Fraction();

$min = self::_getmin($fra1->fenmu,$fra2->fenmu);

$fenzi_left = $fra1->fenzi * ($min / $fra1->fenmu);

$fenzi_right = $fra2->fenzi * ($min / $fra2->fenmu);

$fra->fenmu = $min;

$fra->fenzi = $fenzi_left + $fenzi_right;

$fra->reduction();

return $fra;

}

/**

* 减法运算,加法的逆运算,只需要将参数$fra2的分子取反,将减法运算化为加法运算

*

* @param fraction $fra1 分数相减的被减数

* @param fraction $fra2 分数相减的减数

* @return fraction $fra 分数相减的计算结果

*/

public static function minus($fra1, $fra2)

{

$fra_t = new Fraction(-$fra2->fenzi,$fra2->fenmu);

return self::add($fra1,$fra_t);

}

在上述算法中,定义了两个静态方法,每个方法需要传入两个分数对象,之后就可以按上面的算法步骤进行加法和减法运算了。其中减法运算只需要转换为加法即可。需要注意的是,在减法运算中,存在两种可能的写法:

【写法1】

$fra2->fenzi = -$fra2->fenzi;

【写法2】

$fra_t = new Fraction(-$fra2->fenzi,$fra2->fenmu);

其中,第一种写法直接改变了减数分子的值,这里对减法本身的结果不会造成影响,表面上看是成立的,但其实这种写法产生了副作用,在计算乘法时,fra2就已经不是最初的分数值了,因此我们需要new一个新的对象,如写法2所示,这样就不会产生副作用改变分数2的值。

4.4 分数乘除

分数乘数就比较简单了,如下所示:

/**

* 乘法运算,分子相乘,分母相乘之后再约分

*

* @param fraction $fra1 分数相乘的乘数1

* @param fraction $fra2 分数相乘的乘数2

* @return fraction $fra 分数相乘的计算结果

*/

public static function multiply($fra1,$fra2)

{

$fra = new Fraction();

$fenzi = $fra1->fenzi * $fra2->fenzi;

$fenmu = $fra1->fenmu * $fra2->fenmu;

$fra->fenzi = $fenzi;

$fra->fenmu = $fenmu;

$fra->reduction();

return $fra;

}

/**

* 除法运算,乘法运算的逆运算,只需要将参数$fra2的分子分母调换,将除法运算化为乘法运算

*

* @param fraction $fra1 分数相除的被除数

* @param fraction $fra2 分数相除的除数

* @return fraction 分数相除的计算结果

*/

public static function divide($fra1,$fra2)

{

$fra_t = new Fraction($fra2->fenmu,$fra2->fenzi);

$fra = self::multiply($fra1,$fra_t);

return $fra;

}

4.5 分数的表示

最后,我们需要写一个方法,把分数以a/b的形式打印出来。

public function display()

{

printf("%d/%d\n",$this->fenzi,$this->fenmu);

}

这样我们的分数类的定义完了。

5. 结果展示

下面展示运行结果,先写一个调用:

$fra1 = new Fraction(3,4);

$fra1->display();

$fra2 = new Fraction(12,20);

$fra2->reduction();

$fra2->display();

$fra3 = Fraction::add($fra1,$fra2);

$fra3->display();

$fra4 = Fraction::minus($fra1,$fra2);

$fra4->display();

$fra5 = Fraction::multiply($fra1,$fra2);

$fra5->display();

$fra6 = Fraction::divide($fra1,$fra2);

$fra6->display();

如上,fra1期望直接打印出3/4, fra2对12/20先约分再输出,期望是3/5,fra3是计算fra1和fra2的加法,fra4为减法,fra5为乘法,fra6为除法。结果如下所示:

用Java编写约分最简公式_一个有趣的算法问题:如何定义一个分数类相关推荐

  1. 用Java编写约分最简公式,2013年Java方向C组第五题

    第五题 标题:有理数类 有理数就是可以表示为两个整数的比值的数字.一般情况下,我们用近似的小数表示.但有些时候,不允许出现误差,必须用两个整数来表示一个有理数. 这时,我们可以建立一个"有理 ...

  2. Java 编写程序打印以下图形_怎么用java编写如下程序在屏幕上输出如下图形 * *** *** * 循环语句做(if语句)...

    怎么用java编写如下程序在屏幕上输出如下图形 * *** ***** ******* ***** ... 4个答案  提问时间: 2011-12-16  22个赞 回答:这个图形对吧? * *** ...

  3. java编写字符串连接程序注释_一种利用JAVA注释支持多行字符串的方法

    从BeetlSql项目将SQL全放在Beetl模板里得到启发,又想到一个比较偏门的用法.以下代码实测通过,详见jSqlBox项目的test\examples\multipleLineSQL\SqlTe ...

  4. java线程池原理简答_面试官让我讲讲Java线程池的实现原理,我笑了...

    期待与你,一起进步 随着cpu核数越来越多,不可避免的利用多线程技术以充分利用其计算能力.所以,多线程技术是服务端开发人员必须掌握的技术. 线程的创建和销毁,都涉及到系统调用,比较消耗系统资源,所以就 ...

  5. 方根法公式_方根的简易算法

    1.1已知平方根整数部分,求平方根小数部分的算法: 任何一个数都可以写成某个整数的平方加上一个余数,如15可以写成32+6.8.9可以写成22+4.9.任何一个正数写成某个整数的平方加一个余数项的通式 ...

  6. C++编写一个关于圆形的程序,要求定义一个圆形类Circle

    1.编写一个关于圆形的C++程序.要求定义一个圆形类Circle,其中包含如下成员: 1)1个私有数据成员(半径). 2)3个公有函数成员(设置半径.计算面积.计算周长). 3)3个构造函数(不带参数 ...

  7. python写一个类600行代码_带你领略算法的魅力,一个600行代码的分词功能实现(二)...

    从大学毕业到工作的开始几年,一直觉得大学期间学的线性代数,离散数学,概率论简直是浪费时间. 那时候实际做的代码,大部分都是数据进销存.数据输入到数据库介质中的转换,CS,BS架构都写过一些.总觉得现实 ...

  8. java编写某计算器控制台程序_用java程序编写一个计算器

    点击查看用java程序编写一个计算器具体信息 答:给你一个参考,希望不要被百度吞了当晚餐 import java.awt.BorderLayout; import java.awt.GridLayou ...

  9. java编写某计算器控制台程序_计算器 - 进阶的憨狗 - 博客园

    源起 最近在看程杰著作的<大话设计模式>,全书以小菜和大鸟对话的形势,由浅入深的讲解程序的设计思想,影射出一个个设计模式.我之前虽然也使用过一些设计模式,但没有系统的学习.整理.总结,现从 ...

最新文章

  1. Python的安装 || python介绍
  2. python pymysql cursors_python pymysql cursor的问题
  3. android icu4c 7.1编译报错,android4.0编译系统时候遇到的错误集
  4. matlab 可视化界面,Matlab?的可视化界面设计
  5. 突发!贾跃亭或将申请个人破产重组
  6. Android权限之sharedUserId和签名
  7. java两个数最大公约数和最小公倍数_java中请给出例子程序:找出两个数的最大公约数和最小公倍数...
  8. python3之udp
  9. Android 程序员计算器 开发记录-Git版本控制初步接触
  10. 百度云盘超4G大文件上传不了怎么办?
  11. 面对众多云数据库,应该使用哪个云数据库好?
  12. 恕我直言:你可能一直用错了 kafka 的重试机制
  13. python的continue用法_Python学习笔记之Break和Continue用法分析
  14. 并发-MESI缓存一直协议详解
  15. 数字人民币将如何改变金融生态?
  16. 基于Java+SpringMVC+vue+element宠物管理系统设计实现
  17. python 字节流分段_学习Python又多了一种选择,原来是他!
  18. 工业自动化流水线上的机器视觉检测应用 (一):图像识别
  19. 二本学电子信息 计算机好不好,2021二本电子信息工程就业前景好吗
  20. CSS盒子模型/页面布局

热门文章

  1. Elasticsearch 中为什么选择倒排索引而不选择 B 树索引
  2. iphone11屏比例_苹果11的屏幕分辨率是多少
  3. PHP 索引数组合并
  4. 删除地址栏输入历史记录
  5. Google Hacking 搜索引擎攻击与防范
  6. win7系统中的消息队列服务器,高手分析win7系统安装消息队列的详细
  7. 字母上一横怎么word里打出来
  8. 圆周率一千万亿位_目前圆周率已经达到十万亿位了,为何还要算?有什么用处?...
  9. matlab零序五次谐波,基于5次谐波的小电流接地系统故障选线方法仿真与分析.docx...
  10. =default =delete