Какой самый быстрый, самый эффективный и питонный способ выполнить математическую сигма сумму? - PullRequest
3 голосов
/ 07 мая 2019

Допустим, я хочу выполнить математическое суммирование, скажем, формула Мадхавы – Лейбница для π , в Python:

generated by latex.codecogs.com/gif.latex?%5Cfrac%7B%5Cpi%7D%7B4%7D%5Capprox%20%5Csum_%7Bk%3D0%7D%5E%7Bn%7D%20%5Cfrac%7B%28-1%29%5Ek%7D%7B2k+1%7D

Внутри функциипод названием Leibniz_pi (), я мог бы создать цикл для вычисления частичной суммы n th , такой как:

def Leibniz_pi(n):
    nth_partial_sum = 0       #initialize the variable
    for i in range(n+1):
        nth_partial_sum += ((-1)**i)/(2*i + 1)
    return nth_partial_sum

Я предполагаю, что было бы быстрее использовать что-то вроде xrange () вместо диапазона ().Будет ли еще быстрее использовать numpy и встроенный метод numpy.sum ()?Как бы выглядел такой пример?

Ответы [ 3 ]

5 голосов
/ 07 мая 2019

Я полагаю, что большинство людей определят самое быстрое решение @zero, используя только numpy как наиболее питоническое, но оно, безусловно, не самое быстрое.С некоторыми дополнительными оптимизациями вы можете превзойти и без того быструю реализацию numpy с коэффициентом 50.

Использование только Numpy (@zero)

import numpy as np
import numexpr as ne
import numba as nb

def Leibniz_point(n):
    val = (-1)**n / (2*n + 1)
    return val

%timeit Leibniz_point(np.arange(1000)).sum()
33.8 µs ± 203 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Makeиспользование числаxpr

n=np.arange(1000)
%timeit ne.evaluate("sum((-1)**n / (2*n + 1))")
21 µs ± 354 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Скомпилируйте вашу функцию, используя Numba

# with error_model="numpy", turns off division-by-zero checks 
@nb.njit(error_model="numpy",cache=True)
def Leibniz_pi(n):
  nth_partial_sum = 0.       #initialize the variable as float64
  for i in range(n+1):
      nth_partial_sum += ((-1)**i)/(2*i + 1)
  return nth_partial_sum

%timeit Leibniz_pi(999)
6.48 µs ± 38.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

Редактировать, оптимизируя дорогостоящие (-1) **n

import numba as nb
import numpy as np

#replacement for the much more costly (-1)**n
@nb.njit()
def sgn(i):
    if i%2>0:
        return -1.
    else:
        return 1.

# with error_model="numpy", turns off the division-by-zero checks
#
# fastmath=True makes SIMD-vectorization in this case possible
# floating point math is in general not commutative
# e.g. calculating four times sgn(i)/(2*i + 1) at once and then the sum 
# is not exactly the same as doing this sequentially, therefore you have to
# explicitly allow the compiler to make the optimizations

@nb.njit(fastmath=True,error_model="numpy",cache=True)
def Leibniz_pi(n):
    nth_partial_sum = 0.       #initialize the variable
    for i in range(n+1):
        nth_partial_sum += sgn(i)/(2*i + 1)
    return nth_partial_sum

%timeit Leibniz_pi(999)
777 ns ± 5.36 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
4 голосов
/ 07 мая 2019

3 предложения (с вычислением скорости):

определить Лейбница балл не кумулятивная сумма:

def Leibniz_point(n):
    val = (-1)**n / (2*n + 1)
    return val

1) суммировать понимание списка

%timeit sum([Leibniz_point(n) for n in range(100)])
58.8 µs ± 825 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

%timeit sum([Leibniz_point(n) for n in range(1000)])
667 µs ± 3.41 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

2) стандарт для петли

%%timeit
sum = 0
for n in range(100):
    sum += Leibniz_point(n)
61.8 µs ± 4.45 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

%%timeit
sum = 0
for n in range(1000):
    sum += Leibniz_point(n)
    729 µs ± 43.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

3) использовать массив numpy (рекомендуется)

%timeit Leibniz_point(np.arange(100)).sum()
11.5 µs ± 866 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%timeit Leibniz_point(np.arange(1000)).sum()
61.8 µs ± 3.69 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
2 голосов
/ 07 мая 2019

Как правило, для операций, включающих коллекции из более чем нескольких элементов, numpy будет быстрее. Простая реализация numpy может выглядеть примерно так:

def leibniz(n):
    a = np.arange(n + 1)
    return (((-1.0) ** a) / (2 * a + 1)).sum()

Обратите внимание, что вы должны указать, что числителем является float с 1.0 на Python 2. На Python 3 с 1 все будет в порядке.

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