Я считаю, что проблема связана с np.exp(-x)
, которая быстро становится очень маленькой, поскольку x
увеличивается, что приводит к оценке как нулю из-за ограниченной числовой точности. Например, даже для x
всего x=10**2*
, np.exp(-x)
оценивается в 3.72007597602e-44
, тогда как x
значения 10**3
или выше результат 0
.
Я не знаю особенностей реализации quad
, но он, вероятно, выполняет некоторую выборку функции, которая должна быть интегрирована в заданный диапазон интеграции. Для большого верхнего предела интегрирования большинство образцов np.exp(-x)
оцениваются в ноль, поэтому значение интеграла недооценивается. (Заметим, что в этих случаях абсолютная погрешность по quad
имеет тот же порядок, что и интегральное значение, которое является индикатором того, что последний является ненадежным.)
Один из способов избежать этой проблемы - ограничить верхнюю границу интеграции значение, выше которого числовая функция становится очень малой (и, следовательно, незначительно влияет на интегральное значение). Из вашего кода snipet значение 10**4
представляется хорошим выбором, однако значение 10**2
также дает точную оценку интегралу.
Другой подход, позволяющий избежать числовых проблем точности, заключается в использовании модуля, который выполняет вычисления в произвольной точной арифметике, такой как mpmath
. Например, для x=10**5
, mpmath
оценивает exp(-x)
следующим образом (с использованием нативного mpmath
экспоненциальной функции)
import mpmath as mp
print(mp.exp(-10**5))
3.56294956530937e-43430
Обратите внимание, как мала эта величина. При стандартной аппаратной числовой точности (используется numpy
) это значение становится 0
.
mpmath
предлагает функцию интегрирования (mp.quad
), которая может обеспечить точную оценку интеграла при любых значениях верхней интегральной границы.
import mpmath as mp
print(mp.quad(lambda x : .5 * mp.exp(-.5 * x), [0, mp.inf]))
print(mp.quad(lambda x : .5 * mp.exp(-.5 * x), [0, 10**13]))
print(mp.quad(lambda x : .5 * mp.exp(-.5 * x), [0, 10**8]))
print(mp.quad(lambda x : .5 * mp.exp(-.5 * x), [0, 10**5]))
1.0
0.999999650469474
0.999999999996516
0.999999999999997
Мы также можем получить еще более точные оценки за счет увеличения точности, скажем, 50
десятичной точки (от 15
, которая является стандартной точности)
mp.mp.dps = 50;
print(mp.quad(lambda x : .5 * mp.exp(-.5 * x), [0, mp.inf]))
print(mp.quad(lambda x : .5 * mp.exp(-.5 * x), [0, 10**13]))
print(mp.quad(lambda x : .5 * mp.exp(-.5 * x), [0, 10**8]))
print(mp.quad(lambda x : .5 * mp.exp(-.5 * x), [0, 10**5]))
1.0
0.99999999999999999999999999999999999999999829880262
0.99999999999999999999999999999999999999999999997463
0.99999999999999999999999999999999999999999999999998
В общем, стоимость получения этой точности - это увеличенное время вычисления.
P.S .: Само собой разумеется, что если вы в состоянии оценить ваш интеграл аналитически в первую очередь (например, с помощью Sympy
), вы можете забыть все вышеперечисленное.
mpmath не является непогрешимым или: 'mp.quad (лямбда х: 0,5 * mp.exp (- .5 * x), [0, 10 ** 20]) '->' 2.20502636520112e-56'. Дело в том, что численное интегрирование функций невозможно без каких-либо условий «гладкости» - функция не должна иметь слишком резких «всплесков» в интервале интегрирования. Когда интервал интеграции очень велик, функция 'exp (-x/2)' очень «колючая», что вызывает проблемы. –
@pv. В самом деле, спасибо за комментарий. Однако, если вы повышаете точность, такой проблемы нет. Например, попробуйте 'mp.mp.dps = 100' перед вызовом' mp.quad' – Stelios
. Повышение точности просто подталкивает верхнюю границу вверх, попробуйте '10 ** 120'. Это также увеличивает стоимость вычислений, которая в этом случае не нужна. Проблема заключается не в том, что значения функций настолько малы, что они ниже диапазона с плавающей запятой, но тот факт, что функция при масштабировании до интервала интеграции является очень колючей, что вводит в заблуждение оценку ошибки алгоритма интеграции. –