近似计算正弦和余弦值的程序问题
我正在尝试写一个程序,这个程序可以接受一个角度(以度为单位),然后根据用户选择的项数来近似计算这个角度的正弦值(sin)和余弦值(cos)。如果你不知道怎么计算正弦和余弦,可以参考这个链接:如何计算。说到这里,这是我目前的代码:
import math
def main():
print()
print("Program to approximate sin and cos.")
print("You will be asked to enter an angle and \na number of terms.")
print("Written by ME")
print()
sinx = 0
cosx = 0
x = int(input("Enter an angle (in degrees): "))
terms = int(input("Enter the number of terms to use: "))
print()
for i in range(1, terms+1):
sinx = sinx + getSin(i, x)
cosx = cosx + getCos(i, x)
print(cosx, sinx)
def getSin(i, x):
if i == 1:
return x
else:
num, denom = calcSinFact(i, x)
sin = num/denom
return sin
def getCos(i, x):
if i == 1:
return 1
else:
num, denom = calcCosFact(i, x)
cos = num/denom
return cos
def calcSinFact(i, x):
if i % 2 == 1:
sign = -1
if i % 2 == 0:
sign = +1
denom = math.factorial(i*2-1)
num = sign * (x**(i*2-1))
return num, denom
def calcCosFact(i, x):
if i % 2 == 1:
sign = -1
if i % 2 == 0:
sign = +1
denom = math.factorial(i*2)
num = sign * (x**(i*2))
return num, denom
程序可以运行,但如果我使用上面图片中的例子,我得到的余弦值是 -162527117141.85715,正弦值是 -881660636823.117。显然,这个结果有问题。在上面的图片中,正确的答案应该是余弦值 0.50000000433433 和正弦值 0.866025445100。我猜可能是我在第一个循环中加值的方式出了问题,但我也可能错了。任何帮助都非常感谢!
4 个回答
这里有一个改进版:
from math import radians
import sys
# version compatibility shim
if sys.hexversion < 0x3000000:
# Python 2.x
inp = raw_input
rng = xrange
else:
# Python 3.x
inp = input
rng = range
def type_getter(type):
def fn(prompt):
while True:
try:
return type(inp(prompt))
except ValueError:
pass
return fn
get_float = type_getter(float)
get_int = type_getter(int)
def calc_sin(theta, terms):
# term 0
num = theta
denom = 1
approx = num / denom
# following terms
for n in rng(1, terms):
num *= -theta * theta
denom *= (2*n) * (2*n + 1)
# running sum
approx += num / denom
return approx
def calc_cos(theta, terms):
# term 0
num = 1.
denom = 1
approx = num / denom
# following terms
for n in rng(1, terms):
num *= -theta * theta
denom *= (2*n - 1) * (2*n)
# running sum
approx += num / denom
return approx
def main():
print(
"\nProgram to approximate sin and cos."
"\nYou will be asked to enter an angle and"
"\na number of terms."
)
theta = get_float("Enter an angle (in degrees): ")
terms = get_int ("Number of terms to use: ")
print("sin({}) = {}".format(theta, calc_sin(radians(theta), terms)))
print("cos({}) = {}".format(theta, calc_cos(radians(theta), terms)))
if __name__=="__main__":
main()
需要注意的是,由于麦克劳林级数是围绕x=0展开的,离0越近的theta值收敛得越快:比如说,calc_sin(radians(-90), 5)
的结果是-1.00000354258,而calc_sin(radians(270), 5)
的结果是-0.444365928238(这个结果离正确值-1.0大约远了157,000倍)。
我们可以通过使用和结合阶乘和整数幂的递归定义,完全避免所有的幂运算和阶乘运算。进一步的优化是同时计算余弦和正弦的值,这样幂运算只需要计算一次。
PI = 3.1415926535897932384;
RadInDeg=PI/180;
def getCosSin(x, n):
mxx = -x*x;
term = 1;
k = 2;
cossum = 1;
sinsum = 1;
for i in range(n):
term *= mxx
term /= k; k+=1
cossum += term
term /= k; k+=1
sinsum += term
return cossum, x*sinsum
def main():
print "\nProgram to approximate sin and cos."
print "You will be asked to enter an angle and \na number of terms."
x = int(input("Enter an angle (in degrees): "))
terms = int(input("Enter the number of terms to use: "))
print
x = x*RadInDeg;
cosx, sinx = getCosSin(x,terms)
print cosx, sinx
if __name__=="__main__":
main()
首先,有几点需要注意。最好在上一个打印的结尾或者下一个打印的开头加上 \n
,而不是用空的 print()
。使用调试工具是很有帮助的,可以用 logging
模块,或者直接用 print
来找出问题,通过比较预期的值和实际返回的值。
下面是我用的一个有效的代码:
import math
def main():
print()
print("Program to approximate sin and cos.")
print("You will be asked to enter an angle and \na number of terms.")
print("Written by ME")
print()
sinx = 0
cosx = 0
x = int(input("Enter an angle (in degrees): "))
terms = int(input("Enter the number of terms to use: "))
print()
x = x / 180.0 * math.pi; # added
for i in range(1, terms+1):
sinx = sinx + getSin(i, x)
cosx = cosx + getCos(i, x)
print("Cos:{0}, Sinus:{1}".format(cosx,sinx)); # changed
def getSin(i, x):
if i == 1:
return x
else:
num, denom = calcSinFact(i, x)
sin = float(num)/denom # changed
return sin
def getCos(i, x):
if i == 1:
return 1
else:
num, denom = calcCosFact(i, x)
cos = float(num)/denom # changed
return cos
def calcSinFact(i, x):
if i % 2 == 1:
sign = +1 # changed
if i % 2 == 0:
sign = -1 # changed
denom = math.factorial(i*2-1)
num = sign * (x**(i*2-1))
return num, denom
def calcCosFact(i, x):
if i % 2 == 1:
sign = +1 # changed
if i % 2 == 0:
sign = -1 # changed
denom = math.factorial(i*2-2) # changed
num = sign * (x**(i*2-2)) # changed
return num, denom
我做了哪些修改呢?(希望我没有忘记什么)
- 你的
sign
变量是错的,正好相反。所以我在条件中改了符号。 - 这可能不是必须的,但我在用
num
除以denom
时加了从整数到浮点数的转换。 - 根据 近似的定义,输入的
x
是以弧度为单位的。所以我加了从度数到弧度的转换。x = x / 180.0 * math.pi;
- 你在
calcCosFact
中的索引是错的,总是多了2。(比如说,应该是2的地方你写成了4,应该是6的地方你写成了8……)
我得到了这个结果: 输入一个角度(以度为单位):180 输入要使用的项数:5 余弦:-0.976022212624,正弦:0.00692527070751
现在应该是正确的了。我还可以推荐 WolframAlpha,当你需要快速做一些数学计算时可以用它。
这里有几个问题,正如Russell Borogove的评论所指出的。
第一个问题是你使用的公式
(可以查看维基百科)要求x的单位是弧度,而不是度数。绕一圈是360度,或者说是2*pi,所以你可以通过将度数乘以pi/180来转换为弧度,下面的Python代码展示了如何错误地和正确地计算90度的正弦值。
>>> math.sin(90)
0.8939966636005579
>>> math.sin(90*math.pi/180)
1.0
第二个问题是代码的其他部分。正如评论中提到的,有一些bug,找到它们的最好方法是使用一些策略性的print
语句。不过,你可以用更少的代码行来写这个程序,简单的程序通常会有更少的bug,而且如果有问题也更容易调试。
由于这是一个作业,我不会直接帮你做,但有一个相关的例子是sinh(x)的级数。
(同样来自维基百科)
你可以使用Python的列表推导式一次性生成这些项。然后可以用print
输出这个列表,并用sum
求和得到结果,像下面的程序一样。
x = 90 * math.pi / 180 # 90 degrees
n = 5
terms = [x**(2*i+1)/math.factorial(2*i+1) for i in range(n)]
print terms
sinh = sum(terms)
print sinh, math.sinh(x)
这个程序的输出是
[1.5707963267948966, 0.6459640975062462, 0.07969262624616703, 0.004681754135318687, 0.00016044118478735975]
2.30129524587 2.30129890231
我直接根据数学公式生成了Python的列表推导式代码,方便的是在左侧用“Sigma”符号表示了求和。你也可以用类似的方法生成sin和cos。你需要的一个关键要素是级数中每个点的符号。数学公式告诉你需要(-1)n。在Python中对应的写法是(-1)**n
,可以放到列表推导式代码的合适位置。