第 8 章 反函數與複合函數¶

本章重點:

  • 重新理解「函數」與「輸入 → 輸出」的觀念。
  • 複合函數的想法:先做一個函數,再做另一個函數,形成 $f(g(x))$ 或 $g(f(x))$。
  • 反函數的想法:能夠「把輸入還原」的函數 $f^{-1}$。
  • 使用 Python / SymPy 定義複合函數與求反函數。
  • 了解反函數存在的條件(單射、水平線測試、限制定義域)。
  • 透過單位換算、編碼與解碼等例子,把這些觀念與生活情境連結。

8.1 函數作為「輸入 → 輸出」的機器¶

我們可以把函數想成一台機器:

  • 把數字 $x$ 放進去(輸入)。
  • 機器依照規則 $f$ 處理,吐出結果 $f(x)$(輸出)。

在 Python 中,我們可以這樣表示:

In [1]:
def f_python(x):
    return 2*x + 3

for val in [0, 1, 5]:
    print(f"x = {val}, f(x) = {f_python(val)}")
x = 0, f(x) = 3
x = 1, f(x) = 5
x = 5, f(x) = 13

如果存在另一台機器 $g$,可以把 $f$ 的輸出「還原」成原來的 $x$, 也就是 $g(f(x)) = x$,我們就稱 $g$ 是 $f$ 的反函數,寫作 $g = f^{-1}$。

8.2 複合函數:先做 $g$ 再做 $f$¶

若有兩個函數 $f, g$:

  • $g$ 把 $x$ 變成 $g(x)$。
  • $f$ 再把 $g(x)$ 變成 $f(g(x))$。

這個新函數稱為複合函數 $f \circ g$,定義為:

$$(f \circ g)(x) = f(g(x)).$$

在 Python 中:

In [2]:
def f(x):
    return x**2 + 1

def g(x):
    return 3*x - 2

def fog(x):  # f ∘ g
    return f(g(x))

for val in [0, 1, 2]:
    print(f"x = {val}, g(x) = {g(val)}, f(g(x)) = {fog(val)}")
x = 0, g(x) = -2, f(g(x)) = 5
x = 1, g(x) = 1, f(g(x)) = 2
x = 2, g(x) = 4, f(g(x)) = 17

在 SymPy 中,我們可以用 Lambda 與符號變數,直接操作解析式:

In [3]:
import sympy as sp
sp.init_printing()

x = sp.symbols('x', real=True)
f_sym = sp.Lambda(x, x**2 + 1)
g_sym = sp.Lambda(x, 3*x - 2)

fog_sym = sp.simplify(f_sym(g_sym(x)))  # f(g(x))
gof_sym = sp.simplify(g_sym(f_sym(x)))  # g(f(x))
fog_sym, gof_sym
Out[3]:
$\displaystyle \left( \left(3 x - 2\right)^{2} + 1, \ 3 x^{2} + 1\right)$

注意:一般來說 $f(g(x))$ 與 $g(f(x))$ 並不相等, 所以複合函數操作有「順序」的概念。

8.3 使用 subs 做函數複合¶

在 SymPy 中,若函數以「解析式」形式存在,也可以用 subs 來做複合。

例如 $f(x) = x^2 + 1$、$g(x) = 3x - 2$,我們要算 $f(g(x))$:

In [4]:
x = sp.symbols('x', real=True)
f_expr = x**2 + 1
g_expr = 3*x - 2

fog_expr = f_expr.subs(x, g_expr)   # 把 f 中的 x 用 g(x) 取代
gof_expr = g_expr.subs(x, f_expr)   # 把 g 中的 x 用 f(x) 取代
fog_expr, gof_expr
Out[4]:
$\displaystyle \left( \left(3 x - 2\right)^{2} + 1, \ 3 x^{2} + 1\right)$

這種寫法非常直接,適合用在較複雜的函數組合中。

8.4 反函數的直觀:能不能「反推回去」¶

對一個函數 $f$,若存在函數 $g$ 滿足:

  • $g(f(x)) = x$(對所有適當的 $x$), -(通常也要求)$f(g(y)) = y$(對所有適當的 $y$),

則稱 $g$ 是 $f$ 的反函數,寫作 $g = f^{-1}$。

