级数展开的三角函数
Trigonometry functions from series expansion
我正在尝试编写模拟 math.sin
和 math.tan
的函数,但是我没有使用 math
库,而是使用级数展开来执行计算。
公式来自 Mathematics 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)
这是我的尝试,但我不知道如何执行符号翻转 +
/ -
/ +
/ ... [的系列扩展的一部分=17=]:
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昨天发的now deleted question的美化版
你很接近。下面是使用 sum
和 enumerate
进行系列扩展的一种方法。
enumerate
通过获取可迭代的每个值并附加一个索引来工作,即第一项为 0,第二项为 1,等等。然后我们只需要测试索引是否为偶数或奇数并使用 ternary statement.
此外,您可以使用 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
函数的符号只对第一和第四象限是正确的。这与您提供的公式一致。您可以对输入执行一个简单的转换来解决这个问题。
- 您可以通过增加参数
n
来提高准确性。
- 你也可以在没有库的情况下计算阶乘,但我会把它留作练习。
您可以通过使用前一项计算和的下一项来避免在每一步重新计算 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
我正在尝试编写模拟 math.sin
和 math.tan
的函数,但是我没有使用 math
库,而是使用级数展开来执行计算。
公式来自 Mathematics 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)
这是我的尝试,但我不知道如何执行符号翻转 +
/ -
/ +
/ ... [的系列扩展的一部分=17=]:
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昨天发的now deleted question的美化版
你很接近。下面是使用 sum
和 enumerate
进行系列扩展的一种方法。
enumerate
通过获取可迭代的每个值并附加一个索引来工作,即第一项为 0,第二项为 1,等等。然后我们只需要测试索引是否为偶数或奇数并使用 ternary statement.
此外,您可以使用 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
函数的符号只对第一和第四象限是正确的。这与您提供的公式一致。您可以对输入执行一个简单的转换来解决这个问题。- 您可以通过增加参数
n
来提高准确性。 - 你也可以在没有库的情况下计算阶乘,但我会把它留作练习。
您可以通过使用前一项计算和的下一项来避免在每一步重新计算 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