حساب قيمة PI- ما هو الخطأ في التعليمات البرمجية

StackOverflow https://stackoverflow.com/questions/1479291

  •  18-09-2019
  •  | 
  •  

سؤال

أنا أفعل تمرين آخر C ++. يجب أن أحسب قيمة PI من السلسلة اللانهائية:

PI = 4 - 4/3 + 4/5 - 4/7 + 4/9 -4 / 11 +. وبعد وبعد

يتعين على البرنامج طباعة القيمة التقريبية ل PI بعد كل من أول 1000 حالة من هذه السلسلة. هنا هو رمزي:

#include <iostream>
using namespace std;

int main()
{
    double pi=0.0;
    int counter=1;

    for (int i=1;;i+=2)//infinite loop, should "break" when pi=3.14159
    {
        double a=4.0;
        double b=0.0;

        b=a/static_cast<double>(i);

        if(counter%2==0)
            pi-=b;
        else
            pi+=b;

        if(i%1000==0)//should print pi value after 1000 terms,but it doesn't  
            cout<<pi<<endl;

        if(pi==3.14159)//this if statement doesn't work as well
            break;

        counter++;
    }

    return 0;
}

تتجمع دون أخطاء وتحذيرات، ولكن تظهر نافذة وحدة التحكم الفارغة فقط بعد التنفيذ. إذا قمت بإزالة السطر "إذا كان (i٪ 1000 == 0)"، يمكنني أن أرى أنه تشغيل وطباعة كل قيمة بي، لكنه لا يتوقف، مما يعني أن العبارة الثانية لا تعمل أيضا. لست متأكدا مما يجب القيام به. أنا أفترض أنه ربما خطأ منطقي بسيط.

هل كانت مفيدة؟

المحلول

حسنا، I٪ 1000 لن = 0، حيث يعمل عدادك من I = 1، ثم بزيادات 2. وبالتالي، أنا دائما غريب، ولن أكون مضاعفا 1000.

السبب في أنه لا ينتهي أبدا هو أن الخوارزمية لا تتلتقي إلى 3.14157 بالضبط - ستكون دقة أعلى إما تحت أو فوق التقريب. تريد أن تقول "عندما داخل دلتا معينة من 3.14157"، لذلك اكتب

if (fabs(pi - 3.14157) < 0.001)
  break

أو شيء مماثل، ولكن "إغلاق" تريد الحصول عليه قبل التوقف.

نصائح أخرى

نظرا لأنك تبدأ في 1 وزيادة 2، فأنا دائما رقم فردي، لذلك لن يكون I٪ 1000 أبدا 0.

لديك أكثر من مشكلة واحدة:

أ. أنا٪ 1000 == 0 لن يكون صحيحا أبدا لأنك تكرار أرقام فردية فقط.

P. PI == 3.14159: لا يمكنك مقارنة القيم المزدوجة تماما مثل ذلك لأن الطريقة التي يتم فيها تمثيل أرقام النقطة العائمة (يمكنك أن تقرأ عنها هنا في سؤال آخر). من أجل العمل، يجب أن تقارن القيم بطريقة أخرى - طريقة واحدة هي طرحها من بعضها البعض وتحقق من أن النتيجة المطلقة أقل من 0.0000001.

  1. لديك مشاكل بدقة النقطة العائمة. يحاول if(abs(pi - 3.14159) < 0.000005).
  2. i%1000 لن تكون أبدا 0 ل i هو دائما غريب.

لا ينبغي أن يكون:

if (counter%1000==0)
  1. أبدأ الساعة 1 ثم الزيادات بواسطة 2. لذلك أنا دائما غريب ولن أكون مضاعفا 1000، وهذا هو السبب في أنه إذا كان (i٪ 1000 == 0) لا يمر أبدا.

  2. مباشرة مقارنة العوامات لا تعمل، بسبب القضايا الدقيقة العائمة. ستحتاج إلى مقارنة أن الفرق بين القيم قريبة بما فيه الكفاية.

PI = 4 - 4/3 + 4/5 - 4/7 + 4/9 -4/11 + ...

عام

pi = σ.أنا=0 (-1)أنا 4 / (2أنا+1)

مما يمنحنا نهجا نظيفا لكل مصطلح؛ ال أناتم إعطاء المصطلح من قبل:

double term = pow(-1,i%2) * 4 / (2*i+1);

أين أنا= 0،1،2، ...، ن

لذلك، يمكن أن تكون حلقةنا بسيطة إلى حد ما، بالنظر إلى عدد من التكرارات N

int N=2000;
double pi=0;
for(int i=0; i<N; i++)
{
    double term = pow(-1,i%2) * 4 / (2*(double)i+1);
    pi += term;
    cout << i << "\t" << pi <<endl;
}

ذكرت سؤالك الأصلي "البرنامج لديه لطباعة القيمة التقريبية ل PI بعد كل من أول 1000 حالة من هذه السلسلة". هذا لا يعني أي حاجة للتحقق مما إذا كان قد تم الوصول إلى 3.14159، لذلك لم أدرج هذا هنا. ال pow(-1,i%2) المكالمة هي فقط لتجنب if البيانات (التي هي بطيئة) ومنع أي مضاعفات كبيرة أنا.

كن على علم أنه بعد عدد من التكرارات، فإن الفرق بين حجم PI وحجم المصطلح الصحيح (يقول -4/25) سيكون صغيرا جدا بحيث ستجاوز دقة أ double, لذلك ستحتاج إلى أنواع عالية الدقة للتعامل معها.

بواسطة ABS الافتراضي يستخدم ماكرو ABS وهو ما هو للتضاعف، استخدم مكتبة CMATH.

#include <iostream>
#include <cmath>

int main()
{
    double pi=0.0;

    double a=4.0;
    int i = 1; 

    for (i=1;;i+=2)
    {

        pi += (1 - 2 * ((i/2)%2)) * a/static_cast<double>(i);          

        if( std::abs(pi - 3.14159) < 0.000001 )
              break;

        if (i > 2000) //1k iterations
              break;
    }

    std::cout<<pi<<std::endl;

    return 0;
}

هنا هو رمز تصحيح. اعتقدت أنه قد يكون مفيدا في المستقبل إذا كان لدى شخص ما مشكلة مماثلة.

#include <iostream>
#include <cmath>

using namespace std;

int main()
{
double pi=0.0;
int counter=1;

for (int i=1;;i+=2)
{
 double a=4.0;
 double b=0.0;

 b=a/static_cast<double>(i);

 if(counter%2==0)
  pi-=b;
 else
  pi+=b;

 if(counter%1000==0) 
  cout<<pi<<" "<<counter<<endl;


 if (fabs(pi - 3.14159) < 0.000001) 
  break;

 counter++;
}
cout<<pi;

 return 0;
}

هنا أفضل واحد:

class pi_1000
{
public:
    double doLeibniz( int i ) // Leibniz famous formula for pi, source: Calculus II :)
    {
        return ( ( pow( -1, i ) ) * 4 ) / ( ( 2 * i ) + 1 );
    }

 void piCalc()
{
    double pi = 4;
    int i;

    cout << "\npi calculated each iteration from 1 to 1000\n"; //wording was a bit confusing.
                                                    //I wasn't sure which one is the right one: 0-1000 or each 1000th.
    for( i = 1; i < 1000; i++ )
    {
        pi = pi + doLeibniz( i );
        cout << fixed << setprecision( 5 ) << pi << "\t" << i + 1 << "\n";
    }

    pi = 4;
    cout << "\npi calculated each 1000th iteration from 1 to 20000\n";
    for( i = 1; i < 21000; i++ )
    {
        pi = pi + doLeibniz( i );
        if( ( ( i - 1 ) % 1000 )  == 0 )
            cout << fixed << setprecision( 5 ) << pi << "\t" << i - 1 << "\n";
    }

}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top