例:$f(x) = 2x + 3$。

若要還原 $x$,我們解方程 $y = 2x + 3$:

$x = \dfrac{y-3}{2}$。

因此 $f^{-1}(y) = \dfrac{y-3}{2}$,若改變變數名稱,通常寫成:

$f^{-1}(x) = \dfrac{x-3}{2}$。

8.5 用 SymPy 求反函數(線性例子)¶

讓 SymPy 幫我們做上述步驟:

  1. 令 $y = f(x)$。
  2. 解方程 $y = f(x)$ 對 $x$。
  3. 把解視為 $x = f^{-1}(y)$。
In [5]:
x, y = sp.symbols('x y', real=True)
f_expr = 2*x + 3
equation = sp.Eq(y, f_expr)
solution_for_x = sp.solve(equation, x)
equation, solution_for_x
Out[5]:
$\displaystyle \left( y = 2 x + 3, \ \left[ \frac{y}{2} - \frac{3}{2}\right]\right)$

解出的 solution_for_x[0] 是 $x$ 用 $y$ 表示的形式:$x = \dfrac{y-3}{2}$。

若要把它寫成反函數的形式,可以把 $y$ 改回常用的自變數符號(例如 $t$ 或 $x$):

In [6]:
t = sp.symbols('t', real=True)
f_inv_expr = solution_for_x[0].subs(y, t)  # 把 y 換成 t
f_inv = sp.Lambda(t, f_inv_expr)
f_inv
Out[6]:
$\displaystyle \left( t \mapsto \frac{t}{2} - \frac{3}{2} \right)$

我們可以檢查 $f^{-1}(f(x))$ 是否等於 $x$:

In [7]:
check = sp.simplify(f_inv(f_expr))  # f^{-1}(f(x))
check
Out[7]:
$\displaystyle x$

8.6 非線性例子與定義域限制¶

考慮 $f(x) = x^2$。若不限制定義域,$f$ 並沒有反函數,因為:

$f(2) = 4$ 且 $f(-2) = 4$,輸出 4 對應到兩個輸入。

此時常見的做法是限制定義域,例如只考慮 $x \ge 0$, 這樣就可以定義反函數 $f^{-1}(x) = \sqrt{x}$(只取非負根)。

用 SymPy 嘗試求反函數:

In [8]:
x, y = sp.symbols('x y', real=True)
f_expr = x**2
equation = sp.Eq(y, f_expr)
solution_for_x = sp.solve(equation, x)
equation, solution_for_x
Out[8]:
$\displaystyle \left( y = x^{2}, \ \left[ - \sqrt{y}, \ \sqrt{y}\right]\right)$

你會得到兩個解:$x = \sqrt{y}$ 與 $x = -\sqrt{y}$, 這反映出若不限制定義域,就無法得到「唯一」的反函數。

通常我們會在題目中指定「限制在 $x \ge 0$」,這樣就選擇 $x = \sqrt{y}$ 這支分支作為反函數。

8.7 常見可逆函數:線性、指數、對數¶

8.7.1 線性函數¶

$f(x) = ax + b$,若 $a \ne 0$,則 $f$ 為一對一函數,反函數為:

$f^{-1}(x) = \dfrac{x-b}{a}$。

8.7.2 指數與對數¶

$f(x) = e^x$ 在 $\mathbb{R}$ 上是嚴格遞增,因此有反函數 $f^{-1}(x) = \ln x$, 其定義域為 $x>0$。

SymPy 中可以直接用 exp 與 log:

In [9]:
x = sp.symbols('x', real=True)
f_exp = sp.exp(x)
f_log = sp.log(x)
f_exp, f_log
Out[9]:
$\displaystyle \left( e^{x}, \ \log{\left(x \right)}\right)$

我們可以驗證 $\log(e^x) = x$:

In [10]:
check1 = sp.simplify(sp.log(sp.exp(x)))
check1
Out[10]:
$\displaystyle x$

另一方面,$e^{\log x} = x$ 對 $x>0$ 成立:

In [11]:
check2 = sp.simplify(sp.exp(sp.log(x)))
check2
Out[11]:
$\displaystyle x$

這些等式反映了指數與對數互為反函數的關係。

8.8 利用組合檢查反函數¶

