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