本人是个编程爱好者,同时又是一个数学爱好者,很希望能够用程序去解决一些数学问题。比如我想计算n/m的值(n,m都是正整数),虽然我们可以在float范围内进行除法计算,但所得的值都不够精确。怎么样才能够算出n/m的精确值呢?首先要明白n/m的值有两类,一类是可以整除的,另一类不能整除,但结果是无限循环小数。因此我要的结果是,如果可以整除,输出完整结果;不能整除,输出一个循环节。现在比较难的问题是不能整除的情况,我们怎么知道n/m从第几位开始循环,并且循环节的长度是多少呢?要解决这两个问题需要一些相关的数学知识。
关于循环节长度,看一个例子(本人从网上找来的):
求1/2008的小数形式的循环节长度。
解答:
2008=2^3×251
φ(251)=250
250的正因数有1、2,5、10、25、50、125、250,x取上述正因数并且满足10^x≡1 (mod 251)的最小的x是50,所以1/2008的循环节长度是50。
分析:解答似乎"不知所云",因为这涉及到数论知识。首先,10^φ(m)≡1(mod m),是欧拉函数的性质,在此略去证明。对正整数n,欧拉函数是不大于n的数中与n互质的数的数目。而1/2008的循环节长度等于1/251的循环节长度,因为循环小数乘除2或5的任意幂次,循环节长度不变,乘除的2,5在mod 10时不会体现。10^t≡1(mod n)与循环小数的关系,就是10^t被n除余数为1,这样在除法式中,除到第t位时余数与开始时相同,这意味着新的循环开始了,即t是循环节长度。而251是质数,其欧拉函数为251-1=250。由于250必为循环节长度的整数倍,故一个循环节的长度为250的约数,对约数从小到大逐一试验即可。
我们要获得循环节的长度,就要拣出m的所有约数x,并进行验证10^x≡1,则x为循环节长度。
至于从第几位开始循环:分母中含有2的因子个数加上5的因子个数再加上1,即若n=2^i*5^j*p,则从第i+j+1位开始循环。
根据以上知识,进行编程,代码如下:
#include<stdio.h>
#define MAX 1000
//求n与m的最大公约数
int gcd(int n, int m)
{
int temp;
int r;
if(n < m)
{
temp = n;
n = m;
m = temp;
}
while(m != 0)
{
r = n % m;
n = m;
m = r;
}
return n;
}
//判断小数从第几位开始循环
//返回0说明是有限小数
int cirNode(int n, int m)
{
int i = 1;
int g;
//把n/m化成最简形式
g = gcd(n, m);
m = m/g;
while(m%2 == 0)
{
i++;
m = m/2;
}
if(m == 1)
return 0;
while(m%5 == 0)
{
i++;
m = m/5;
}
if(m == 1)
return 0;
return i;
}
//欧拉函数:小于n并且与n互素的个数
int Eulur(int n)
{
int i;
int m;
m = 0;
for(i=1; i<n; i++)
if(gcd(n, i) == 1)
m++;
return m;
}
//计算n/m循环节的长度
int cirLength(int n, int m)
{
int l; //循环节的长度
int i, j, k, e, g, t;
int a[MAX];
l = i = j = t =0;
//把n/m化成最简形式
g = gcd(n, m);
m = m/g;
//去掉因素2和5
while(m%2 == 0)
m = m/2;
while(m%5 == 0)
m = m/5;
t = Eulur(m);
for(i=1; i<=t; i++) //求t的所有约数
if(t%i == 0)
a[j++] = i;
for(i=0; i<j; i++)
{
e = 1;
for(k=1; k<=a[i]; k++)
{
e = (10*e)%m; // (10^a[i] mod m) == 1
}
if(e == 1)
{
l = a[i];
return l;
}
}
}
int szDiv[MAX]; //保存计算结果,szDiv[0]为整数部分,其余为小数部分
int num = 0; //szDiv数组的有效位
void divide(int n, int m)
{
int i, j, k, len;
szDiv[0] = n/m;
n = n%m;
if(n == 0) // n/m刚好整除,没有小数
{
return;
}
else if(cirLength(n, m) == 0) //有限小数
{
i = 1;
do{
n = n*10;
szDiv[i++] = n/m;
num++;
n = n%m;
}while(n != 0);
}
else //无限小数,包括纯循环与混循环小数
{
j = cirNode(n, m);
k = cirLength(n, m);
len = j+k-1;
for(i = 1; i<=len; i++)
{
szDiv[i] = n*10/m;
num++;
n = (n*10)%m;
}
}
}
int main()
{
int m, n, i, p;
printf("n/m:");
scanf("%d/%d", &n, &m);
printf("%d/%d=", n, m);
divide(n, m);
if(num == 0)
printf("%d/n", szDiv[0]);
else
{
printf("%d.", szDiv[0]);
for(i=1; i<=num; i++)
{
printf("%d", szDiv[i]);
if(i%50 == 0) //每50个数一行
printf("///n");
}
printf("/n");
p = cirNode(n, m);
if(p != 0)
{
printf("Circle Node is:"); //打印循环节
for(i=p; i<=num; i++)
{
printf("%d", szDiv[i]);
if((i-p+1)%50 == 0) //每50个数一行
printf("///n");
}
printf("/n");
}
}
return 0;
}
运行结果:
n/m:1/2008
1/2008=0.00049800796812749003984063745019920318725099601593/
625
Circle Node is:49800796812749003984063745019920318725099601593625/