分数化小数 计蒜客(无限循环小数 循环节 欧拉函数 欧拉定理 十进制)

原题链接

分数化小数

题目描述

对于一个分数(不一定是最简形式),给出它的小数形式,如果小数有循环节的话,把循环节放在一对圆括号中.
例如,1/4 =0.25,1/3=0.3333写成0.(3)1/7= 0.142857142857...写成0.(142857)。如果结果是一种整数xxx,则用xxx.0 等表示整数xxx
输入包括一行,包括被空格分隔开的分子N和分母D(第一个是N,第二个是D)。
输出包括一行,为转换后的小数形式。

输入样例

45 56

输出样例

0.803(571428)

题目解释

题目中需要求一个 分数 的小数,如果是无限循环小数,则输出 0.xxx(xxx) 的格式。
因此我们考虑,先求出这个小数的 循环起始点(S)循环长度(T)

我们举个栗子。对于 x = 45 56 x = \frac{45}{56} x=5645 这种情况。
我们考虑先对 x ∗ 10 x * 10 x10 即: 45 56 , 450 56 , 4500 56 , 45000 56 ⋯ \frac{45}{56},\frac{450}{56},\frac{4500}{56},\frac{45000}{56}\cdots 5645,56450,564500,5645000
然后我们将这些分数的分子进行 m o d   56 mod\ 56 mod 56 操作, 即: 45 56 , 2 56 , 20 56 , 32 56 , 40 56 , 8 56 , 24 56 , 16 56 , 48 56 , 32 56 , 40 56 ⋯ \frac{45}{56},\frac{2}{56},\frac{20}{56},\frac{32}{56},\frac{40}{56},\frac{8}{56},\frac{24}{56},\frac{16}{56},\frac{48}{56},\frac{32}{56},\frac{40}{56}\cdots 5645,562,5620,5632,5640,568,5624,5616,5648,5632,5640
我们可以明显的发现当操作进行到第 10 次的时候和第 4 次重复了,显然已经形成了一个长度为 6 的循环节,即从第 4 项开始循环。

由此我们可以推广到更一般的情况。假设分数为 p q \frac{p}{q} qp ,由于小数部分和整数无关,因此我们可以假设这个分数为真分数。不妨假设 p < q p<q p<q

由上可知,我们可以把第 k + 1 k + 1 k+1 个分数写成 p ∗ 1 0 k   m o d   q q \frac{p*10^k\ mod\ q}{q} qp10k mod q
因此我们可以假设第 i i i 个分数和第 j j j 个分数相等,即构成了一个循环节。有 p ∗ 1 0 i − 1   m o d   q q = p ∗ 1 0 j − 1   m o d   q q \frac{p*10^{i-1}\ mod\ q}{q} = \frac{p*10^{j -1}\ mod\ q}{q} qp10i1 mod q=qp10j1 mod q
两边同乘 10 10 10 ,得 p ∗ 1 0 i   m o d   q q = p ∗ 1 0 j   m o d   q q \frac{p*10^i\ mod\ q}{q} = \frac{p*10^j\ mod\ q}{q} qp10i mod q=qp10j mod q
p ∗ 1 0 j ≡ p ∗ 1 0 i ( m o d   q ) ( i < j ) p * 10^j ≡ p * 10^i (mod \ q) \quad (i < j) p10jp10i(mod q)(i<j)
又可表示为 p ∗ 1 0 j = p ∗ 1 0 i + q ∗ k p*10^j = p*10^i + q*k p10j=p10i+qk
g = g c d ( p , q ) g = gcd(p,q) g=gcd(p,q),设 p = p ′ ∗ g , q = q ′ ∗ g p = p'*g,q=q'*g p=pg,q=qg。即 p ′ ∗ 1 0 j − p ′ ∗ 1 0 i = q ′ ∗ k p' *10^j - p'*10^i=q'*k p10jp10i=qk
p ′ ( 1 0 j − 1 0 i ) = q ′ ∗ k   →   p ′ ∗ 1 0 i ( 1 0 j − i − 1 ) = q ′ k p'(10^j-10^i) = q'*k \ →\ p'*10^i(10^{j - i} - 1) = q'k p(10j10i)=qk  p10i(10ji1)=qk
由此可知 q ′ ∣ 1 0 i ( 1 0 j − i − 1 ) p ′ q'|10^i(10^{j - i} - 1)p' q10i(10ji1)p
因为 p ′ p' p q ′ q' q 互质,因此可得 q ′ ∣ 1 0 i ( 1 0 j − i − 1 ) q'|10^i(10^{j - i} - 1) q10i(10ji1)
1 0 i 10^i 10i 为偶数,且和 q ′ q' q 有公因数。
( 1 0 j − i − 1 ) (10^{j - i} - 1) (10ji1) 为奇数,显然不可能存在和 q ′ q' q 的公因数,因此 q ′ q' q ( 1 0 j − i − 1 ) (10^{j - i} - 1) (10ji1) 互质。
显然 i i i 1 0 i 10^i 10i q ′ q' q 共同决定,又知 1 0 i 10^i 10i q ′ q' q 的公因数由 2 2 2 5 5 5 贡献。
因此 i = m a x ( n , m ) i = max(n,m) i=max(n,m) n n n q ′ q' q中贡献的 2 2 2 的个数, m m m q ′ q' q中贡献的 5 5 5 的个数。

