化實數為分數
實數包含有理數和無理數,任何有理數都可以表示為p/q(p,q是整數,q!=0)的形式,如果指定乙個分數的分母不超過某個值,對於一般的有理數或者無理數,是不可以用乙個分數來準確地表示的。我們這裡主要討論,如何找出一對分數p1/q1和p2/q2,使得q1 和q2 小於給定的值n,而p1/q1和p2/q2盡可能接近乙個給定的實數.。為了便於說明,我們用c++語言的格式給出問題的定義:
函式介面void search( int &p1, int &q1, int &p2,int &q2, int n, double f)
功能: 找出一對不可約p1/q1 和p2/q2, 使得這兩個分數的分母不大於n, 且p1/q1 <= f <= p2/q2, 且這兩個分數盡可能接近f。
在解決這個問題之前,我們先介紹一些準備知識。
定義1:最簡分數(也稱既約分數或不可約分數)。若p,q的最大公約數是1,我們稱分數p/q 是最簡分數。
不特別說明,以下提到的分數的分子和分母均為非負整數。
定義2:真分數,若p,q是正整數,0
定理1:分數a/b, c/d是最簡真分數(也可以是0/1或者1/1)且a/b 1) 數(a+c)/(b+d)是乙個最簡分數,
2) a/b < (a+c)/(b+d)
我們這裡僅對第二個結論給出證明。我們定義r1 =a /b, r2= c/d, 則
(a+c)/(b+d) – a/b
= (ab+cb-ab-ad)/(bb+bd)
=(cb-ad)/(bb+bd)
=(r2*bd-r1bd)/(bb+bd)
= (r2-r1)*bd / (bb+bd) >0
固(a+c)/(b+d)>a/b。同理可證 (a+c)/(b+d) < c/d
定義3:法雷數列
對任意給定的乙個自然數n,將分母小於等於n的不可約的真分數按公升序排列,並且在第乙個分數之前加上0/1,在最後乙個分數之後加上1/1,這個序列稱為n級法雷數列,以fn表示。如f5為:1/5, 1/4, 1/3, 2/5, 1/2, 3/5, 2/3, 3/4, 4/5.
法雷數列的構造:
法雷數列的構造可採用2分法,即如果 a/b, c/d (a/b
step1: 準備兩個數 0/1, 1/1 作為整個法雷數列的第乙個元素和最後乙個元素
0/1, 1/1
step2: 在兩個數中間插入1個數1/2, 變為
0/1, 1/2, 1/1
step3: 在每對相鄰兩個數中間插入1個數,變為
0/1, 1/3, 1/2, 2/3, 1/1
step4: 在每對相鄰兩個數中間插入1個數,變為
0/1, 1/4, 1/3, 2/5, 1/2, 3/5, 2/3, 3/4, 1/1
step5: 0/1 和 1/4 之間 和3/4和 1/1 仍然可插入1個數,使得插入的數分母不大於5
0/1, 1/5, 1/4, 1/3, 2/5, 1/2, 3/5, 2/3, 3/4,4/5, 1/1
至此,該序列包含了所有分母不大於5的最簡真分數,且各個分數以遞增順序排列。
法雷數列的性質:
1.除了1級法雷數列外,所有的法雷數列都有奇數個元素,其中居於正中間的那個元素一定是1/2.
2.當n趨於正無窮時,n級法雷數列包含的元素的個數趨於3/(π*π) * n
2≈ 0.30396355 * n
2.3.n級法雷數列中,若相鄰兩個元素是a/b 和c/d (a/b
實數化分數方法
對於有理數0,我們可以用0/1表示;對於有理數x<0, 總可以表示為 –(p/q), 其中p>0,q>0;而對於所有大於等於1的正有理數,總可以表示為 n + p/q ( n, p, q為非負整數,q!=1, p= a/b且f<=c/d,a/b稱為實數f的下界,c/d稱為實數f的上界,求這個下界和上界實際上是找出乙個n級法雷數列中兩個相鄰的元素。下面是化乙個小於1的正實數為分數的演算法。
step1: 置實數f 的下界為 a/b=0/1, 上界為c/d =1/1。
step2: 計算出下界和上界之間的數 p/q = (a+c)/(b+d)
step3: 若 q>n(分母大於指定值),計算中止。
若 abs(p/q-f)小於指定的值=, 計算中止。
若 p/q > f, 置下界a/b為p/q
若 p/q< f, 置上界c/d為p/q
step4, 重複step 2-3
當計算終止時,a/b為這個實數的下界,c/d為這個實數的上界。
上述方法確定方法的初始上界和下界範圍較寬。我們可用下面的方法直接求得更精確的上界和下界。
case 1: f<1/2, 計算1/f向下取整得到整數c, 則實數f的下界和上界為 1/(c+1)和1/c。
case 2: f>1/2, 計算1/(1-f)向下取整得到整數c, 則實數f的下界和上界為 (c-1)/c和c/(c+1)。
誤差分析,根據法雷數列性質3我們知道,n級法雷數列中相鄰的兩個元素可以表示乙個區間 [a/b, c/d],前乙個元素q/b為區間的下界,後乙個元素c/d為區間的上界,這個區間的寬度h =c/d- a/b,滿足 1/n <= h <1/(n*n-1)。若運氣好的話,乙個實數正好落在乙個寬度為1/n(n-1) 的區間,這個區間的下界或上界與這個實數的差不超過abs(1/(n*(n-1)))。若運氣很差,乙個實數恰好小於法雷序列的第2個元素或者最後乙個元素。則這個元素的下界和上界與這個實數的差不超過1/n。
下面給出一段示例**,這段**可計算出sqrt(2),sqrt(3),sqrt(5) 以及無理數π和e的 分數表示。
#include "math.h"
#include "stdlib.h"
#include "stdio.h"
typedef struct _frac
frac;
double getvalue( frac f)
//f必須為正數
void searchfrac( frac* plow, frac* phigh,int n, double f)
else
mid.numerator= low.numerator + high.numerator;
mid.denominator=low.denominator + high.denominator;
while ( mid.denominator < n && fabs(f-getvalue(mid))>1e-15 )
else
mid.numerator= low.numerator+ high.numerator;
mid.denominator=low.denominator + high.denominator; }
if (k>0)
*phigh = high;
*plow =low;}
int main(int argc, char* argv);
for (int i=0;i
1.第36次程式設計比賽第1題題目 — 程式設計愛好者論壇:
洛谷 P1577 切繩子 實數二分,化實數為整數
實數二分,精度會缺失 如果直接用double二分,最後 2lf輸出的時候會自動向上取整,可能改變了答案 解決辦法是先把每根繩子長度a i 乘以100化為整數,再按整數的方法二分,最後輸出答案時再除以100即可。注意在二分過程中要特判m 0的情況 否則在judge函式中會除以0導致re m 0時直接b...
分數化小數
兩個整數相除,將結果用字串返回。如果是迴圈小數,將迴圈的位用括號括起來。函式原型為 void div const int a,const int b,char str 輸入 1 3 輸出 0.3 整數相除.cpp include includeusing namespace std const in...
分數化小數
進入研一以來,折騰了一整個學期,既沒好好學習,也沒有別的收穫,前途規劃更是白紙一張。在科大還有3個多月的時間,索性就逼迫自己下,把之前欠下的東西都補上,也不枉來這一年。最近開始重新學習演算法,主要有 演算法競賽入門經典 演算法競賽訓練指南 演算法導論 三本書,注重圖論部分和coding能力,但時間上...