【问题标题】:Calculate Nth Pi Digit计算第 N 个 Pi 数字
【发布时间】:2015-05-31 15:30:42
【问题描述】:

我试图在不使用Math.Pi 的情况下计算 Pi 的第 n 位数字,它可以指定为参数。 我修改了一个现有的算法,因为我喜欢在不使用字符串转换或默认类的情况下找到第 N 个数字。 这是我的算法目前的样子:

   static int CalculatePi(int pos)
    {
        List<int> result = new List<int>();
        int digits = 102;
        int[] x = new int[digits * 3 + 2];
        int[] r = new int[digits * 3 + 2];
        for (int j = 0; j < x.Length; j++)
            x[j] = 20;
        for (int i = 0; i < digits; i++)
        {
            int carry = 0;
            for (int j = 0; j < x.Length; j++)
            {
                int num = (int)(x.Length - j - 1);
                int dem = num * 2 + 1;
                x[j] += carry;
                int q = x[j] / dem;
                r[j] = x[j] % dem;
                carry = q * num;
            }
            if (i < digits - 1)

                result.Add((int)(x[x.Length - 1] / 10));
            r[x.Length - 1] = x[x.Length - 1] % 10; ;
            for (int j = 0; j < x.Length; j++)
                x[j] = r[j] * 10;
        }
        return result[pos];
    }

到目前为止,它一直工作到第 32 位,然后出现错误。 当我尝试像这样打印数字时:

  static void Main(string[] args)
    {
        for (int i = 0; i < 100; i++)
        {
            Console.WriteLine("{0} digit of Pi is : {1}", i, CalculatePi(i));
        }

        Console.ReadKey();
    }

第 32 位和第 85 位以及其他一些数字我得到 10,这显然是不正确的。

27 的原始数字如下所示:

...3279502884.....

但我明白了

...32794102884....

算法有什么问题,我该如何解决这个问题? 算法还可以调整以提高速度吗?

【问题讨论】:

  • 嗯,用调试器和原程序对比一下,你觉得我们应该给你调试一下?
  • 速度的提升在于不循环调用计算,因为你一遍又一遍地重新计算数字列表,只是为了提取不同的数字。返回列表而不是单个值。
  • @MOehm:感谢您的想法。但是您是否也对错误的“10”位有所了解?我该如何纠正算法中的这个错误?

标签: c# algorithm pi digit


【解决方案1】:

到目前为止,它一直有效,直到光标到达数字 32。在此之后,将引发异常。

规则如下:

  • 数字 31 is incorrect,因为它应该是 5 而不是 4。
  • 第 32 位应为 0。
  • 当您得到 10 位结果时,您需要将 1 加到前一位上,以将 10 更改为 0。

当 362 = 10 时,下面的代码更改将适用于 ~ 数字 361。

一旦程序进入 900's 那么就会有很多错误的数字。

在您的循环中,您可以通过跟踪前一个数字来做到这一点,仅在计算出后续数字后将其添加到列表中。

溢出需要在发生时进行处理,如下所示:

    int prev = 0;

    for (int i = 0; i < digits; i++)
    {
        int carry = 0;

        for (int j = 0; j < x.Length; j++)
        {
            int num = (int)(x.Length - j - 1);

            int dem = num * 2 + 1;

            x[j] += carry;

            int q = x[j] / dem;

            r[j] = x[j] % dem;

            carry = q * num;

        }

        // calculate the digit, but don't add to the list right away:
        int digit = (int)(x[x.Length - 1] / 10);

        // handle overflow:
        if(digit >= 10)
        {
            digit -= 10;

            prev++;

        }

        if (i > 0)
            result.Add(prev);

        // Store the digit for next time, when it will be the prev value:

        prev = digit;

        r[x.Length - 1] = x[x.Length - 1] % 10;

        for (int j = 0; j < x.Length; j++)
            x[j] = r[j] * 10;

    }

由于数字是一个接一个地按顺序更新的,比以前晚了一整轮迭代,所以可以删除if (i &lt; digits - 1) 检查。

但是,您需要添加一个新的来替换它:if (i &gt; 0),因为您在第一次通过循环时没有有效的prev 值。

仅计算前 100 位数字的巧合意味着上述方法可行。

但是,您认为当 10 位结果跟随 9 位结果时会发生什么?恐怕不是好消息,因为 1 需要结转到 9(以前的值),这将变为 10。

一个更强大的解决方案是完成您的计算,然后在您的列表上向后循环,将您遇到的任何 10 带到前面的数字,并传播任何进位。

考虑以下几点:

for (int pos = digits - 2; pos >= 1; pos--)
{
     if(result[pos] >= 10)
     {
          result[pos] -= 10;

          result[pos - 1] += 1;

     }

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-08
    • 1970-01-01
    • 2012-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-13
    相关资源
    最近更新 更多