Тригонометрические функции из разложения в ряд - PullRequest
0 голосов
/ 08 мая 2018

Я пытаюсь написать функции, эмулирующие math.sin и math.tan, но вместо использования библиотеки math выполняю вычисления с использованием расширения серии.

Формулы взяты из математики SE, Как бы вы вычислили Касательный без калькулятора? :

sin (x) = x - x ^ 3/3! + х ^ 5/5! −...

tan (x) = sin (x) / √ (1 - sin (x) ^ 2)

Это моя попытка, но я не мог понять, как выполнить переворачивание знака + / - / + / ... часть расширения серии для sin:

from math import factorial

res = 0
for i in [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]:
    res += 1**i/factorial(i)

print(res)  # 1.1752011936438016

Результат неверный, потому что я не применил переключатель + / -. Я мог бы добавить предложение if / else, но это кажется грязным. Есть ли лучший способ?

Примечание : Этот вопрос является украшенной версией удаленного вопроса , который был размещен вчера @ Lana.

Ответы [ 2 ]

0 голосов
/ 08 мая 2018

Вы можете избежать пересчета x ** n и факториала на каждом шаге, вычислив следующий член суммы, используя предыдущий:

def sin2(x, n=20):
    curr =  x
    res = curr 
    for i in range(2, n, 2):
        curr *= - x**2/(i*(i+1))
        res += curr
    return res

По сравнению с версией jpp, это примерно вдвое быстрее:

from math import factorial

def sin(x, n=20):
    return sum(x**j/factorial(j)*(1 if i%2==0 else -1)
               for i, j in enumerate(range(1, n, 2)))


%timeit sin(0.7)
# 100000 loops, best of 3: 8.52 µs per loop
%timeit sin2(0.7)
# 100000 loops, best of 3: 4.54 µs per loop

И это может стать немного быстрее, если мы вычислим - x**2 раз и навсегда:

def sin3(x, n=20):
    curr =  x
    res = 0
    minus_x_squared = - x**2
    for i in range(2, n, 2):
        res += curr
        curr *= minus_x_squared/(i*(i+1))
    return res

%timeit sin2(0.7)
# 100000 loops, best of 3: 4.6 µs per loop

%timeit sin3(0.7)
# 100000 loops, best of 3: 3.54 µs per loop
0 голосов
/ 08 мая 2018

Вы рядом. Ниже приведен один из способов использования sum с enumerate для расширения серии.

enumerate работает, беря каждое значение итерируемого и прикрепляя индекс, то есть 0 для первого элемента, 1 для второго элемента и т. Д. Затем нам нужно только проверить, является ли индекс четным или нечетным, и использовать троичное заявление .

Кроме того, вы можете использовать range вместо перечисления нечетных чисел, необходимых в вашем расширении.

from math import factorial

def sin(x, n=20):
    return sum(x**j/factorial(j)*(1 if i%2==0 else -1)
               for i, j in enumerate(range(1, n, 2)))

def tan(x):
    return sin(x) / (1-(sin(x))**2)**0.5

print(tan(1.2))  # 2.572151622126318

Вы можете избежать необходимости использования троичного оператора и enumerate в целом:

def sin(x, n=20):
    return sum((-1)**i * x**(2*i+1) / factorial(2*i+1) for i in range(n))

Если вы напишите первые несколько терминов от руки, эквивалентность станет ясной.

Примечания:

  • Знак функции tan верен только для 1-го и 4-го квадрантов. Это согласуется с формулами, которые вы предоставили. Вы можете выполнить тривиальное преобразование для ввода, чтобы учесть это.
  • Вы можете улучшить точность, увеличив параметр n.
  • Вы также можете вычислить факториал без библиотеки, но я оставлю это как упражнение.
...