简体   繁体   English

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

[英]Find the kth digit after the decimal point of a fraction a/b with a,b,k being very large integers (less than 10e18)

I was tasked to find a digit of kth position after the decimal point of a fraction(a/b).我的任务是在分数 (a/b) 的小数点后找到第 k 个数字 position。 Yesterday I found out this algorithm.昨天我发现了这个算法。
To get any digit after the decimal point, I generate a variable called rem and make a loop为了获得小数点后的任何数字,我生成了一个名为 rem 的变量并进行了循环

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

the loop will return a value being the kth digit after the decimal point.循环将返回一个值,该值是小数点后的第 k 个数字。
However the task require me to calculate with a,b,k being very large number ( less or equal to 10e18), so it's sure that the code will exceed the time limit.但是这个任务要求我用a,b,k计算非常大的数(小于或等于10e18),所以代码肯定会超过时间限制。

  • Find the number of the digits before the repetend.找出重复之前的位数。 It's the greater of the numbers of 2 and 5 factors in the denominator.它是分母中 2 和 5 因数中较大的一个。
  • If the k doesn't exceed the number of digits, run the for loop.如果 k 不超过位数,则运行 for 循环。
  • Else, we will still run the for loop to k+1.否则,我们仍然会运行 for 循环到 k+1。 Store the value of the remainder of the division in a variable x.将除法的余数存储在变量 x 中。
  • Run a while loop with the same content above until the remainder again have the value of x.以上面相同的内容运行一个 while 循环,直到余数再次具有 x 的值。 Since then, store every quotient of the division into an array name qut.此后,将除法的每一个商存储到一个名为 qut 的数组中。
  • After the while loop terminates, the array will have stored every digit inside the repetend. while 循环终止后,数组将存储 repetend 中的每个数字。 According to the number of the digits inside the array, we can calculate the kth digit.根据数组中位数的个数,我们可以计算出第k位。
    However, this algorithm still prove to be time consuming because in the case of a and b are two consecutive integer, the repetend become very large.但是,这个算法仍然被证明是耗时的,因为在a和b是两个连续的integer的情况下,repetend变得非常大。 Can you help me with any idea?你能帮我个主意吗?

What your for loop calculates is effectively 10 * (a*10 k % b) / b. 您的for循环计算的有效值为10 *(a * 10 k %b)/ b。 We can do this more efficiently by doing exponentiation by squaring. 我们可以通过平方求幂来更有效地做到这一点。 We have to be careful not to overflow at every point though: 但是,我们必须注意不要在每个点上都溢出:

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;
}

It runs in the blink of an eye for any a,b,k that fit in 64 bit integers. 眨眼之间就可以找到适合64位整数的任意a,b,k

I believe two of the solutions above is still incorrect, because of a little error.我相信上面的两个解决方案仍然不正确,因为有一点错误。

I think the following will work nicely:我认为以下内容会很好地工作:

10 * (a*10^(k-1) % b) / b

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM