我正在尝试编写模拟math.sin
和math.tan
的函数,但不使用math
库,而是使用系列扩展执行计算。
公式来自数学SE,How would you calculate the Tangent without a calculator?:
sin(x)= x - x ^ 3/3! + x ^ 5/5! -...
tan(x)= sin(x)/√(1-sin(x)^ 2)
这是我的尝试,但我无法弄清楚如何执行+
/ -
/ +
/ ...部分扩展{{1 }}:
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
条款,但这看起来很混乱。还有更好的方法吗?
注意:这个问题是昨天由@Lana发布的now deleted question的装饰版本。
答案 0 :(得分:5)
您可以通过使用前一个计算下一个和的项来避免重新计算每个步骤的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
答案 1 :(得分:1)
你很亲密。以下是使用else
和sum
进行系列扩展的一种方式。
enumerate
通过获取iterable的每个值并附加索引来工作,即第一个项为0,第二个项为1,等等。然后我们只需要测试索引是偶数还是奇数使用ternary statement。
此外,您可以使用enumerate
代替列出展开所需的奇数。
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))
功能的符号仅适用于第1和第4象限。这与您提供的公式一致。您可以对输入执行简单的转换以解决此问题。tan
来提高准确性。