找出分数 a/b 的小数点后第 k 位,其中 a,b,k 是非常大的整数(小于 10e18)

Con*_* An 5 c++ algorithm math

我的任务是在分数 (a/b) 的小数点后找到第 k 个位置的数字。昨天我发现了这个算法。
为了获得小数点后的任何数字,我生成一个名为 rem 的变量并进行循环

for (int i = 1; i <= k+1; i++)    
      {
         rem = a%b;
         a = rem*10;
      }
      cout << a/b;    
Run Code Online (Sandbox Code Playgroud)

循环将返回一个值,该值是小数点后的第 k 位数字。
但是任务要求我计算 a,b,k 是非常大的数字(小于或等于 10e18),所以代码肯定会超过时间限制。

  • 找出重复前的位数。它是分母中 2 和 5 因数中的较大者。
  • 如果 k 不超过位数,则运行 for 循环。
  • 否则,我们仍将 for 循环运行到 k+1。将除法余数的值存储在变量 x 中。
  • 使用上面相同的内容运行 while 循环,直到余数再次具有 x 的值。从那时起,将除法的每个商存储到一个数组名称 qut 中。
  • while 循环结束后,数组将在 repetend 中存储每个数字。根据数组里面的位数,我们可以计算出第k位。
    然而,这个算法仍然被证明是耗时的,因为在 a 和 b 是两个连续整数的情况下,重复变得非常大。你能帮我出个主意吗?

orl*_*rlp 5

for 循环计算的实际上是 10 * (a*10 k % b) / b。我们可以通过平方求幂来更有效地做到这一点。但我们必须小心,不要在每一点都溢出:

int kth_digit_frac(uint64_t a, uint64_t b, uint64_t k) {
    return 10 * mulmodu64(a, powmod(10, k, b), b) / b;
}

// a*b % m, overflow safe
inline uint64_t mulmodu64(uint64_t a, uint64_t b, uint64_t m) {
    #if defined(__GNUC__) && defined(__x86_64__)
        uint64_t q, r;
        asm("mulq %3;"
            "divq %4;"
            : "=a"(q), "=d"(r)
            : "a"(a), "d"(b), "rm"(m)
            : "cc");
        return r;
    #else
        a %= m;
        b %= m;

        // No overflow possible.
        if (a == 0) return 0;
        if (b <= std::numeric_limits<uint64_t>::max() / a) return (a*b) % m;

        uint64_t res = 0;
        while (a != 0) {
            if (a & 1) {
                if (b >= m - res) res -= m;
                res += b;
            }

            a >>= 1;
            if (b >= m - b) b += b - m;
            else            b += b;
        }

        return res;
    #endif
}


// b^e % m, overflow safe
inline uint64_t powmod(uint64_t b, uint64_t e, uint64_t m) {
    uint64_t r = 1;

    b %= m;
    while (e) {
        if (e % 2 == 1) r = mulmodu64(r, b, m);
        b = mulmodu64(b, b, m);
        e >>= 1;
    }

    return r;
}
Run Code Online (Sandbox Code Playgroud)

a,b,k对于任何适合 64 位整数的内容,它都会在眨眼间运行。