【问题标题】:Trying to approximate pi digits in C++试图在 C++ 中逼近 pi 数字
【发布时间】:2021-02-22 06:21:09
【问题描述】:

我试图在不牺牲太多速度的情况下获得尽可能多的正确数字,所以我发现 David Chudnovsky 和 ​​Gregory Chudnovsky 基于 Ramanujan 的工作的收敛系列,当我运行它时,无论 k 的值如何,3 位数字都不正确后,我会达到 3.14159265358973 我还注意到 counter = 1 ,这意味着循环不起作用?我非常感谢您的帮助。 我的代码是:

#include <iostream>
#include <cmath>
#include <iomanip>
using namespace std;


long double  factorial(unsigned long long int x, unsigned long long int n);


int main()
{
    long double big_sum = 0.0;
    unsigned long long int k = 0;
    unsigned long long int n = 0;
    long double numerator;
    long double denominator;
    long double turm;


    turm = (long double)12.0;

    cout << "TYPE NUMBER OF DIGITS : ";
    cin >> n;
    cout << endl;

    /* unsigned long long int counter = 0; */

    for (k = 0; k < pow(10, 10000); k++)
    {
        /* counter++; */
    
        cout << setprecision(n) << fixed;

        numerator = pow((-1.0), k) * factorial(6 * k, n) * (545140134 * k + 13591409);

        denominator = factorial(3 * k, n) * pow(factorial(k, n), 3) * pow(640320, 3 * k + (long 
        double)(3.0 / 2));

        big_sum += turm * (numerator / denominator);

        cout << "PI IS = " << pow(big_sum, -1);
    
        /* cout << "  counter = " << counter; */
    }

    return 0;
}

long double factorial(unsigned long long int x, unsigned long long int n)
{
    if (x == 0) return 1;

    long long int factorial_of_x = 1;

    for (unsigned long long int i = 1; i <= x; x++)
    {
        cout << setprecision(n) << fixed;

        factorial_of_x *= i;
    }

    return factorial_of_x;
}

【问题讨论】:

  • 什么计数器 = 1? cmets中的那个?那么请把 cmets 转成代码。不执行评论。严格来说,您的代码中没有counter
  • 我没有检查详细信息,但是每当阶乘出现在公式中时,您不能只为阶乘保留一个术语,这会溢出。为x = (numerator / denominator) 保留一个术语并在每个迭代中更新它,即不分开。个别术语肯定会溢出(无论您使用哪种类型),但整个 x 将保持在接近 1 的某个位置
  • ...请不要标记不相关的语言。即使你不关心 C 和 C++ 是不同的语言,我也不想搜索 C++ 问题然后发现答案是不同的语言
  • 你正在迭代 pow(10, 10000) 迭代......这些可能太多了,而且永远循环。
  • 啊,但那是因为您是从循环内部打印的。而且您的factorial 永远不会以x &gt; 0 结尾,因为循环增量中有x++ 而不是i++

标签: c++


【解决方案1】:

至少这些问题

递增i:

// for (unsigned long long int i = 1; i <= x; x++)
for (unsigned long long int i = 1; i <= x; i++)

减少巨大的迭代限制,因为factorial(6 * k, n) 迅速超过unsigned long long 范围。

//for (k = 0; k < pow(10, 10000); k++) {
for (k = 0; k < 5; k++) {

经过 2 次迭代,我达到了稳定的结果。 (为清楚起见添加了空格)

PI IS = 3.1415926535897 342 091255279861173
PI IS = 3.1415926535897 932 400306920008859
PI IS = 3.1415926535897 932 400306920008859

π Ref = 3.1415926535897 932 384626433832795...

样本固定阶乘

long double factorial(unsigned x) {
    if (x == 0) return 1;
    long double factorial_of_x = 1.0;
    for (unsigned i = 1; i <= x; i++) {
        factorial_of_x *= i;
    }
    return factorial_of_x;
}

【讨论】:

    【解决方案2】:

    你的循环中有这些术语:

    numerator = pow((-1.0), k) * factorial(6 * k, n) * (545140134 * k + 13591409);
    
    denominator = factorial(3 * k, n) * pow(factorial(k, n), 3) * pow(640320, 3 * k + (long 
        double)(3.0 / 2));
    
    big_sum += turm * (numerator / denominator);
    

    问题在于numeratordenominator 都增长得非常快。无论你使用什么类型,都会在某个时候溢出。

    另一方面,您在每次迭代中添加的项 turm * (numerator / denominator); 将收敛到 1(因为,假设您没有弄错,整个迭代的设置使得 big_sum 收敛到 1/pi) .

    每次迭代不计算完整项,只考虑下一次迭代的变化。我不知道你使用的公式,所以为了说明,我将使用极其简化的表达式(fact(k)k 的阶乘):

    num = pow((-1.0), k) * fact(k) * (k);
    den = fact(k);
    big_sum += turm * (numerator / denominator);
    

    现在只考虑每次迭代中的变化:

    num_i+1 = num_i * (-1.0) * (i+1) * (i+1)/(i);
    den_i+1 = den_i * (i+1);
    

    当然,仅此一项是无济于事的。它只是写下相同内容的不同方式,值将相同。诀窍是要意识到虽然numden 增长很快,但将它们分开会产生相对较小的数字。所以不要保留单独的条款......

    dn_i+1 = num_i+1 / den_i+1
           = num_i/den_i * (-1.0) * (i+1)/(i);
    

    我希望有可能掌握这个想法。正如我之前所说,我使用了(错误的)简化表达式。尽管对于实数表达式,策略是相同的:不要计算大数,只累积您应用于最终结果的因子。这个因子保证收敛到1(因为否则迭代不会收敛到某个有限的结果)。

    【讨论】:

    • 谢谢,我想我明白了,我会试试看
    • @George122V fwiw,我自己不喜欢这个答案。问题是我只有在写作时才意识到这些表达式比我最初想象的要复杂得多。在提出因子收敛到1 的论点时,我也混淆了加法/乘法。关键是,如果迭代收敛,那么您应用于big_sum 的更改将随着每次迭代而变得越来越小,而中间结果将变得越来越大。整个想法只是为了避免那些不断增长的中间词。希望你能从答案中得到一些东西......
    【解决方案3】:

    我看到了你的帖子,你可以使用方程式 这里是 π/4 = 3 tan^-1(1/4) + tan^-1(1/20) + tan^-1(1/1985)

    或者你可以做哈佛所做的事情:物理模拟

    这是一个 3blue1brown 视频 https://www.youtube.com/watch?v=HEfHFsfGXjs

    【讨论】:

    • 使用 tan 计算 Pi 在这种情况下是作弊。
    • @tadman 好吧,你总是可以用polynomials 来近似arctans,不过......
    • 与其用“3 tan^-1(1/4) + tan^-1(1/20) + tan^-1(1/1985)”作弊,不如用acos(-1)作弊
    猜你喜欢
    • 2016-08-26
    • 1970-01-01
    • 2010-12-25
    • 2018-07-23
    • 1970-01-01
    • 2012-01-01
    • 2016-07-14
    • 2011-01-21
    • 2012-09-28
    相关资源
    最近更新 更多