接下来需要求循环节 T = j − i T = j - i T=ji

q ′ ′ = q ′ 2 n ∗ 5 m q'' = \frac{q'}{2^n*5^m} q=2n5mq
经过一番计算,上式 q ′ ∣ 1 0 i ( 1 0 j − i − 1 ) q'|10^i(10^{j - i} - 1) q10i(10ji1) 已经变成了 q ′ ∣ 2 n 5 m ( 1 0 j − i − 1 ) ∗ 1 0 i 2 n 5 m q'|2^n5^m(10^{j - i} - 1)*\frac{10^i}{2^n5^m} q2n5m(10ji1)2n5m10i
q ′ ′ ∣ ( 1 0 j − i − 1 ) ∗ 1 0 i 2 n 5 m q''|(10^{j - i} - 1)*\frac{10^i}{2^n5^m} q(10ji1)2n5m10i
又知 q ′ ′ q'' q 1 0 i 2 n 5 m \frac{10^i}{2^n5^m} 2n5m10i 互质,且由上文可知 q ′ q' q ( 1 0 j − i − 1 ) (10^{j - i} - 1) (10ji1) 互质可知 q ′ q' q ( 1 0 j − i − 1 ) (10^{j - i} - 1) (10ji1) 也互质。
只需要解出 j − i j -i ji 就是我们无限循环小数中的循环节了。即
1 0 j − i ≡ 1   ( m o d   q ′ ′ ) 10^{j - i}≡1\ (mod\ q'') 10ji1 (mod q)
不妨假设 x = j − i x = j - i x=ji,即解 1 0 x ≡ 1   ( m o d   q ′ ′ ) 10^x≡1\ (mod\ q'') 10x1 (mod q)
由欧拉定理可知,存在最小的 1 0 x ≡ 1   ( m o d   q ′ ′ ) 10^x≡1\ (mod\ q'') 10x1 (mod q) ,当 x x x φ ( q ′ ′ ) \varphi(q'') φ(q) 的一个因子。
因此我们只需要去枚举所有 φ ( q ′ ′ ) \varphi(q'') φ(q) 的因子,从而找到一个最小的 x x x,使得 1 0 x ≡ 1   ( m o d   q ′ ′ ) 10^x≡1\ (mod\ q'') 10x1 (mod q)

此时解出的 i i i 就是循环节开始的前一位, x x x j − i j - i ji 为循环节的长度。
接下来的任务只需要模拟乘法,按照题目要求输出即可。

但是非常悲伤的事情是,队友暴力模拟 A 了。

代码

#include 
using namespace std;
typedef long long LL;
LL p, q, S, T, Z;

LL Euler(LL x) {
  LL ans = x;
  for (LL i = 2; i * i <= x; i++) {
    if (x % i == 0) {
      ans = (ans/i) * (i - 1);
      while (x % i == 0) x /= i;
    }
  }
  if (x > 1) ans = (ans/x) * (x - 1);
  return ans;
}

LL power(LL a, LL x, LL mod) {
  LL ans = 1;
  while (x) {
    if (x & 1) ans = (ans * a)%mod;
    a = (a * a)%mod;
    x >>= 1;
  }
  return ans;
}

LL gcd(LL a, LL b) {
  return b == 0? a:gcd(b, a%b);
}

LL get_first(LL &x) {
  LL ans1 = 0, ans2 = 0;
  while (x % 2 == 0) {
    x /= 2;
    ans1++;
  }
  while (x % 5 == 0) {
    x /= 5;
    ans2++;
  }
  return max(ans1, ans2);
}

LL get_T(LL x, LL mod) {
  LL Min = 1e18;
  for (LL i = 1; i * i <= x; i++) {
    if (x % i == 0) {
      if (power(10, i, mod) == 1) {Min = min(Min, i); break;}
      if (power(10, x/i, mod) == 1) Min = min(Min, x/i);
    }
  }
  return Min;
}

void print(LL p, LL q, LL S, LL T) {
  printf("%lld.", p/q);
  p -= q * (p/q);
  for (int i = 0; i < S + T; i++) {
    if (i == S) printf("(");
    p *= 10;
    printf("%lld", p/q);
    p -= q * (p/q);
    if (p == 0) break;
    if (i == S + T - 1) printf(")");
  }
  printf("\n");
}

int main()
{
  while (scanf("%lld %lld", &p, &q) != EOF) {
    LL a = p, b = q;
    LL g = gcd(p, q);
    p /= g, q /= g;

    S = get_first(q);
    LL phi = Euler(q);
    T = get_T(phi, q);
    
    if (T == 1e18) S = T;
    print(a, b, S, T);
  }

  return 0;
}

你可能感兴趣的:(编程语言,c语言,笔记本推荐,c,算法)