歷屆試題 小數第n位


問題描述

  我們知道,整數做除法時,有時得到有限小數,有時得到無限循環小數。
  如果我們把有限小數的末尾加上無限多個0,它們就有了統一的形式。


  本題的任務是:在上面的約定下,求整數除法小數點后的第n位開始的3位數。
輸入格式
  一行三個整數:a b n,用空格分開。a是被除數,b是除數,n是所求的小數后位置(0<a,b,n<1000000000)
輸出格式
  一行3位數字,表示:a除以b,小數后第n位開始的3位數字。
樣例輸入
1 8 1
樣例輸出
125
樣例輸入
1 8 3
樣例輸出
500
樣例輸入
282866 999000 6
樣例輸出
914
思路:我們以3/7舉例,下面是手寫的常規除法的計算過程。

 

比如n=4時,我們計算的是小數第四位,那么很明顯就是40除於7的值,所以我們只需要知道第四位小數對應的哪個被除數是誰就行,用變量sa表示被除數,則第一位小數就是sa/7,余數就是下一個小數對應的被除數,那么用余數更新sa,一次類推可以計算出所有的小數,注意上圖第一個被除數30如果在下面再次出現則容易知道接下來會重復計算,意味着產生循環節,所以我們可以通過循環節長度更新n,降低時間復雜度。

代碼
 1 #include<bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 int main()
 6 {
 7     int a,b,n;
 8     int sa;
 9     cin >> a >> b >> n;
10     sa=a%b;//初始化sa,sa是求每次相除的余數 
11     for(int i=1;i<=n;i++){
12         sa=sa%b*10;//在本輪循環相除后余數發生改變 
13         //cout << sa <<" "<<sa%b <<" "<<a%b << endl;
14         if(sa%b==a%b){//如果下一次循環(注意sa%b是下一次循環的余數)的余數 等於初始的余數,說明接下來的循環或重復之前的計算 
15             n%=i;//n縮小(這是降低時間復雜度的關鍵) , 
16             i=0;//重新開始遍歷 
17         }
18     }
19     for(int i=1;i<=3;i++){
20         cout << sa/b;//輸出該位置計算結果 ,注意如果是有限小數切sa=0時,接下來都是輸出0 
21         sa=sa%b*10;//下一次計算的余數 
22     }
23     return 0;
24 }

 但是仔細一想上個代碼其實是不完全正確的,或不完美的,雖然可以通過測試系統,因為上個代碼默認循環節的第一個數字就是小數點后的第一個數字,但是很許多循環小數並非如此

這是第三個測試用例相除的結果,發現循環節是149,從第四個數字開始,如果我們要查詢結果數億之后的某個數的后三位,上個代碼明顯超時,所以需考慮循環節不在第一位的情況,

在上個代碼上修改如下

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int t=1;
 4 int array[10];
 5 int compare(int m)//得到循環節開始的下標
 6 {
 7     for(int i=1;i<t;i++){
 8         if(array[i]==m) return i;//此時第二個循環節的第一個數字遇到第一個循環節的第一個數字,返回第一個循環節開始的下標
 9 
10     }
11     return 0;
12 }
13 int main()
14 {
15     int a,b,n;
16     int sa;
17     cin >> a >> b >> n;
18     int m=0;//表示還沒遇到循環節
19     int num=0;
20 
21      memset(array,0,sizeof(array));
22     sa=a%b;//初始化sa,sa是求每次相除的余數
23     for(int i=1;i<=n;i++){
24         if(m==0 && i!=1) array[t++]=sa%b;
25         if(m==0 && i==1) array[t++]=sa;
26         sa=sa%b*10;//在本輪循環相除后余數發生改變
27         if(m==0)
28         {
29             int aa=sa%b;
30             num=compare(aa);
31         //    cout << num << endl;
32         }
33         if(num!=0&&m!=1){
34             n%=(i-num+1);
35             n+=(num-1);
36             i=0;
37             m=1;
38         }
39         //if(nu)
40     }
41     for(int i=1;i<=3;i++){
42         cout << sa/b;//輸出該位置計算結果 ,注意如果是有限小數切sa=0時,接下來都是輸出0
43         sa=sa%b*10;//下一次計算的余數
44     }
45     return 0;
46 }

下面的小demo是計算循環節的,根據上個代碼衍生出來的

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int t=1;
 4 int array[100];
 5 int compare(int m)//得到循環節開始的下標
 6 {
 7     for(int i=1;i<t;i++){
 8         if(array[i]==m) return i;//此時第二個循環節的第一個數字遇到第一個循環節的第一個數字,返回第一個循環節開始的下標
 9 
10     }
11     return 0;
12 }
13 int main()
14 {
15     //freopen("D:/Data.txt","r",stdin);
16     int a,b;
17     int sa;
18     cin >> a >> b;
19     int num=0;
20     memset(array,0,sizeof(array));
21     sa=a%b;
22     for(int i=1;i<1000;i++){
23         if(sa%b==0) {t=1000; break;}
24         if(i!=1) array[t++]=sa%b;
25         if(i==1) array[t++]=sa;
26         sa=sa%b*10;
27         int aa=sa%b;
28         num=compare(aa);
29         if(num!=0) break;
30 
31     }
32     if(t==1000) cout << "不存在循環節" << endl;
33      else{
34            sa=a%b;
35       for(int i=1;i<=t;i++){
36         if(i>num) cout << sa/b;//輸出該位置計算結果 ,注意如果是有限小數切sa=0時,接下來都是輸出0
37         sa=sa%b*10;//下一次計算的余數
38     }
39   }
40     return 0;
41 }

 





免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM