Что я должен изменить, чтобы мое приближение arctan (x) могло правильно отображать x = 1 и x = -1? - PullRequest
0 голосов
/ 19 ноября 2018

Одним из моих заданий на С было написать приближение arctan(x) на языке C. Уравнение, на котором я должен основывать это:

arctan(x)=\sum {k=0}^{\infty }(-1)^{k} \tfrac{x^{2k+1}}{2k+1}

enter image description here

Кроме того, x определяется только как -1<=x<=1.

Вот мой код.

#include <stdio.h>
#include <math.h>


double main(void) {

    double x=1;
    double k;
    double sum;
    double sum_old;
    int count;

    double pw(double y, double n) {
        double i;
        double number = 1;

        for (i = 0; i < n; i++) {
            number *= y;
        }
        return(number);
    }

    double fc (double y) {
        double i;
        double number = 1;

        for (i = 1; i <= y; i++){
            number *= i;
        }
        return(number);
    }

    if(x >= (-1) && x <= 1) {
        for(k=0; sum!=sum_old; k++) {
            sum_old = sum;
            sum += pw((-1), k) * pw(x, (2*k) + 1)/((2*k) + 1);
            count++;

            printf("%d || %.17lf\n", count, sum);
        } 



    printf("My result is: %.17lf\n",sum);
    printf("atan(%f) is: %.17f\n", x, atan(x));
    printf("My result minus atan(x) = %.17lf\n", sum - atan(x));
    } else {
        printf("x is not defined. Please choose an x in the intervall [-1, 1]\n");
        }



return 0;
}

Кажется, что он отлично работает с каждым значением, кроме значений 1 и -1. Если x=1, то вывод заканчивается на:

...
7207 || 0.78543285189457468
7208 || 0.78536

Принимая во внимание, что выходные данные должны выглядеть больше как это. В этом случае х = 0,5.

25 || 0.46364760900080587
26 || 0.46364760900080587
My result is: 0.46364760900080587
atan(0.500000) is: 0.46364760900080609
My result minus atan(x) atan(x) = -0.00000000000000022

Как я могу улучшить свой код, чтобы он мог работать с x=1 и x=-1.

Заранее спасибо.

PS: я использую свою собственную созданную функцию pw() вместо pow(), потому что я хотел обойти ограничение не использовать pow(), поскольку у нас этого еще не было в наших лекциях.

PPS: Буду признателен за советы по улучшению моего кода.

Ответы [ 4 ]

0 голосов
/ 20 ноября 2018

Вот некоторые модификации, которые делают ваш код более компактным и содержат меньше ошибок:

#include <stdio.h>
#include <math.h>

#define x 0.5               //here x is much easier to change

double pw(double, double);      //declaration of the function should be done

int main() {                    //the default return type of main is int.

    double k;
    double sum = 0 ;                //you should initiate your variables.
    double sum_old = 1 ;            //=1 only to pass the for condition first time.
                                    //you don't need to define counter here
    if(x < -1 || x > 1){
        printf("x is not defined. Please choose an x in the interval [-1, 1]\n");
        return 0;
    }
    for(k=0; sum!=sum_old; k++) {
        sum_old = sum;
        sum += pw((-1), k) * pw(x, (2*k) + 1)/((2*k) + 1);
        printf("%.0f || %.17lf\n", k, sum);
    }

    printf("My result is: %.17lf\n",sum);
    printf("atan(%f) is: %.17f\n", x, atan(x));
    printf("My result minus atan(x) = %.17lf\n", sum - atan(x));

return 0;
}

double pw(double y, double n) {         //functions should be declared out of the main function
    double i;
    double number = 1;

    for (i = 0; i < n; i++) {
        number *= y;
    }
    return(number);
}

double fc (double y) {
    double i;
    double number = 1;

    for (i = 1; i <= y; i++){
        number *= i;
    }
    return(number);
}
0 голосов
/ 20 ноября 2018

В каждой итерации вы добавляете (-1) k x 2 k + 1 / (2 k + 1), и вы останавливаетесь, когда сумма не изменяется.

Если бы это было вычислено с идеальной арифметикой (точной, бесконечно точной арифметикой), она никогда не будетостановка для ненулевого x , так как вы всегда меняете сумму.При вычислении с арифметикой с фиксированной точностью он останавливается, когда член настолько мал, что не меняет сумму из-за ограниченной точности.

Когда | x |меньше единицы на любую значительную величину, это происходит быстро, потому что x 2 k + 1 становится меньше.Когда | х |один, термин становится просто 1 / (2 k + 1), который уменьшается очень медленно.Только когда k приблизится к 2 53 , сумма не изменится.

Вы можете рассмотреть возможность изменения условия остановки, когда sum не изменилось с sum_old Скорее, чем тогда, когда он вообще не изменился.

0 голосов
/ 20 ноября 2018

Я думаю, что причина этого в математической, а не в программировании.

Вдали от мелких ошибок и корректировок, которые вы должны сделать в своем коде, включение x = 1 в бесконечную серию арктанов является граничным условием: arctan infinite series В этой сериимы добавляем отрицательное значение к положительному значению, а затем к отрицательному значению.Это означает, что сумма будет увеличиваться, уменьшаться, увеличиваться, ... и это будет иметь значение для каждой итерации.Эта разница будет меньше, пока точность double не поймает ее, поэтому программа остановится и выдаст нам значение.

Но в уравнении суммы.Когда мы устанавливаем z = 1 и n переходит от 0 к ∞, это сделает этот член (-1 ^ n) равным 1 за один раз и -1 на следующей итерации.Кроме того, значение z-члена будет равно единице, а значение знаменателя при приближении n к бесконечности будет = ∞.

Таким образом, сумма нескольких итераций будет равна + 1 / ∞ -1 / ∞ + 1 / ∞-1 / ∞ ... (где ∞ здесь представляет большое число).Таким образом, серия не достигнет определенного числа.Это потому, что z = 1 является границей в этом уравнении.И это вызывает бесконечные итерации в вашем решении, не достигая числа.

Если вам нужно вычислить arctan (1), я думаю, вам следует использовать эту формулу: arctan 2

Все формулы взяты из этой статьи в Википедии .

0 голосов
/ 20 ноября 2018
if(x >= (-1) && x <= 1) {
    for(k=0; sum!=sum_old; k++) {
        sum_old = sum;
        sum += pw((-1), k) * pw(x, (2*k) + 1)/((2*k) + 1);
        count++;

        printf("%d || %.17lf\n", count, sum);
    } 

Сравнение пар может быть хитрым . Обычный способ сравнения пар - это тестирование в пределах epsilon . Где-то должно быть определено значение эпсилона, но для каких целей достаточно цифр для аппроксимации? Если вам нужны только 3 или 4 цифры, вы можете вместо этого иметь

#define EPSILON 0.0001 //make this however precise you need to approximate.

if(x >= (-1) && x <= 1) {
    for(k=0; fabs(sum - sum_old) > EPSILON; k++) {
        sum_old = sum;
        sum += pw((-1), k) * pw(x, (2*k) + 1)/((2*k) + 1);
        count++;

        printf("%d || %.17lf\n", count, sum);
    } 

Если проблема в том, что -1,1 итерации слишком много раз, либо уменьшите точность, либо увеличьте шаг на итерацию. Я не уверен, что это то, что вы спрашиваете, пожалуйста, уточните.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...