若聲稱某個函數 $g$ 是 $f$ 的反函數,我們可以檢查:

  • $f(g(x))$ 是否在適當的定義域上化簡為 $x$。
  • $g(f(x))$ 是否也能化簡為 $x$。

例:$f(x) = \dfrac{x-3}{2}$ 與 $g(x) = 2x + 3$。

In [12]:
x = sp.symbols('x', real=True)
f_expr = (x - 3)/2
g_expr = 2*x + 3

fog = sp.simplify(f_expr.subs(x, g_expr))  # f(g(x))
gof = sp.simplify(g_expr.subs(x, f_expr))  # g(f(x))
fog, gof
Out[12]:
$\displaystyle \left( x, \ x\right)$

你可以試著換成其他候選函數,檢查它們是否真的是彼此的反函數。

8.9 啟發性例子一:攝氏與華氏溫度換算¶

日常生活中最經典的反函數例子之一,就是攝氏(°C)與華氏(°F)的換算:

$F = \dfrac{9}{5}C + 32$。

這裡可以把 $f$ 視為「從攝氏到華氏」的函數:

$f(C) = \dfrac{9}{5}C + 32$。

我們想要反函數 $f^{-1}$,從華氏換回攝氏:

用 SymPy 解方程 $F = \frac{9}{5}C + 32$:

In [13]:
C, F = sp.symbols('C F', real=True)
equation_CF = sp.Eq(F, sp.Rational(9, 5)*C + 32)
solution_C = sp.solve(equation_CF, C)
equation_CF, solution_C
Out[13]:
$\displaystyle \left( F = \frac{9 C}{5} + 32, \ \left[ \frac{5 F}{9} - \frac{160}{9}\right]\right)$

解得:$C = \dfrac{5}{9}(F - 32)$,也就是 $f^{-1}(F)$。

我們可以檢查組合:

  • 從攝氏 0 度 → 華氏 → 再換回攝氏。
In [14]:
f_CF = sp.Lambda(C, sp.Rational(9, 5)*C + 32)          # C → F
f_FC = sp.Lambda(F, sp.Rational(5, 9)*(F - 32))        # F → C

C0 = 0
F0 = f_CF(C0)
C_back = f_FC(F0)
C0, F0, C_back
Out[14]:
$\displaystyle \left( 0, \ 32, \ 0\right)$

你可以自己試試看 100°C(水的沸點)或 -40°C(攝氏與華氏相等的溫度), 感受反函數在實際單位換算中的角色。

8.10 啟發性例子二:簡單編碼與解碼¶

想像一個非常簡化的「加密」方式:

  • 把每個數字 $x$ 變成 $f(x) = 3x + 1$,稱為「編碼」。
  • 想要恢復原始數字,就需要一個「解碼」函數 $g$,使得 $g(f(x)) = x$。

顯然這與之前的線性反函數例子完全相同。

用 Python 實作一個小小的編碼/解碼系統:

In [15]:
def encode(x):
    return 3*x + 1

def decode(y):
    return (y - 1)/3

data = [5, 10, -2]
encoded = [encode(x) for x in data]
decoded = [decode(y) for y in encoded]

print("原始資料:", data)
print("編碼後:", encoded)
print("解碼後:", decoded)
原始資料: [5, 10, -2]
編碼後: [16, 31, -5]
解碼後: [5.0, 10.0, -2.0]

在真正的資安與密碼學中,編碼與解碼函數會複雜得多, 但「函數與反函數」的概念仍然是核心之一。

8.11 啟發性例子三:複合函數作為流程管線¶

在資料處理或科學計算中,我們常常會依序做一系列的轉換:

  1. 把原始資料加上某個偏移量(平移)。
  2. 再把結果乘上一個比例因子(縮放)。
  3. 可能再套用某種非線性函數(例如對數)。

這就像是有一連串函數 $f, g, h$,我們對輸入 $x$ 做:

$h(g(f(x)))$。

來一個簡單的例子:

  • $f(x) = x + 1$(加 1)。
  • $g(x) = 2x$(乘 2)。
  • $h(x) = \ln x$(取自然對數,定義域 $x>0$)。

組合起來:$H(x) = h(g(f(x))) = \ln(2(x+1))$。

用 SymPy 實作與簡化:

