#每日一題:小數第n位(數論/模擬)

試題 歷屆試題 小數第n位

資源限制
時間限制:1.0s 內存限制:256.0MB


Daily English

你做過的事,就永遠不會忘,即便你會想不起來。
Once you do something,you never forget.Even if you can’t remember.

問題描述

我們知道,整數做除法時,有時得到有限小數,有時得到無限循環小數。
  如果我們把有限小數的末尾加上無限多個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

思路:

  • way1:
    明確結果只取3位,從n開始取,包括第n位,還要取兩位,a,b,n可達1e9。
    假設沒有精度問題,我們算出來的結果,要取小數點後面的幾位,
    一般我們會先把小數擴大(10^x)倍,將結果轉化爲整數,然後取餘。
    所以問題轉化一下:
    ans = a / b * 10^(n+2) % 1000;
    求解ans。
    由於b可能很大,所以a/b就會因精度問題而算不到正確結果。
    上面的式子可以看成:
    a/b mod m
    這就很容易想到:
    模m意義下,a除以b 模m 等同於 a乘以 b的逆元(inv(b)) 模 m
    即: a/b mod m = a * inv(b) mod m
    所以:
    ans = a * inv(b) * 10^(n+2) % 1000;
    此時模m=1000,不是質數,所以不能通過費馬小定理來求解逆元
    不能保證b與m互質(gcd(b,m)= 1),所以也不能通過擴展歐幾里得來求解逆元

當逆元無法來求解的時候,需要考慮一個一般公式(轉化成不需要求解逆元):a / b % m = a % (b*m) / b
證明見:證明詳情

所以根據公式a / b % m = a % (b*m) / b 問題轉化:

ans = a * 10^(n+2) % (b*1000) / b

注意輸出,結果沒有3位要補0。

way2:
第二種方法就是模擬一下手算,如果出現無限循環的情況是怎麼樣的。
比如:1 / 3:
小數第一位: 1 * 10 / 3
第二位:10%3 = 1,1 * 10 / 3。。。
發現只要被除數再次出現就會陷入循環(出現循環節)。
所以我們就可以只 算到一個循環 並記錄下來
後面的根據記錄直接推出(詳見代碼)。

代碼:

way1 數論

#include <iostream>
#include <cstdio>
using namespace std;
typedef long long LL;
LL quickPow(LL a,LL b,LL mod)
{
    LL res = 1;
    a %= mod;
    while(b)
    {
        if(b&1) res = res * a % mod;
        a  = a * a % mod;
        b >>= 1;
    }
    return res%mod;
}
int main()
{
    int a,b,n;
    scanf("%d%d%d",&a,&b,&n);
    LL mod = b * 1000;
    LL t = quickPow(10,n+2,mod);
    int ans = a % mod * t % mod;
    printf("%03d\n",ans/b);
    return 0;
}

way2模擬

/*
s:記錄一個循環的開始處
e:記錄一個循環的截至處
mp1[i]:記錄被除數i是否出現
mp2[i]:記錄第i個小數是多少
mp3[i]:記錄被除數爲i時 所對應的小數 在的位置
*/
#include <iostream>
#include <map>
using namespace std;
map<int,int>mp1;
map<int,int>mp2;
map<int,int>mp3;
int s,e;
void calc(int a,int b)
{
    a %= b;
    //沒有循環,後面通過補0,可以統一當作有循環處理
    while(1)
    {
        a *= 10;
        if(!mp1[a])
        {
            mp1[a]++;
            mp2[++e] = a / b;
            mp3[a] = e;
        }
        else
        {
            //所以是再次出現被除數時才跳出循環
            s = mp3[a];
            break;
        }
        a %= b;
    }
}
int getAns(int n)
{
    return n <= e ? mp2[n] : mp2[(n-s)%(e-s+1)+s];
}
int main()
{
    int a,b,n;
    cin>>a>>b>>n;
    s = e = 0;
    calc(a,b);
    for(int i = n; i < n+3; i++)
    {
        cout<<getAns(i);
    }
    cout<<endl;
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章