近似计算正弦和余弦值的程序问题

0 投票
4 回答
2646 浏览
提问于 2025-04-18 00:45

我正在尝试写一个程序,这个程序可以接受一个角度(以度为单位),然后根据用户选择的项数来近似计算这个角度的正弦值(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 个回答

0

这里有一个改进版:

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倍)。

0

我们可以通过使用和结合阶乘和整数幂的递归定义,完全避免所有的幂运算和阶乘运算。进一步的优化是同时计算余弦和正弦的值,这样幂运算只需要计算一次。

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()
0

首先,有几点需要注意。最好在上一个打印的结尾或者下一个打印的开头加上 \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

我做了哪些修改呢?(希望我没有忘记什么)

  1. 你的 sign 变量是错的,正好相反。所以我在条件中改了符号。
  2. 这可能不是必须的,但我在用 num 除以 denom 时加了从整数到浮点数的转换。
  3. 根据 近似的定义,输入的 x 是以弧度为单位的。所以我加了从度数到弧度的转换。 x = x / 180.0 * math.pi;
  4. 你在 calcCosFact 中的索引是错的,总是多了2。(比如说,应该是2的地方你写成了4,应该是6的地方你写成了8……)

我得到了这个结果: 输入一个角度(以度为单位):180 输入要使用的项数:5 余弦:-0.976022212624,正弦:0.00692527070751

现在应该是正确的了。我还可以推荐 WolframAlpha,当你需要快速做一些数学计算时可以用它。

2

这里有几个问题,正如Russell Borogove的评论所指出的。

第一个问题是你使用的公式

enter image description here

(可以查看维基百科)要求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)的级数。

enter image description here

(同样来自维基百科)

你可以使用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,可以放到列表推导式代码的合适位置。

撰写回答