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