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.

433 lines
18 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
from scipy.optimize import brentq, differential_evolution
################################################################################################################
##################################################### 修改 #####################################################
def get_domain(func_expr, var):
"""
确定函数的定义域(针对常见问题如 ln(x), 1/x 等)
:param func_expr: sympy 函数表达式
:param var: 自变量
:return: 有效定义域 (x_min, x_max)
"""
try:
if any(f in str(func_expr) for f in ['ln', 'log', '/']):
x_min = 1e-6 # 避免 x=0
else:
x_min = -10
x_max = 10
try:
func_expr.subs(var, x_min)
except (ValueError, ZeroDivisionError):
x_min = 1e-6
try:
func_expr.subs(var, x_max)
except (ValueError, ZeroDivisionError):
x_max = 10
return x_min, x_max
except Exception:
return 1e-6, 10
def extreme_value(function):
"""
计算函数的极值点和极值
:param function: 函数表达式
:return: 极值点与极值(字典形式)
"""
function = convert_formula(function)
independent_variable, function = split_function(function)
var = symbols(independent_variable)
func_expr = sympify(function)
derivative = diff(func_expr, var)
# 获取定义域
x_min, x_max = get_domain(func_expr, var)
# 转换为数值函数
try:
deriv_num = lambdify(var, derivative, 'numpy')
func_num = lambdify(var, func_expr, 'numpy')
except Exception as e:
print(f"导数或函数转换失败:{e}")
return {}
# 区间扫描
step = 0.1 # 减小步长以提高精度
x_vals = np.arange(x_min, x_max + step, step)
f_scan = []
for xi in x_vals:
try:
f_scan.append(deriv_num(xi))
except (ValueError, ZeroDivisionError, OverflowError):
f_scan.append(np.nan)
# 检测导数符号变化区间
candidate_intervals = []
for i in range(len(f_scan)-1):
if np.isnan(f_scan[i]) or np.isnan(f_scan[i+1]):
continue
if f_scan[i] * f_scan[i+1] <= 0:
candidate_intervals.append((x_vals[i], x_vals[i+1]))
# 使用布伦特法寻找零点
extreme_points = []
for a, b in candidate_intervals:
try:
root = brentq(deriv_num, a, b, xtol=1e-8)
root_rounded = round(float(root), 6)
if root_rounded not in [round(r, 6) for r in extreme_points]:
extreme_points.append(root_rounded)
except (ValueError, ZeroDivisionError, OverflowError):
continue
# 全局搜索补充可能的漏掉零点
def objective(x):
try:
return abs(deriv_num(x[0]))
except (ValueError, ZeroDivisionError, OverflowError):
return np.inf
try:
result = differential_evolution(objective, bounds=[(x_min, x_max)], tol=1e-6)
if result.success and result.fun < 1e-6:
root_rounded = round(float(result.x[0]), 6)
if root_rounded not in [round(r, 6) for r in extreme_points]:
extreme_points.append(root_rounded)
except Exception:
pass
# 计算极值
extreme_values = []
for point in extreme_points:
try:
value = func_num(point)
extreme_values.append(float(value))
except (ValueError, ZeroDivisionError):
continue
extreme_dict = {point: value for point, value, in zip(extreme_points, extreme_values)}
return extreme_dict
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)
# 获取定义域
x_min, x_max = get_domain(func_expr, var)
# 转换为数值函数
try:
deriv_num = lambdify(var, derivative, 'numpy')
except Exception as e:
print(f"导数转换失败:{e}")
return [], []
# 区间扫描
step = 0.1
x_vals = np.arange(x_min, x_max + step, step)
f_scan = []
for xi in x_vals:
try:
f_scan.append(deriv_num(xi))
except (ValueError, ZeroDivisionError, OverflowError):
f_scan.append(np.nan)
# 检测导数符号变化区间
candidate_intervals = []
for i in range(len(f_scan)-1):
if np.isnan(f_scan[i]) or np.isnan(f_scan[i+1]):
continue
if f_scan[i] * f_scan[i+1] <= 0:
candidate_intervals.append((x_vals[i], x_vals[i+1]))
# 使用布伦特法寻找零点
change_points = []
for a, b in candidate_intervals:
try:
root = brentq(deriv_num, a, b, xtol=1e-8)
change_points.append(float(round(root, 3)))
except (ValueError, ZeroDivisionError, OverflowError):
continue
# 生成单调区间
change_points = sorted(change_points)
up_intervals, down_intervals = [], []
try:
current_sign = np.sign(deriv_num(x_min + 1e-6))
except (ValueError, ZeroDivisionError):
current_sign = 0
start = float(round(x_min, 3))
for point in change_points:
if current_sign > 0:
up_intervals.append((start, point))
elif current_sign < 0:
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))
elif current_sign < 0:
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) # 二阶导数
# 获取定义域
x_min, x_max = get_domain(func_expr, var)
# 转换为数值函数
try:
deriv_num = lambdify(var, derivative, 'numpy')
except Exception as e:
print(f"二阶导数转换失败:{e}")
return [], []
# 区间扫描
step = 0.1
x_vals = np.arange(x_min, x_max + step, step)
f_scan = []
for xi in x_vals:
try:
f_scan.append(deriv_num(xi))
except (ValueError, ZeroDivisionError, OverflowError):
f_scan.append(np.nan)
# 检测二阶导数符号变化区间
candidate_intervals = []
for i in range(len(f_scan)-1):
if np.isnan(f_scan[i]) or np.isnan(f_scan[i+1]):
continue
if f_scan[i] * f_scan[i+1] <= 0:
candidate_intervals.append((x_vals[i], x_vals[i+1]))
# 使用布伦特法寻找零点
change_points = []
for a, b in candidate_intervals:
try:
root = brentq(deriv_num, a, b, xtol=1e-8)
change_points.append(float(round(root, 3)))
except (ValueError, ZeroDivisionError, OverflowError):
continue
# 生成凹凸区间
ao_intervals, tu_intervals = [], []
try:
current_sign = np.sign(deriv_num(x_min + 1e-6))
except (ValueError, ZeroDivisionError):
current_sign = 0
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 max_min(function):
"""
计算函数的极大值和极小值,若存在极值点,则返回极大值和极小值;
否则返回定义域边界 [x_min, x_max] 内的函数值作为最大最小值的估计。
:param function: 函数表达式
:return: 极大值和极小值(或边界值的最大最小值)
"""
extreme_dict = extreme_value(function)
function = convert_formula(function)
independent_variable, function = split_function(function)
var = symbols(independent_variable)
func_expr = sympify(function)
func_num = lambdify(var, func_expr, 'numpy')
# 获取定义域
x_min, x_max = get_domain(func_expr, var)
# 如果没有极值点,检查边界值
if not extreme_dict:
try:
boundary_values = [float(func_num(x_min)), float(func_num(x_max))]
max_value = max(boundary_values)
min_value = min(boundary_values)
print(f"未找到极值点,使用边界值 x={x_min} 和 x={x_max} 估计最大最小值")
except (ValueError, ZeroDivisionError):
print("无法计算边界值,可能由于定义域问题")
return None, None
else:
max_value = max(extreme_dict.values())
min_value = min(extreme_dict.values())
# 打印极值点信息
max_points = [p for p, v in extreme_dict.items() if v == max_value]
min_points = [p for p, v in extreme_dict.items() if v == min_value]
print(f"极大值点:{[f'x={p:.6f}' for p in max_points]}, 极大值:{max_value:.4f}")
print(f"极小值点:{[f'x={p:.6f}' for p in min_points]}, 极小值:{min_value:.4f}")
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 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)
if max_value is not None and min_value is not None:
print(f"最大值:{max_value:.4f},最小值:{min_value:.4f}")
else:
print("无法计算最大最小值,请检查函数定义域或表达式")
############################################## 修改 ##################################################
######################################################################################################
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()