Я пытаюсь запустить этот код в Dev C ++:
#include<conio.h>
#include<iostream>using namespace std;
int main()
{
float sum =1;
int num = -1;
for(int i=1; i<=1000; i++)
{
num *= i;
sum += 1/(num);
}
cout<<sum<<endl;
getch();
return 0;
}
Всякий раз, когда я компилирую и запускаю код, я получаю сообщение об ошибке Windows, что программа перестала работать. Может ли кто-нибудь помочь мне?
С помощью этого кода я пытаюсь вычислить следующее суммирование:
∞
∑ 1 / н!
п = 0
(Нижний предел вашей суммы должен быть 1, а не 0, но давайте отложим это.)
Ваш алгоритм настолько нестабилен, что никакие исправления не приведут к его эффективной работе. Проще говоря, факториалы просто становятся слишком большими. Поведение при переполнении целочисленного типа со знаком, кроме signed char
или же char
не определено Кажется, что происходит то, что int
по сути, принимает факториал по модулю большой степени 2, и в конечном итоге это будет 0, так как большие факториалы кратны большим степеням 2. Таким образом, происходит деление на 0, что приводит к поведению, которое вы наблюдаете.
К счастью, вы можете заметить, что ваша сумма может быть записана как
1 + 1/2(1 + 1/3(1 + 1/4...))
что гораздо проще разбить на части численно. Один подход будет
#include <iostream>
#include <iomanip>
#include <cmath>
int main() {
constexpr int N = 19;
double sum = 1 + 1.0 / N;
for (int n = N - 1; n >= 1; --n){
sum = 1 + sum / n;
}
std::cout << std::setprecision(15) << sum << " " << std::exp(1);
}
Выход 2.71828182845905 2.71828182845905
получается, показывая, что это правильно, так как сумма этой серии является математической константой е.
Хотя это прекрасно с точки зрения численности — скрытые глубины включают в себя тот факт, что термины имеют одинаковую величину, а сходимость происходит очень быстро, и этот ответ, как мы надеемся, развенчивает распространенный миф о том, что арифметика с плавающей запятой по своей сути неточна, недостатком этого подхода является необходимость знать верхний предел заранее; то есть вы не можете остановиться, когда достигается определенная терпимость. По опыту знаю, что вам понадобится около 20 терминов.
Это происходит потому, что 1000!
на самом деле огромное количество, которое намного больше, чем то, что int
или даже long long
мог справиться. Также важно, что именно происходит в реальной жизни, когда умножение переполняется. Короче говоря, очень скоро ваш num
было бы точно 0
что может быть немного удивительным для вас (это должно произойти, когда n!
делится на 2^32
). А потом попытаться вычислить 1/(num)
терпит неудачу с арифметической ошибкой (деление на 0).
Подсказка: на самом деле 1000!
настолько велика, что ни один стандартный тип не может справиться с этим (и 1/1000!
достаточно мал, чтобы быть за пределами точности любого стандартного типа). Если вы действительно хотите рассчитать сумму достаточно точно, вам придется использовать несколько нетривиальных приемов.
Следите за обратным факториалом. В этом случае вы можете просто разделить предыдущее значение.
#include<iostream>
using namespace std;
int main()
{
double sum = 1.0;
int num = 1;
double inv_fact = 1.0;
for(int i=1; i<=1000; i++)
{
inv_fact = inv_fact / i;
sum += inv_fact;
}
cout<<sum<<endl;
return 0;
}
дает:
2,71828