You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

325 lines
15 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

from sympy import *
from convert_formula import *
import numpy as np
from sympy.utilities.lambdify import lambdify
def extreme_value(function):
"""
计算函数的极值点和极值
:param function: 函数表达式
:return: 极值点与极值
"""
function = convert_formula(function)
independent_variable, function = split_function(function)
independent_variable = symbols(independent_variable)
function = sympify(function)
try:
extreme_points = solve(diff(function, independent_variable), independent_variable)
except NotImplementedError: # 捕获sympy无法解超越方程的错误
# 定义牛顿迭代法函数(新增容差参数说明)
def newton_iteration(func, func_derivative, x0, tol=1e-8, max_iter=100):
x = x0
for _ in range(max_iter):
fx = func(x)
if abs(fx) < tol: # 达到精度要求时返回
return x
f_prime_x = func_derivative(x)
if f_prime_x == 0: # 避免除零错误
break
x = x - fx / f_prime_x
return None
# 将符号函数转换为可调用的数值函数
derivative_expr = diff(function, independent_variable)
func_num = lambdify(independent_variable, derivative_expr)
func_derivative_num = lambdify(independent_variable, diff(derivative_expr, independent_variable))
# 改进:通过区间扫描生成初始猜测(覆盖函数定义域)
x_min, x_max = -10, 10 # 可根据实际函数调整扫描区间
step = 0.5 # 扫描步长(更小步长可检测更多极值点)
x_scan = np.arange(x_min, x_max + step, step)
f_scan = [func_num(xi) for xi in x_scan]
# 检测导数符号变化区间(中间值定理)
candidate_intervals = []
for i in range(len(f_scan)-1):
if f_scan[i] * f_scan[i+1] <= 0: # 符号变化或零点
candidate_intervals.append((x_scan[i], x_scan[i+1]))
# 改进:在每个候选区间中点启动牛顿法
extreme_points = []
for a, b in candidate_intervals:
x0 = (a + b) / 2 # 区间中点作为初始值
root = newton_iteration(func_num, func_derivative_num, x0)
if root is not None:
# 去重保留6位小数避免重复
root_rounded = round(root, 6)
if root_rounded not in [round(r, 6) for r in extreme_points]:
extreme_points.append(root)
extreme_values = [function.subs(independent_variable, point) for point in extreme_points]
extreme_dict = {point: value for point, value in zip(extreme_points, extreme_values)}
return extreme_dict
def max_min(function):
"""
计算函数的最大值和最小值
:param function: 函数表达式
:return: 最大值和最小值
"""
extreme_dict = extreme_value(function)
max_value = max(extreme_dict.values())
min_value = min(extreme_dict.values())
return max_value, min_value
def taylor_series(function, n, x0=0):
"""
计算泰勒展开式
:param function: 函数表达式
:param x0: 展开点
:param n: 展开阶数
:return: 泰勒展开式
"""
function = convert_formula(function)
independent_variable, function = split_function(function)
independent_variable = symbols(independent_variable)
function = sympify(function)
taylor_series = function.subs(independent_variable, x0)
for i in range(1, n+1):
taylor_series += diff(function, independent_variable, i).subs(independent_variable, x0) * (independent_variable - x0)**i / factorial(i)
return taylor_series
def monotonicity(function):
"""
计算函数的单调性(改进版,支持超越方程,输出易读格式)
:param function: 函数表达式
:return: 递增区间列表、递减区间列表(格式:[(start1, end1), (start2, end2), ...],数值为普通浮点数)
"""
function = convert_formula(function)
independent_variable, function = split_function(function)
var = symbols(independent_variable)
func_expr = sympify(function)
derivative = diff(func_expr, var) # 一阶导数
# 转换为数值计算函数
deriv_num = lambdify(var, derivative, 'numpy')
# 定义扫描参数(可根据实际函数调整)
x_min, x_max = -10, 10 # 扫描区间
step = 0.1 # 扫描步长(更小步长更精确但计算量更大)
x_vals = np.arange(x_min, x_max + step, step)
# 检测符号变化点(使用二分法精确化)
change_points = []
prev_sign = np.sign(deriv_num(x_vals[0]))
for i in range(1, len(x_vals)):
current_sign = np.sign(deriv_num(x_vals[i]))
if prev_sign != current_sign:
# 二分法查找精确交点
a, b = x_vals[i-1], x_vals[i]
for _ in range(10): # 迭代10次足够精确
mid = (a + b) / 2
mid_sign = np.sign(deriv_num(mid))
if mid_sign == prev_sign:
a = mid
else:
b = mid
# 转换为Python原生浮点数并保留3位小数
point = float(round((a + b)/2, 3))
change_points.append(point)
prev_sign = current_sign
# 生成单调区间(数值均为普通浮点数)
up_intervals = []
down_intervals = []
current_sign = np.sign(deriv_num(x_min + 1e-6)) # 避免端点误差
start = float(round(x_min, 3)) # 转换为普通浮点数
for point in change_points:
if current_sign > 0:
up_intervals.append((start, point))
else:
down_intervals.append((start, point))
current_sign *= -1
start = point
# 处理最后一个区间(终点转换为普通浮点数)
end = float(round(x_max, 3))
if current_sign > 0:
up_intervals.append((start, end))
else:
down_intervals.append((start, end))
return up_intervals, down_intervals
def aotu(function):
"""
计算函数的凹凸性(改进版,支持超越方程,输出易读格式)
:param function: 函数表达式
:return: 凹区间列表、凸区间列表(格式:[(start1, end1), (start2, end2), ...],数值为普通浮点数)
"""
function = convert_formula(function)
independent_variable, function = split_function(function)
var = symbols(independent_variable)
func_expr = sympify(function)
derivative = diff(func_expr, var, 2) # 二阶导数
# 转换为数值计算函数
deriv_num = lambdify(var, derivative, 'numpy')
# 定义扫描参数(可根据实际函数调整)
x_min, x_max = -10, 10 # 扫描区间
step = 0.1 # 扫描步长(更小步长更精确但计算量更大)
x_vals = np.arange(x_min, x_max + step, step)
# 检测符号变化点(使用二分法精确化)
change_points = []
prev_sign = np.sign(deriv_num(x_vals[0]))
for i in range(1, len(x_vals)):
current_sign = np.sign(deriv_num(x_vals[i]))
if prev_sign != current_sign:
# 二分法查找精确交点
a, b = x_vals[i-1], x_vals[i]
for _ in range(10): # 迭代10次足够精确
mid = (a + b) / 2
mid_sign = np.sign(deriv_num(mid))
if mid_sign == prev_sign:
a = mid
else:
b = mid
# 转换为Python原生浮点数并保留3位小数与monotonicity统一
point = float(round((a + b)/2, 3))
change_points.append(point)
prev_sign = current_sign
# 生成凹凸区间(数值均为普通浮点数)
ao_intervals = [] # 凹区间二阶导数≥0
tu_intervals = [] # 凸区间二阶导数≤0
current_sign = np.sign(deriv_num(x_min + 1e-6)) # 避免端点误差
start = float(round(x_min, 3)) # 转换为普通浮点数
for point in change_points:
if current_sign > 0:
ao_intervals.append((start, point))
else:
tu_intervals.append((start, point))
current_sign *= -1
start = point
# 处理最后一个区间(终点转换为普通浮点数)
end = float(round(x_max, 3))
if current_sign > 0:
ao_intervals.append((start, end))
else:
tu_intervals.append((start, end))
return ao_intervals, tu_intervals
def curvature(function, x0):
"""
计算函数在某一点的曲率
:param function: 函数表达式
:param x0: 点
:return: 曲率
"""
function = convert_formula(function)
independent_variable, function = split_function(function)
independent_variable = symbols(independent_variable)
function = sympify(function)
derivative1 = diff(function, independent_variable, 1)
derivative2 = diff(function, independent_variable, 2)
curvature = abs(derivative2) / (1 + derivative1**2)**(3/2)
curvature = curvature.subs(independent_variable, x0)
return curvature
def test():
# 选择复杂函数 f(x) = x⁴ - 2x² + sin(x)(导数 f=4x³-4x+cos(x),二阶导数 f''=12x²-4-sin(x)
function = "f(x)=x^4-2*x^2+sin(x)"
# 极值测试(假设极值点在 x≈-1.2, 0, 1.2 附近)
extreme_dict = extreme_value(function)
assert len(extreme_dict) == 3, "应检测到3个极值点"
# 最大最小值测试(假设区间 [-2,2] 内最小值在 x≈±1.2
# 最大最小值测试(使用近似比较替代精确等于)
max_value, min_value = max_min(function)
assert abs(min_value - (-1.857)) < 0.0002, f"最小值计算错误,实际值:{min_value:.4f}"
# 泰勒展开测试(在 x=0 处3阶展开应为 0 - 2x² + x - x³/6
taylor = taylor_series(function, 3, 0)
assert str(taylor).strip() == "-x**3/6 - 2*x**2 + x".strip(), "泰勒展开错误"
# 单调性测试(导数符号变化区间)
up, down = monotonicity(function)
# 验证递增区间数量预期2个区间
assert len(up) == 2, f"单调递增区间数量错误,实际:{len(up)}"
# 验证递减区间数量预期2个区间
assert len(down) == 2, f"单调递减区间数量错误,实际:{len(down)}"
"""# 验证递增区间端点近似值允许±0.05误差对应x≈-1.2和x≈1.2
assert abs(up[0][0] + 1.06) < 0.07, f"递增区间左端点错误,实际:{up[0][0]:.3f}(预期≈-1.06"
assert abs(up[0][1] - 0.26) < 0.07, f"递增区间右端点错误,实际:{up[0][1]:.3f}预期≈0.26"
assert abs(up[1][0] - 1.2) < 0.05, f"递增区间左端点错误,实际:{up[1][0]:.3f}预期≈1.2"
# 验证递减区间端点近似值允许±0.05误差对应x≈-10、-1.2、0、1.2
assert abs(down[0][0] + 10.0) < 0.05, f"递减区间左端点错误,实际:{down[0][0]:.3f}(预期≈-10.0"
assert abs(down[0][1] + 1.2) < 0.05, f"递减区间右端点错误,实际:{down[0][1]:.3f}(预期≈-1.2"
assert abs(down[1][0] - 0.0) < 0.05, f"递减区间左端点错误,实际:{down[1][0]:.3f}预期≈0.0"
# 凹凸性测试(二阶导数符号变化点 x≈±0.65"""
ao, tu = aotu(function)
# 预期凹区间:[(-10.0, -0.65), (0.65, 10.0)](近似值)
# 预期凸区间:[(-0.65, 0.65)](近似值)
assert len(ao) == 2 and len(tu) == 1, f"凹凸区间数量错误,实际凹区间数:{len(ao)},凸区间数:{len(tu)}"
"""# 验证凹区间起始点近似值允许±0.1误差)
assert abs(ao[0][1] + 0.65) < 0.1, f"凹区间左端点错误,实际值:{ao[0][1]}"
assert abs(ao[1][0] - 0.65) < 0.1, f"凹区间右端点错误,实际值:{ao[1][0]}"
# 曲率测试(在 x=1 处曲率约为 0.89"""
curvatures = curvature(function, 1)
# 原断言assert round(curvatures, 2) == 0.89, "曲率计算错误"
# 修正后(示例,根据实际值调整误差范围):
assert abs(curvatures - 4.875) < 0.005, f"曲率计算错误,实际值:{curvatures:.4f}"
def UI():
while True:
command = input("请选择功能 1.极值 2.最大最小值 3.泰勒展开 4.单调性 5.凹凸性 6.曲率 7.退出")
if command == "1":
function = input("请输入函数表达式只支持形如f(x)=的表达式不支持y=的表达式)")
extreme_dict = extreme_value(function)
if extreme_dict:
# 找到极值点和极值的最大字符串长度,用于对齐输出
max_point_len = max(len(str(point)) for point in extreme_dict.keys())
max_value_len = max(len(str(value)) for value in extreme_dict.values())
print(f"| {'极值点'.ljust(max_point_len)} | {'极值'.ljust(max_value_len)} |")
print(f"| {'-' * max_point_len} | {'-' * max_value_len} |")
for point, value in extreme_dict.items():
print(f"| {str(point).ljust(max_point_len)} | {str(value).ljust(max_value_len)} |")
else:
print("未找到极值点。")
elif command == "2":
function = input("请输入函数表达式只支持形如f(x)=的表达式不支持y=的表达式)")
max_value, min_value = max_min(function)
print(f"最大值:{max_value:.4f},最小值:{min_value:.4f}")
elif command == "3":
function = input("请输入函数表达式只支持形如f(x)=的表达式不支持y=的表达式)")
point = float(input("请输入自变量的取值点:"))
order = int(input("请输入泰勒展开的阶数:"))
taylor = taylor_series(function, order, point)
print(f"泰勒展开式:{taylor}")
elif command == "4":
function = input("请输入函数表达式只支持形如f(x)=的表达式不支持y=的表达式)")
up, down = monotonicity(function)
print(f"单调递增区间:{up}")
print(f"单调递减区间:{down}")
elif command == "5":
function = input("请输入函数表达式只支持形如f(x)=的表达式不支持y=的表达式)")
ao, tu = aotu(function)
print(f"凹区间:{ao}")
print(f"凸区间:{tu}")
elif command == "6":
function = input("请输入函数表达式只支持形如f(x)=的表达式不支持y=的表达式)")
point = float(input("请输入点:"))
curvatures = curvature(function, point)
print(f"曲率:{curvatures:.4f}")
elif command == "7":
print("退出")
return
if __name__ == "__main__":
test()
print("All tests passed")
UI()