2.6/ 7 精确表示浮点数 + 最大公约数
来源:互联网 发布:单农男装网络旗舰店 编辑:程序博客网 时间:2024/05/18 03:52
1. 前言
本文的一些图片, 资料 截取自编程之美
2. 问题描述
3. 问题分析
这个问题, 说白了, 就是将小数表示转换为分数表示
小数表示 => 分数表示
对于有限不循环小数, 可以直接令分子分母同时乘上(10^小数位数), 比如 : 0.234, 可以将其视为(0.234/ 1), 然后分子分母同时乘以1000, 得到(234/ 1000), 然后在化简即为所求
对于无限循环小数, 这里为了描述方便, 我们可以将其抽象为0.(a1a2..am)(b1b2..bn), 其中a1, a2, …, am为其不循环部分, b1, b2, …, bn为其循环部分
令X为0.(a1a2..am)(b1b2..bn), 则10^m*X = (a1a2,..am) + 0.(b1b2,..bn)
令Y为0.(b1b2,..bn), 则10^n*Y = b1b2,…bn + 0.(b1b2,..bn) => 10^n*Y = b1b2,…bn + Y => Y = (b1b2,..bn) / (10^n - 1)
将Y的值带入上面X的式子, 得到X = ( (a1a2,…am) + ( (b1b2,..bn) / (10^n-1) ) ) ) / (10^m) => ( ((a1a2,…am) * (10^n-1 ) ) + (b1b2,..bn) ) ) / ( (10^n-1) * (10^m) )
然后 在套用上面的推导出来的公式, 计算出来
最后在进行化简, 即为所求
编程之美分析原图 :
化简操作 : 只需要分子分母同时除以分子分母的最大公约数即可
因此, 这里又导出了另一个算法, gcd, 求最大公约数
解法一 : 经典的欧几里得辗除法 gcd(x, y) => gcd(y, y%x)
解法二 : 利用了开销较小的减法 gcd(x, y) => gcd(y, y-x) [注意 : 需要 y > x] , 来代替开销昂贵的求模操作, 适用于y 接近x的一般的情景
解法三 : 利用最大公约数和素数的性质, 令p为一个数组
原理1 : gcd(val01, val02) = gcd(p*val01’, p*val02’) = p * gcd(val01’, val02’)
原理2 : gcd(val01, val02) 如果val01能被p整除, 且p为素数, 并且val02不能被p整除 gcd(val01, val02) = gcd(val01’, val02)
剩下的情况, 就只能辗转相除, 或者辗转相减了
这里为了提高效率, 取p为2
4. 代码
/** * file name : Test13FloatToFraction.java * created at : 9:00:43 AM May 22, 2015 * created by 970655147 */package com.hx.test03;public class Test13FloatToFraction { // 将浮点数转换为分数 public static void main(String []args) { Number n = new Number(0, 0.142857f, 2, 142857); Fraction f = resolveNumber(n); Log.log(n); Log.log(f); simplify(f); Log.log(f);// Log.log(89285632.0 / 624999375);// Log.log(greatestCommonDivisor01(42, 30) );// Log.log(greatestCommonDivisor02(42, 30) );// Log.log(greatestCommonDivisor03(42, 30) ); } // 将num解析为一个分数[可可能非最简] // 思路 : 如果num为有限小数 则分子分母同时乘以(10^该小数的位数) 得到分数 // 如果num为 无限循环小数 假设该小数为0.a1a2..am(b1b2..bn) // 10^m * X = a1a2...am + 0.(b1b2...bn) // 假设 0.(b1b2..bn)为Y 10^n * Y = b1b2..bn + 0.(b1b2..bn) // 得到 (10^n - 1) * Y = b1b2..bn // 从而 Y = b1b2...bn / (10^n - 1) // 带入原式 10^n * X = a1a2..am + Y // 10^n * X = a1a2..am + Y // X = (a1a2...am + (b1b2..bn / (10^n -1) ) ) / (10^m) // X = ( ((a1a2..am) / (10^n - 1) ) + (b1b2...bn) ) / ((10^m) * (10^n - 1)) public static Fraction resolveNumber(Number num) { Fraction res = new Fraction(); if(num.cycleTime == 0) { int len = getLengthByFloat(num.floatVal); res.denominator = pow(10, len); res.nominator = (long )(num.floatVal * res.denominator) + (num.inteVal * res.denominator); } else { int nonCycleNum = getLengthByFloat(num.floatVal); int cycleNum = getLengthByInteger(num.cycleNum); long powCycleNum10 = (pow(10, cycleNum) - 1); long powNonCycle10 = pow(10, nonCycleNum); res.denominator = powCycleNum10 * powNonCycle10; res.nominator = (long ) ( (num.floatVal * (powNonCycle10 - 1) + num.cycleNum) + (num.inteVal * res.denominator) ); } return res; } // 辗除法 gcd(val01, val02) => gcd(val02, val01%val02) public static long greatestCommonDivisor01(long longVal01, long longVal02) { if(longVal02 == 0) { return longVal01; } return greatestCommonDivisor01(longVal02, longVal01 % longVal02 ); } // 辗除法02 gcd(val01, val02) => if(val01 > val02) gcd(val02, val01-val02) // else gcd(val02, val01) public static long greatestCommonDivisor02(long longVal01, long longVal02) { if(longVal01 < longVal02) { return greatestCommonDivisor02(longVal02, longVal01); } if(longVal02 == 0) { return longVal01; } return greatestCommonDivisor02(longVal02, longVal01 - longVal02); } // 原理1 : gcd(val01, val02) = gcd(p*val01', p*val02') = p * gcd(val01', val02') // 原理2 : gcd(val01, val02) 如果val01能被p整除, 且p为素数, 并且val02不能被p整除 gcd(val01, val02) = gcd(val01', val02) // 原理3 : gcd(val01, val02) => if(val01 > val02) gcd(val02, val01-val02) // else gcd(val02, val01) // 这里去p为2 提高效率 public static long greatestCommonDivisor03(long longVal01, long longVal02) {// Log.log(longVal01, longVal02); if(longVal01 < longVal02) { return greatestCommonDivisor03(longVal02, longVal01); } if(longVal02 == 0) { return longVal01; } boolean isEven01 = (longVal01 & 1) == 0; boolean isEven02 = (longVal02 & 1) == 0; if(isEven01 && isEven02) { return greatestCommonDivisor03(longVal01 >> 1, longVal02 >> 1) << 1; } else if(isEven01) { return greatestCommonDivisor03(longVal01 >> 1, longVal02); } else if(isEven02) { return greatestCommonDivisor03(longVal01, longVal02 >> 1); } else { return greatestCommonDivisor03(longVal02, longVal01 - longVal02); } } // 化简f分式 private static void simplify(Fraction f) { long gcd = greatestCommonDivisor03(f.nominator, f.denominator); f.denominator /= gcd; f.nominator /= gcd; } // 表示base^time private static long pow(long base, int time) { long res = 1; for(int i=0; i<time; i++) { res *= base; } return res; } // 获取floatVal的浮点数部分的长度 private static int getLengthByFloat(float floatVal) { String floatStr = String.valueOf(floatVal); return floatStr.length() - floatStr.lastIndexOf(".") - 1; } // 获取inteVal的长度 private static int getLengthByInteger(long intVal) { return String.valueOf(intVal).length(); } // 如下一个Number表示一个小数 // 如 : 4. 23434343434(34) => inteval = 4, floatVal = 0.2, cycleNum = 34 static class Number { long inteVal; float floatVal; int cycleTime; long cycleNum; public Number() { super(); } public Number(long inteVal, float floatVal, int cycleTime, long cycleNum) { this.inteVal = inteVal; this.floatVal = floatVal; this.cycleTime = cycleTime; this.cycleNum = cycleNum; } public String toString() { int size = (int) (20 + cycleNum * 7 ); StringBuilder sb = new StringBuilder(size); sb.append((inteVal + floatVal) ); for(int i=0; i<cycleTime; i++) { sb.append(cycleNum); } return sb.toString(); } } // 一个分数的表示 // 如果 2/ 9 => nominator = 2, denominator = 9 static class Fraction { long nominator; long denominator; public String toString() { return nominator + " / " + denominator; } }}
5. 运行结果
6. 总结
这里再再一次的证明了, 算法和数学的紧密联系, …
不过, 对于循环部分的提取值真的是挺秒的, 如果数学好的同学, 应该能想出来
对于最大公约数的算法, 晕, 欧几里得这家伙真是厉害, 他是怎么想到的辗转相除, 难道是无意间想到了可能存在这个结论, 然后使用了很多数据来验证这个结论么..
注 : 因为作者的水平有限,必然可能出现一些bug, 所以请大家指出!
- 2.6/ 7 精确表示浮点数 + 最大公约数
- 为什么浮点数不能精确表示?
- 2.6-精确表达浮点数
- 2.6 精确表达浮点数
- Java浮点数的精确计算及表示
- 浮点数能够精确表示的整数的范围
- C语言为什么不能精确表示浮点数
- Java浮点数的精确计算及表示
- Java浮点数的精确计算及表示
- 编程之美 - 浮点数的精确表示
- Java浮点数的精确计算及表示
- Java浮点数的精确计算及表示
- Java浮点数的精确计算及表示
- 浮点数精确计算
- 精确表达浮点数
- 精确表达浮点数
- 精确表达浮点数
- 精确表达浮点数
- IOS项目上架时问题的解决方案(3)
- android-ndk 数据传递
- 解决点击状态栏时ScrollView自动滚动到初始位置失效办法
- 腾讯云使用教程 基本工具 开发工具软件 从入门到精通 图文教程
- python抓取网页的一个小例子
- 2.6/ 7 精确表示浮点数 + 最大公约数
- HDU 5071 Chat (神一般的模拟题)
- C++类型转换小疑惑
- zoj 3820 求三遍树的中心
- 联想键盘功能键不好使,看这里
- 约瑟夫问题
- 泡泡窗口(二)
- leetcode 126: Word Ladder II
- 关于GPS坐标系的几种概念以及相互转化(WGS84,GCJ02, BD09)