模擬手算計算小數循環部分,長除法的使用例如3 / 7
3 / 7 = 0 … 3
30 / 7 = 4 … 2
20 / 7 = 2 … 4
40 / 7 = 5 … 5
50 / 7 = 7 … 1
10 / 7 = 1 … 3
30 / 7 = 4 … 2
可以看到,已經出現循環0.(42571)。
每一次的除法時都有被除數和餘數,當除數重複出現時就表示出現循環節。
如果除數出現第二次,例如上例中30出現的時刻,就是第二次循環開始的位置。
所以需要記住每一次的商及除數的位置,除數不夠除的時候,補零。
#include <iostream>
#include <vector>
#include <cstring>
#include <string>
#include <map>
#include <cstdio>
#include <algorithm>
#define _for(i, a, b) for (int i = (a); i < (b); ++i)
using namespace std;
map<int, int> Pos;//用於計算除數的相應位置
/*
* 從除數和被除數的關係得到循環小數
* @param n 除數
* @param d 被除數
* @param ans 小數部分
* @param r 循環部分長度
*/
void solve(int n, const int d, string& ans, int& r)
{
Pos.clear();
ans = ".";//初始位置是1,ans.size() = 1
while (true)
{
n *= 10;
int p = Pos[n];
if (p == 0) Pos[n] = ans.size();//Pos[n], 表示除數是第幾個的位置
else
{ //如果除數再次出現,Pos[n]此時大於0
r = ans.size() - p;//找到循環節
if (r > 50) {ans.erase(p+50); ans += "...";}//刪除ans的50位置後字符
ans.insert(p, "(");
ans += ")";
break;
}
if (n < d) {ans += '0'; continue;}
int div = n / d, mod = n % d;
ans += (char)(div + '0');//添加手算小數
n = mod;
if (n == 0) {ans += "(0)"; r = 1;break;}
}
}
int main()
{
int a, b;
while (~scanf("%d%d", &a, &b))
{
string ans = ".(0)";
int r = 1;
if (a % b) solve(a % b, b, ans, r);
printf("%d/%d = %d%s\n", a, b, a/b, ans.c_str());
printf(" %d = number of digits in repeating cycle\n\n", r);
}
return 0;
}