文章目录
- 前言
- 一、问题
- 二、分析
- 三、实现
- 1、界面设计
- 2、循环小数类设计
- 3、调用
- 4、运行结果
- 5、神奇的发现
- 总结
- 附录
前言
最近学习C#,遇到这样的一个问题:用分数表示循环小数 0.33(3)=1/3 0.285714(285714)=2/7 ,解决之后来记录一下解决方案。
一、问题
用分数表示循环小数 0.33(3)=1/3 0.285714(285714)=2/7
二、分析
循环小数分纯循环小数和混循环小数。
纯循环小数的化法:先获取循环体的值x,然后获取循环体的位数n,然后将循环体的值x比上n个9,最后化简。如,0.81(81),从这个数中我们可以获取循环体的值为81,循环体的位数为2也就是到时候比上99,所以得出0.81(81)=(81/99)=9/11。
混循环小数的化法:先将其分解成为有限小数与纯循环小数/10^k的和(这里的k是指循环体前面的小数位数),然后分别求出它们的分数形式,最后通分化简。举例如下:0.13(3),这里分解出来的有限小数为0.1、纯循环小数为0.3(3)、k为1(因为循环体3前面的小数只有1位),所以得出0.13(3)=0.1+0.3(3)/10=2/15。
注意点:如果像前面0.33(3)这样的数,它本来是纯循环小数,但是由于它不是写成0.3(3)这样的形式,如果把它归为纯循环小数来书写代码将会提高代码的复杂程度,所以我们把它归类于混循环小数来处理。
三、实现
1、界面设计
1、输入框:使用TextBox控件,并给它命名为cirNumTBox
2、显示框:使用TextBox控件,给它命名为fracTBox,并将ReadOnly属性设置成true
3、转换按钮:使用Button控件,并给它命名为changeBtn
2、循环小数类设计
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CycNumExpress
{
/// <summary>
/// 此类用来描述无限循环小数,拥有循环小数和分数两个属性
/// </summary>
public class CircleNum
{
string circNum; //循环小数
public string CircNum
{
get { return circNum; }
set { circNum = value; }
}
string fraction; //循环小数的分数表示
public string Fraction
{
get { return fraction; }
set { circNum = value; }
}
public CircleNum()
{
}
public CircleNum(string circNum)
{
this.circNum = circNum;
fraction = ChangeToFraction();
}
/// <summary>
/// 此方法用来将无限循环小数转换成分数表示
/// </summary>
/// <returns>无限循环小数的分数表示</returns>
public string ChangeToFraction()
{
string circBody; //循环体
string acycBody; //非循环体
int num; //记录循环体前面小数点的位数
string fraction;//分数
circBody = GetCircBody(circNum);//获取循环体
acycBody = GetAcycBody(circNum);//获取非循环体
//获取循环体前的小数位数,如 1.13(3)其num的值为1
num = GetBCirBodyDecimal(circNum);
fraction = GetFraction(circBody, acycBody, num);//获取分数表示
return fraction;
}
/// <summary>
/// 此函数用来获取循环体,如1.13(3),则获取值为3
/// </summary>
/// <param name="circNum">循环小数</param>
/// <returns>循环体</returns>
public string GetCircBody(String circNum)
{
string circBody = "0";
int firstIndex = circNum.IndexOf("(") + 1; //读的首位置下标
int lastIndex = circNum.LastIndexOf(")") - 1;//读取的尾下标
int readNum = lastIndex - firstIndex + 1;//读取的数目
if ((firstIndex > 0) && readNum > 0)
{
circBody = circNum.Substring(firstIndex, readNum);//截取循环体
}
return circBody;
}
/// <summary>
/// 此函数用来获取非循环体,如1.13(3),则获取值为1.1
/// </summary>
/// <param name="circNum">循环小数</param>
/// <returns>非循环体</returns>
public string GetAcycBody(String circNum)
{
string acycBody = "";
int firstIndex = 0; //读的首位置下标
int lastIndex = circNum.LastIndexOf(GetCircBody(circNum) + "(") - 1;//读取的尾下标
int readNum = lastIndex - firstIndex + 1;//读取的数目
if (firstIndex != -1 && readNum > 0)
{
acycBody = circNum.Substring(firstIndex, readNum);//截取循环体
if (acycBody.EndsWith("."))//如果小数点后没有数了,那么就截去小数点
{
acycBody = circNum.Substring(firstIndex, acycBody.Length - 1);//截取循环体
}
}
else
{
acycBody = circNum;
}
return acycBody;
}
/// <summary>
/// 此函数用来获取循环体前的小数位数,如 1.13(3)其num的值为1
/// </summary>
/// <param name="circNum">循环小数</param>
/// <returns>循环体前的小数位数</returns>
public int GetBCirBodyDecimal(String circNum)
{
int num = 0;
string acycBody = GetAcycBody(circNum);
int firstIndex = acycBody.IndexOf(".") + 1; //读的首位置下标
int lastIndex = acycBody.Length - 1;//读取的尾下标
int readNum = lastIndex - firstIndex + 1;//读取的数目
if ((firstIndex > 0) && (readNum > 0))
{
num = acycBody.Substring(firstIndex, readNum).Length;//截取循环体
}
return num;
}
/// <summary>
/// 此函数用来获取循环小数的分数表示
/// </summary>
/// <param name="circBody">循环体</param>
/// <param name="acycBody">非循环体</param>
/// <param name="num">循环体前的小数位</param>
/// <returns>循环小数的分数表示</returns>
public string GetFraction(string circBody, string acycBody, int num)
{
string fraction;
long mol; //分子
long deno = 0; //分母
string op = "";
if (int.Parse(circBody) == 0)
{
deno = 1;
}
else
{
for (int i = 1; i <= circBody.Length; ++i)
deno = deno * 10 + 9;
}
deno = deno * (int)Math.Pow(10, num);//为分母赋值
mol = long.Parse(circBody) + (long)(Math.Abs(decimal.Parse(acycBody) * deno));//为分子赋值
if (acycBody.Substring(0, 1) == "-")//记录循环小数的符号
{
op = "-";
}
long gcd = FindGCD(mol, deno);//寻找最大公约数
fraction = op + (mol / gcd).ToString() + ((deno / gcd == 1) ? "" : "/" + (deno / gcd).ToString());//得到最简分数
return fraction;
}
/// <summary>
/// 本函数用辗转相除法来求解最大公约数
/// </summary>
/// <param name="num1">整数1</param>
/// <param name="num2">整数2</param>
/// <returns>最大公约数</returns>
public long FindGCD(long num1, long num2)
{
long temp;
num1 = Math.Abs(num1);
num2 = Math.Abs(num2);
temp = num1 % num2;
while (temp != 0)
{
num1 = num2;
num2 = temp;
temp = num1 % num2;
}
return num2;
}
}
}
3、调用
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace CycNumExpress
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void change(object sender, EventArgs e)
{
//有参构造实现
//CircleNum circNum = new CircleNum(cirNumTBox.Text);
//fracTBox.Text = circNum.Fraction;
//无参构造实现
CircleNum circNum = new CircleNum();
circNum.CircNum = cirNumTBox.Text;
fracTBox.Text = circNum.ChangeToFraction();
}
}
}
4、运行结果
纯循环小数转换
混循环小数转换
整数转换
有限小数转换
5、神奇的发现
在不断测试的过程中,遇到了一个神奇的事情,在循环小数中,如果带有9的循环体,那么就会得到近似解,比如0.9(9)=1。刚刚发现被惊到了,还以为遇到什么神奇的bug,后来仔细一想这个结果是正确的,详细证明过程如下:
证明1:
设0.9(9)=x。
那么:10x=9.9(9)则9x=10x-x=9.9(9)-0.9(9)=9。
所以x=1,得证。
证明2:
设0.9 (9)为无限递缩等比数列。
那么:0.9(9)=0.9+0.09+0.009+…+0.90.1的(n-1)次方=
0.9(1-0.1的n次方)/(1-0.1)=1-0.1的n次方。
所以当n趋向于无穷大时0.1的n次方趋向于0 所以0.9(9)=1。
总结
将循环小数转换成其分数形式看起来复杂,但是经过分析,将问题分解细化,还是可以找到比较好的解决方案的,希望此篇文章可以帮助到你。
附录
源码下载:https://github.com/luotingyang/MyRepository/tree/C%23