In [16]:
x = sp.symbols('x', real=True)
f = x + 1
g = 2*f             # g(f(x)) = 2(x+1)
h = sp.log(g)       # h(g(f(x))) = log(2(x+1))

sp.simplify(h)
Out[16]:
$\displaystyle \log{\left(2 x + 2 \right)}$

這種「先做哪個函數,再做哪個函數」的思維,在程式設計中也常被稱為 pipeline(管線) 或 composed functions, 是建構複雜計算流程的一個重要觀念。

8.12 本章小結¶

本章你學到了:

  • 複合函數 $f \circ g$ 的定義與實作方式(包含 Python 與 SymPy)。
  • 如何使用 subs 與 Lambda 來處理解析式的函數複合。
  • 反函數的觀念:能夠「還原輸入」的函數,以及用 solve 求反函數的方法。
  • 反函數存在需要「一對一」的條件,許多非線性函數需要限制定義域才能有反函數。
  • 常見可逆函數:線性、指數與對數,以及它們在單位換算與實際應用中的角色。
  • 複合與反函數在日常生活中的例子:溫度換算、編碼解碼、資料處理管線等。

8.13 練習題¶

請在本 Notebook 中新增儲存格,試著完成以下練習:

  1. 基本複合函數
    令 $f(x) = x^2 + 1$,$g(x) = 2x - 3$。
    (a) 手算 $f(g(x))$ 與 $g(f(x))$。
    (b) 使用 SymPy 的 subs 或 Lambda 計算並簡化,確認與手算結果一致。

  2. 反函數(線性)
    令 $f(x) = -3x + 5$。
    (a) 使用代數方法求出 $f^{-1}(x)$。
    (b) 用 SymPy 建立方程 $y = -3x + 5$,解出 $x$,寫成反函數形式。
    (c) 檢查 $f(f^{-1}(x))$ 與 $f^{-1}(f(x))$ 是否都可以簡化為 $x$。

  3. 非線性反函數與限制定義域
    (a) 令 $f(x) = x^2 - 4$。嘗試用 SymPy 求反函數,觀察結果。
    (b) 思考若限制 $x \ge 0$,反函數應該是什麼形狀?
    (c) 嘗試畫出 $y = x^2 - 4$ 以及 $y = f^{-1}(x)$ 在平面上的圖形(只取適當的部分), 並觀察它們是否為 $y=x$ 的鏡射。

  4. 指數與對數
    (a) 使用 SymPy 簡化 $\log(e^{2x+1})$。
    (b) 使用 SymPy 簡化 $e^{\log(3x)}$(注意定義域條件)。
    (c) 解方程 $e^{x} = 7$,寫出 $x$ 的解,並計算其近似數值。

  5. 單位換算練習
    (a) 除了攝氏與華氏外,試著建立公里(km)與英里(mile)的換算函數(1 mile ≈ 1.609 km)。
    (b) 寫出 km_to_mile 與 mile_to_km 兩個 Python 函數,並檢查它們互為反函數(在計算誤差允許範圍內)。

  6. 函數作為流程管線
    假設某測量數據 $x$ 要經過以下處理:

    • 先減去偏移量 10(函數 $f(x) = x - 10$)。
    • 再除以 2(函數 $g(x) = x/2$)。
    • 再取自然對數(函數 $h(x) = \log x$,假設輸入為正)。
      (a) 寫出組合函數 $H(x) = h(g(f(x)))$ 的解析式(可用 SymPy 簡化)。
      (b) 對幾個不同的 $x$ 值(例如 12, 20, 50)計算 $H(x)$, 並解釋這個流程可能代表什麼樣的資料處理過程。
  7. 加分題:反函數是否存在?
    (a) 對下列函數,思考是否能在「整個實數軸」上定義反函數,若不行,在哪些區間上可以?
    - $f(x) = x^3$
    - $g(x) = \sin x$
    - $h(x) = x^2 + x$
    (b) 嘗試用 SymPy 的繪圖工具畫出它們的圖形,並用「水平線測試」直觀判斷是否一對一。
    (c) 選一個你感興趣的函數,自己決定一個適合的定義域,使其在該區間上可逆, 然後嘗試用 SymPy 求出其反函數。

回首頁