from __future__ import annotations , annotations
import matplotlib.pyplot as plt
import numpy as np
# 设置全局字体和图像大小
font_name = 'TencentSans'
plt.rcParams[ 'font.family' ] = font_name
plt.rcParams[ 'font.size' ] = 16
plt.rcParams[ 'axes.unicode_minus' ] = False # 正确显示负号
plt.figure(figsize = (12 , 8))
# =============================================================================
# 1. 常规指数函数 (底数为2)
# =============================================================================
plt.subplot(2 , 2 , 1) # 2行2列的第1个子图
# 生成x值(-2到2之间等间距的100个点)
x = np.linspace(-2 , 2 , 100)
# 计算以2为底的指数函数值
y = 2 ** x
# 绘制函数曲线
plt.plot(x , y , 'b-' , linewidth = 2 , label = r'$y = 2^x$')
# 标记特殊点:(0,1) - 任何底数的指数函数都经过此点
plt.scatter(0 , 1 , color = 'red' , s = 80 , zorder = 5) # 红色散点标记
plt.annotate('(0,1)' , xy = (0 , 1) , xytext = (0.2 , 1.5) ,
arrowprops = dict(facecolor = 'black' , arrowstyle = '->'))
# 设置坐标轴范围,添加标题和网格
plt.axhline(y = 0 , color = 'k' , linewidth = 0.5) # x轴
plt.axvline(x = 0 , color = 'k' , linewidth = 0.5) # y轴
plt.grid(True , linestyle = '--' , alpha = 0.7)
plt.title('常规指数函数 (底数=2)')
plt.legend()
# =============================================================================
# 2. 自然指数函数 (底数为e)
# =============================================================================
plt.subplot(2 , 2 , 2) # 2行2列的第2个子图
# 计算自然指数函数值
y = np.exp(x) # e^x
# 绘制函数曲线
plt.plot(x , y , 'g-' , linewidth = 2 , label = r'$y = e^x$')
# 标记特殊点:(0,1) - 自然指数函数经过此点
plt.scatter(0 , 1 , color = 'red' , s = 80 , zorder = 5)
plt.annotate('(0,1)' , xy = (0 , 1) , xytext = (0.2 , 1.5) ,
arrowprops = dict(facecolor = 'black' , arrowstyle = '->'))
# 设置坐标轴范围,添加标题和网格
plt.axhline(y = 0 , color = 'k' , linewidth = 0.5)
plt.axvline(x = 0 , color = 'k' , linewidth = 0.5)
plt.grid(True , linestyle = '--' , alpha = 0.7)
plt.title('自然指数函数 (底数=e)')
plt.legend()
# =============================================================================
# 3. 常规对数函数 (底数为2)
# =============================================================================
plt.subplot(2 , 2 , 3) # 2行2列的第3个子图
# 生成x值(0.1到4之间等间距的100个点),避免0
x = np.linspace(0.1 , 4 , 100)
# 计算以2为底的对数函数值
y = np.log2(x)
# 绘制函数曲线
plt.plot(x , y , 'm-' , linewidth = 2 , label = r'$y = \log_2(x)$')
# 标记特殊点:(1,0) - 任何底数的对数函数都经过此点
plt.scatter(1 , 0 , color = 'red' , s = 80 , zorder = 5)
plt.annotate('(1,0)' , xy = (1 , 0) , xytext = (1.2 , -1.5) ,
arrowprops = dict(facecolor = 'black' , arrowstyle = '->'))
# 设置坐标轴范围,添加标题和网格
plt.axhline(y = 0 , color = 'k' , linewidth = 0.5)
plt.axvline(x = 0 , color = 'k' , linewidth = 0.5)
plt.grid(True , linestyle = '--' , alpha = 0.7)
plt.title('常规对数函数 (底数=2)')
plt.legend()
# =============================================================================
# 4. 自然对数函数 (底数为e)
# =============================================================================
plt.subplot(2 , 2 , 4) # 2行2列的第4个子图
# 计算自然对数函数值
y = np.log(x) # ln(x)
# 绘制函数曲线
plt.plot(x , y , 'c-' , linewidth = 2 , label = r'$y = \ln(x)$')
# 标记特殊点:(1,0) - 自然对数函数经过此点
plt.scatter(1 , 0 , color = 'red' , s = 80 , zorder = 5)
plt.annotate('(1,0)' , xy = (1 , 0) , xytext = (1.2 , -1.5) ,
arrowprops = dict(facecolor = 'black' , arrowstyle = '->'))
# 设置坐标轴范围,添加标题和网格
plt.axhline(y = 0 , color = 'k' , linewidth = 0.5)
plt.axvline(x = 0 , color = 'k' , linewidth = 0.5)
plt.grid(True , linestyle = '--' , alpha = 0.7)
plt.title('自然对数函数 (底数=e)')
plt.legend()
# =============================================================================
# 显示图像
# =============================================================================
plt.tight_layout(pad = 3.0) # 调整子图间距
plt.show()
import random
import unittest
from sympy import exp , limit , log , oo , symbols
class ExponentialFunctionTestCase(unittest.TestCase):
def test_custom_exponential(self):
# 自定义底数的指数函数
a , x = symbols('a x')
# 表示 a^x
expr = a ** x
# 过定点(0,1)即当 x=0 时 a^x 一定为 1
self.assertEqual(expr.subs({ x:0 , a:random.randint(1 , 100) }) , 1)
def test_multiplication_rule_for_exponents(self):
# 底数不变,指数相加
tmp_a = random.randint(2 , 1000)
tmp_m = random.randint(1 , 1000)
tmp_n = random.randint(1 , 1000)
a = symbols('a')
m , n = symbols('m n')
expr_1 = a ** m
expr_2 = a ** n
expr_3 = a ** (m + n)
self.assertEqual(expr_1.subs({ m:tmp_m , a:tmp_a }) * expr_2.subs({ n:tmp_n , a:tmp_a }) ,
expr_3.subs({ m:tmp_m , a:tmp_a , n:tmp_n }))
def test_power_of_a_power_rule(self):
# 底数不变,指数相乘
tmp_a = random.randint(2 , 1000)
tmp_m = random.randint(1 , 1000)
tmp_n = random.randint(1 , 1000)
a = symbols('a')
m , n = symbols('m n')
expr_1 = a ** m
expr_2 = expr_1 ** n
expr_3 = a ** (m * n)
self.assertEqual(expr_2.subs({ m:tmp_m , a:tmp_a , n:tmp_n }) ,
expr_3.subs({ m:tmp_m , a:tmp_a , n:tmp_n }))
def test_division_rule_for_exponents(self):
# 底数不变,指数相减
tmp_a = random.randint(2 , 1000)
tmp_m = random.randint(1 , 1000)
tmp_n = random.randint(1 , 1000)
a = symbols('a')
m , n = symbols('m n')
expr_1 = a ** m
expr_2 = a ** n
expr_3 = expr_1 / expr_2
expr_4 = a ** (m - n)
self.assertEqual(expr_3.subs({ m:tmp_m , a:tmp_a , n:tmp_n }) , expr_4.subs({ m:tmp_m , a:tmp_a , n:tmp_n }))
def test_power_of_a_product_rule(self):
# 积的乘方法则
tmp_a = random.randint(2 , 1000)
tmp_b = random.randint(1 , 1000)
tmp_m = random.randint(1 , 1000)
a , b , m = symbols('a b m')
expr_1 = a * b
expr_2 = expr_1 ** m
expr_3 = a ** m
expr_4 = b ** m
expr_5 = expr_3 * expr_4
self.assertEqual(expr_2.subs({ a:tmp_a , b:tmp_b , m:tmp_m }) , expr_5.subs({ a:tmp_a , b:tmp_b , m:tmp_m }))
def test_limitation_1(self):
a = symbols('a' , real = True , gt = 1)
x = symbols('x')
tmp_a = random.randint(2 , 1000)
expr_1 = exp(x * log(a))
expr_2 = expr_1.subs({ a:tmp_a })
self.assertEqual(limit(expr_2 , x , oo , ) , oo)
self.assertEqual(limit(expr_2 , x , -oo , ) , 0)
expr_3 = a ** x
expr_4 = expr_3.subs({ a:tmp_a })
self.assertEqual(limit(expr_4 , x , oo , ) , oo)
# 此时得到的极限时无穷,不符合预期
# 参考文档:https://cloud.tencent.com/developer/article/2529919
# self.assertEqual(limit(expr_4 , x , -oo , ) , 0)
def test_limitation_2(self):
a = symbols('a' , real = True , positive = True , gt = 0 , lt = 1)
x = symbols('x')
expr = exp(x * log(a))
expr_2 = expr.subs({ a:random.randint(1 , 10) / 100.0 })
self.assertEqual(limit(expr_2 , x , oo , ) , 0)
if __name__ == '__main__':
unittest.main()
详情请参考:通过AI排查Sympy计算指数函数极限的bug
from __future__ import annotations
from sympy import E , diff , exp , latex , limit , log , oo , simplify , symbols
"""
SymPy内部将2**x与exp(x*log(2))视为不同表达式结构
2**x 对应 Pow(Integer(2), Symbol('x'))
exp(x*log(2)) 对应 exp(Mul(Symbol('x'), log(Integer(2))))
该差异具有关键意义,表明极限算法会差异化处理二者而非自动转换形式
复数默认假设对极限计算的影响
核心发现在于:符号默认的复数假设是结果差异的根本原因。当x被视为复数时:
2**x会激活复数对数的多值分支特性
当x趋近负无穷时,分支切割可能导致未定义或错误的极限
limit函数虽强大,但在处理Pow表达式时可能忽略复数分支切割
反观exp(x*log(2)):
显式调用sympy.log
对正实底数默认采用主实数值
有效规避复数解释问题,输出正确的实数极限
SymPy不会自动将Pow(2,x)转为exp形式
复数域中 ln(z)=ln∣z∣+i(arg(z)+2kπ) 存在多值性
SymPy的log默认采用主值分支(辐角范围(−π,π])
"""
def exponential_function_limitation():
x = symbols('x' , real = True)
expr_1 = exp(x * log(2))
expr_2 = 2 ** x
print('e^(x*ln2), x->-oo: ' , limit(expr_1 , x , -oo))
print('2^x, x->-oo before fix:' , limit(expr_2 , x , -oo))
print('2^x, x->-oo after fix:' , limit(expr_2.rewrite(exp) , x , -oo))
from __future__ import annotations
from sympy import E , diff , exp , latex , limit , log , oo , simplify , symbols
def exponential_function_diff():
a , x = symbols('a, x')
expr_1 = a ** x
print('一般指数函数的导数:' , latex(simplify(diff(expr_1 , x))))
expr_2 = E ** x
print('自然指数函数的导数:' , latex(simplify(diff(expr_2 , x))))
"""
自然对数函数
Natural logarithmic function (通常表示为 ln(x) 或 log_e(x))
常用对数函数
Common logarithmic function (以10为底的对数,表示为 log_{10}(x) 或简写为 log(x))
一般对数函数
Logarithmic function with base b (以b为底的对数,表示为 log_b(x))
"""
import unittest
from sympy import limit , ln , log , oo , symbols
class TestLogarithmicFunction(unittest.TestCase):
def setUp(self):
# 定义符号变量(x为自变量,a为底数)
self.a , self.x = symbols('a x' , positive = True , real = True)
# ------------------- 基本性质测试 -------------------
def test_pass_through_point_one(self):
"""验证过点(1,0)"""
expr = log(self.x , self.a)
self.assertEqual(expr.subs(self.x , 1) , 0) # ∀a>0, logₐ(1)=0 [5](@ref)
# ------------------- 运算定理测试 -------------------
def test_product_rule(self):
"""乘法法则: logₐ(M·N)=logₐM+logₐN"""
y = symbols('y' , positive = True)
expr = log(self.x * y , self.a)
expected = log(self.x , self.a) + log(y , self.a)
self.assertTrue(expr.simplify() == expected.simplify()) # [5,8](@ref)
def test_power_rule(self):
"""幂运算法则: logₐ(Mᵏ)=k·logₐM"""
k = symbols('k' , real = True)
expr = log(self.x ** k , self.a)
expected = k * log(self.x , self.a)
self.assertTrue(expr.equals(expected)) # [5,8](@ref)
def test_base_conversion(self):
"""换底公式: log₂x = log₁₀x / log₁₀2"""
expr = log(self.x , 2)
converted = log(self.x , 10) / log(2 , 10)
self.assertTrue(expr.simplify() == converted.simplify()) # [3,5](@ref)
# ------------------- 单调性测试 -------------------
def test_increasing_for_a_gt_1(self):
"""a>1时单调递增: x₂>x₁ ⇒ logₐx₂ > logₐx₁"""
expr = log(self.x , 5) # a=5>1
self.assertGreater(
expr.subs(self.x , 10) ,
expr.subs(self.x , 5) , # log₅10 > log₅5
) # [5](@ref)
def test_decreasing_for_a_lt_1(self):
"""0<a<1时单调递减: x₂>x₁ ⇒ logₐx₂ < logₐx₁"""
expr = log(self.x , 0.5) # a=0.5<1
self.assertLess(
expr.subs(self.x , 10) ,
expr.subs(self.x , 5) , # log₀.₅10 < log₀.₅5
) # [5](@ref)
# ------------------- 极限定理测试 -------------------
def test_limit_at_zero_plus(self):
"""x→0⁺时极限为-∞"""
expr = ln(self.x) # 自然对数
self.assertEqual(
limit(expr , self.x , 0 , dir = '+') ,
-oo , # lim_{x→0⁺} ln(x) = -∞ [5,7](@ref)
)
def test_limit_at_infinity(self):
"""x→∞时极限为∞"""
expr = log(self.x , 2)
self.assertEqual(
limit(expr , self.x , oo) ,
oo , # lim_{x→∞} log₂x = ∞ [5](@ref)
)
def test_classical_limit_formula(self):
"""经典极限: lim_{x→0} ln(1+x)/x = 1"""
expr = ln(1 + self.x) / self.x
self.assertEqual(
limit(expr , self.x , 0) ,
1 , # 用于导数推导的关键极限 [5,8](@ref)
)
# ------------------- 特殊性质测试 -------------------
def test_inverse_function_relation(self):
"""验证与指数函数互逆: logₐ(aˣ)=x 且 a^(logₐy)=y"""
a_val , y_val = 3 , 8
# 正向验证: logₐ(aˣ) = x
self.assertEqual(
log(a_val ** 2 , a_val) , # log₃(3²)
2 ,
)
# 反向验证: a^(logₐy) = y
self.assertEqual(
a_val ** log(y_val , a_val) , # 3^{log₃8}
y_val ,
) # [5](@ref)
def test_composite_function_monotonicity(self):
"""复合函数单调性: y=logₐ(2-ax)在定义域内递减"""
a_val = 1.5
expr = log(2 - a_val * self.x , 10)
x0 , x1 = 0.1 , 0.2 # 在定义域[0, 2/a_val)内取点
self.assertGreater(
expr.subs(self.x , x0) ,
expr.subs(self.x , x1) , # x0<x1 ⇒ f(x0)>f(x1)
) # [5](@ref)
# ------------------- 边界与异常测试 -------------------
def test_base_one_raises_error(self):
"""
数学定义:底数 a=1 无意义
若 a=1,则方程 1^y = x 的解不唯一(对任意 y 都成立),无法定义单值函数
在 sympy 中,不抛异常,返回符号结果或简化值
log(1, N) 返回 0,数学上不严谨但程序兼容
"""
self.assertEqual(log(1 , 100) , 0)
def test_log_of_unity(self):
"""真数为1时返回0"""
self.assertEqual(log(1 , self.a) , 0) # ∀a>0, logₐ1=0 [5](@ref)
if __name__ == '__main__':
unittest.main(verbosity = 2)
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。