メインコンテンツまでスキップ

再帰

簡単な再帰関数

今回は、再帰関数を学んでいきます。

ではまずは、次の問題を考えてみましょう。

a1=1a_1 = 1an=an1+1a_n = a_{n - 1} + 1 とします。このとき、ana_n を求めるプログラムを書いてください。

様々な方法が考えられます。

解法 1

この漸化式は、次のように数学的に解くことができます。

an=an1+1an=a1+(n1)×1an=n\begin{aligned} a_n &= a_{n-1} + 1 \\ \therefore a_n &= a_1 + (n-1)\times 1 \\ \therefore a_n &= n \end{aligned}

よって、この結果を使えば次のように簡単に解けます。

Open In Colab

解法 2

他には、次のようにして a1,a2,a3,a_1,a_2,a_3,\dots のように順番に求めていくこともできます。

Open In Colab

解法 1 のように数学的に解くことができればもちろん最も良いですが、数学的に解けないような場合は解法 2 のようにするしかなさそうです。しかし、解法 2 は漸化式の形とは少し見た目が違うため少し考えてから作らないといけません。

そのようなときに、再帰関数を使うとより簡単に書くことができます。(実は、再帰関数を使う以外の選択肢がないような場面もあります。)

実際に、この問題を再帰関数を使って解いたプログラムを見てみましょう。

Open In Colab

このプログラムを見ると、漸化式の形そのままですね。何も考えずに簡単に作ることができます。

さらに、このプログラムをよく見ると関数の中で自身を呼び出しています。a(n) の値を求めるために a(n - 1) を使っています。さらに、a(n - 1) を求めるために a(n - 2) を使っています。これを繰り返していきますが、a(1)1 とわかっているので、そこで終了します。

再帰関数の作り方をまとめると、次のようになります。

  1. 漸化式を立てて、自身を呼び出す。(例:an=an1+1a_n = a_{n-1} + 1
  2. 初期条件の処理を別に作っておく。(例:a1=1a_1 = 1
再帰の定義方法

再帰の定義を普通に書けば次のようになります。

再帰(さいき 英:Recursion,Recursive)は、ある物事について記述する際に、記述しているもの自体への参照が、その記述中にあらわれることをいう。 -- フリー百科事典『ウィキペディア(Wikipedia)』

しかし、再帰の性質から次のように定義が書かれることもあります(笑)

再帰的接頭語

コンピューターの世界では、再帰を用いた命名もよくあります。再帰的接頭語と呼ばれます。例えば、プログラミング言語の PHP は「PHP: Hypertext Preprocessor」、GNU は「GNU's Not Unix」、WINE は「Wine Is Not an Emulator」、TikZ は「TikZ ist kein Zeichenprogramm」となっています。

再帰関数を使ったフィボナッチ数列

では、再帰を使ってもう少し難しいプログラムも作ってみましょう。

フィボナッチ数を求めるプログラムを作ってみましょう。 フィボナッチ数列(FnF_n)は、次のように定義されます。

F0=0F1=1Fn=Fn1+Fn2(n2)\begin{aligned} &F_0 = 0 \\ &F_1 = 1 \\ &F_n = F_{n - 1} + F_{n - 2} \quad (n\geq 2) \end{aligned}

次のような数列になっています。

0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, \dots
Open In Colab

やはり、再帰を使うと定義どおりに書けて簡単ですね。

fib 関数の流れは次のようになっています。

注記

高校数学でやるように、フィボナッチ数列の一般項は求めることができます。ビネの公式と呼ばれます。

Fn=Fn1+Fn2F_n = F_{n - 1} + F_{n - 2}

x2x1=0x^2 - x - 1 = 0 の 2 解を α,β\alpha, \beta とします。すなわち、α=1+52\displaystyle \alpha = \frac{1 + \sqrt{5}}{2}β=152\displaystyle \beta = \frac{1 - \sqrt{5}}{2}とすれば、

{α+β=1αβ=1\begin{dcases} \alpha + \beta &= 1 \\ \alpha \beta &= -1 \end{dcases}

です。よって、

Fn(α+β)Fn1+αβFn2=0F_n - (\alpha + \beta)F_{n - 1} + \alpha \beta F_{n - 2} = 0
{FnαFn1=β(Fn1αFn2)FnβFn1=α(Fn1βFn2)\therefore \begin{dcases} F_n - \alpha F_{n - 1} &= \beta (F_{n - 1} - \alpha F_{n - 2}) \\ F_n - \beta F_{n - 1} &= \alpha (F_{n - 1} - \beta F_{n - 2}) \end{dcases}
{Fn+1αFn=βn(F1αF0)Fn+1βFn=αn(F1βF0)\therefore \begin{dcases} F_{n + 1} - \alpha F_n &= \beta^n (F_1 - \alpha F_0) \\ F_{n + 1} - \beta F_n &= \alpha^n (F_1 - \beta F_0) \end{dcases}
(αβ)Fn=(αnβn)F1F0(βαnαβn)\therefore (\alpha-\beta)F_n = (\alpha^n - \beta^n)F_1 - F_0 (\beta \alpha^n - \alpha \beta^n)
Fn=αnβnαβ(F0=0,F1=1)\therefore F_n = \frac{\alpha^n - \beta^n}{\alpha-\beta} \quad (\because F_0=0, F_1=1)
Fn=15{(1+52)n(152)n}\therefore F_n = \frac{1}{\sqrt{5}}\left\{\left(\frac{1 + \sqrt{5}}{2}\right)^n - \left(\frac{1 - \sqrt{5}}{2}\right)^n\right\}

練習問題 1

再帰関数を使って、11 から nn までの和を求める関数を作ってみましょう。

ヒント

漸化式は、次のようになります。

a1=1an=an1+n\begin{aligned} &a_1=1 \\ &a_n=a_{n - 1} + n \end{aligned}
解答

これを基にすると、次のようになります。

Open In Colab

練習問題 2

再帰関数を使って、最大公約数を求める関数を作ってみましょう。

さらに、その結果を用いて、最小公倍数を求める関数も作ってみましょう。

ヒント

ユークリッドの互除法を用いれば簡単にできます。

解答
Open In Colab
注記

このプログラムは、はじめの引数が a<ba < b の場合でも動きます。一度目の再帰で、gcd(b, a % b) = gcd(b, a) が返されるからです。

つまり、18183030 の最大公約数を求めるとき、gcd(a,b)\mathrm{gcd}(a, b)aabb の最大公約数とすると、

gcd(18,30)=gcd(30,1830×0)=gcd(30,18)=gcd(18,3018×1)=gcd(18,12)=gcd(12,1812×1)=gcd(12,6)=gcd(6,126×2)=gcd(6,0)=6\begin{aligned} \mathrm{gcd}(18, 30) &= \mathrm{gcd}(30, 18 - 30\times 0) \\ &= \mathrm{gcd}(30, 18) \\ &= \mathrm{gcd}(18, 30 - 18\times 1) \\ &= \mathrm{gcd}(18, 12) \\ &= \mathrm{gcd}(12, 18 - 12\times 1) \\ &= \mathrm{gcd}(12, 6) \\ &= \mathrm{gcd}(6, 12 - 6\times 2) \\ &= \mathrm{gcd}(6, 0) \\ &= 6 \end{aligned}

となります。

また、最大公約数 gcd(a,b)\mathrm{gcd}(a, b) と最小公倍数 lcm(a,b)\mathrm{lcm}(a, b) の間には次のような関係があります。

gcd(a,b)lcm(a,b)=ab\mathrm{gcd}(a, b)\cdot \mathrm{lcm}(a, b) = ab

これを使うと、最小公倍数を求めるプログラムは次のようになります。

Open In Colab

練習問題 3

リュカ数を求めるプログラムを作ってみましょう。

リュカ数列(LnL_n)は次のように定義されます。

L0=2L1=1Ln=Ln1+Ln2(n2)\begin{aligned} &L_0 = 2 \\ &L_1 = 1 \\ &L_n = L_{n - 1} + L_{n - 2} \quad (n\geq 2) \end{aligned}

リュカ数列は次のようになります。

2,1,3,4,7,11,18,29,47,76,123,199,322,521,843,1364,2207,2, 1, 3, 4, 7, 11, 18, 29, 47, 76, 123, 199, 322, 521, 843, 1364, 2207, \dots
注記

リュカ数列の一般項は、フィボナッチ数列の時のように計算すると、次のようになります。

(αβ)Ln=(αnβn)L1L0(βαnαβn)(αβ)Ln=(12β)αn(2α1)βnLn=5αn+βnαβ\begin{aligned} (\alpha-\beta)L_n &= (\alpha^n - \beta^n)L_1 - L_0 (\beta \alpha^n - \alpha \beta^n) \\ \therefore (\alpha-\beta)L_n &= (1 - 2\beta)\alpha^n - (2\alpha - 1)\beta^n \\ \therefore L_n &= \sqrt{5} \cdot \frac{\alpha^n + \beta^n}{\alpha-\beta} \end{aligned}
Ln=(1+52)n+(152)nL_n = \left(\frac{1 + \sqrt{5}}{2}\right)^n + \left(\frac{1 - \sqrt{5}}{2}\right)^n
解答

フィボナッチ数列のプログラムから、n == 0 の時と n == 1 の時の値を変えるだけで簡単に作れます。

Open In Colab

練習問題 4

トリボナッチ数を求めるプログラムを作ってみましょう。 トリボナッチ数列(TnT_n)は次のように定義されます。

T0=0T1=0T2=1Tn=Tn1+Tn2+Tn3(n3)\begin{aligned} &T_0 = 0 \\ &T_1 = 0 \\ &T_2 = 1 \\ &T_n = T_{n - 1} + T_{n - 2} + T_{n - 3} \quad (n\geq 3) \end{aligned}

トリボナッチ数列は次のようになります。

0,0,1,1,2,4,7,13,24,44,81,149,274,504,0, 0, 1, 1, 2, 4, 7, 13, 24, 44, 81, 149, 274, 504, \dots
注記

ちなみに、テトラナッチ数、ペンタナッチ数、…もあります。

kk名称
2フィボナッチ数
3トリボナッチ数
4テトラナッチ数
5ペンタナッチ数
6ヘキサナッチ数
7ヘプタナッチ数
8オクタナッチ数
9ノナナッチ数
10デカナッチ数
11ウンデカナッチ数
12ドデカナッチ数
注記

トリボナッチ数列の一般項は、次のようにして計算できます。

x3x2x1=0x^3 - x^2 - x - 1 = 0 の 3 解を α,β,γ\alpha, \beta, \gamma とすると、

{α+β+γ=1αβ+βγ+γα=1αβγ=1\begin{dcases} \alpha + \beta + \gamma = 1 \\ \alpha \beta + \beta \gamma + \gamma \alpha = -1 \\ \alpha \beta \gamma = 1 \end{dcases}
Tn=Tn1+Tn2+Tn3T_n = T_{n - 1} + T_{n - 2} + T_{n - 3}
Tn(α+β+γ)Tn1+(αβ+βγ+γα)Tn2αβγTn3=0\therefore T_n - (\alpha + \beta + \gamma)T_{n - 1} + (\alpha \beta + \beta \gamma + \gamma \alpha)T_{n - 2} - \alpha \beta \gamma T_{n - 3} = 0
{TnαTn1=β(Tn1αTn2)+γ(Tn1αTn2)βγ(Tn2αTn3)TnβTn1=γ(Tn1βTn2)+α(Tn1βTn2)γα(Tn2βTn3)\begin{dcases} \therefore T_n - \alpha T_{n - 1} = \beta (T_{n - 1} - \alpha T_{n - 2}) + \gamma (T_{n - 1} - \alpha T_{n - 2}) - \beta \gamma(T_{n - 2} - \alpha T_{n - 3}) \\ \therefore T_n - \beta T_{n - 1} = \gamma (T_{n - 1} - \beta T_{n - 2}) + \alpha (T_{n - 1} - \beta T_{n - 2}) - \gamma \alpha(T_{n - 2} - \beta T_{n - 3}) \end{dcases}

Un=Tn+1αTn,Vn=Tn+1βTnU_n = T_{n + 1} - \alpha T_n, V_n = T_{n + 1} - \beta T_n とします。

{Un=βUn1+γUn1βγUn2Vn=γVn1+αVn1γαVn2\begin{dcases} U_n = \beta U_{n - 1} + \gamma U_{n - 1} - \beta \gamma U_{n - 2} \\ V_n = \gamma V_{n - 1} + \alpha V_{n - 1} - \gamma \alpha V_{n - 2} \end{dcases}
{UnβUn1=γ(Un1βUn2)UnγUn1=β(Un1γUn2){VnγVn1=α(Vn1γVn2)VnαVn1=γ(Vn1αVn2)\therefore \begin{dcases} U_n - \beta U_{n - 1} = \gamma (U_{n - 1} - \beta U_{n - 2}) \\ U_n - \gamma U_{n - 1} = \beta (U_{n - 1} - \gamma U_{n - 2}) \end{dcases} \begin{dcases} V_n - \gamma V_{n - 1} = \alpha (V_{n - 1} - \gamma V_{n - 2}) \\ V_n - \alpha V_{n - 1} = \gamma (V_{n - 1} - \alpha V_{n - 2}) \end{dcases}

U0=0,U1=1,V0=0,V1=1U_0 = 0, U_1 = 1, V_0 = 0, V_1 = 1 から、

{Un=βnγnβγVn=γnαnγα\therefore \begin{dcases} U_n = \frac{\beta^n - \gamma^n}{\beta - \gamma} \\ V_n = \frac{\gamma^n - \alpha^n}{\gamma - \alpha} \end{dcases}
{Tn+1αTn=βnγnβγTn+1βTn=γnαnγα\therefore \begin{dcases} T_{n + 1} - \alpha T_n = \frac{\beta^n - \gamma^n}{\beta - \gamma} \\ T_{n + 1} - \beta T_n = \frac{\gamma^n - \alpha^n}{\gamma - \alpha} \end{dcases}
Tn=γnαn(αβ)(γα)βnγn(αβ)(βγ)\therefore T_n = \frac{\gamma^n - \alpha^n}{(\alpha - \beta)(\gamma - \alpha)} - \frac{\beta^n - \gamma^n}{(\alpha - \beta)(\beta - \gamma)}
Tn=αn(αβ)(αγ)+βn(βγ)(βα)+γn(γα)(γβ)\therefore T_n = \frac{\alpha^n}{(\alpha - \beta)(\alpha - \gamma)} + \frac{\beta^n}{(\beta - \gamma)(\beta - \alpha)} + \frac{\gamma^n}{(\gamma - \alpha)(\gamma - \beta)}

テトラナッチ数は、次のようになるようです。確かめてはいません。

x4x3x2x1=0x^4 - x^3 - x^2 - x - 1 = 0 の 4 解を α,β,γ,δ\alpha, \beta, \gamma, \delta とすると、

Tn=αn(αβ)(αγ)(αδ)+βn(βγ)(βδ)(βα)+γn(γδ)(γα)(γβ)+δn(δα)(δβ)(δγ)\begin{split} T_n = \frac{\alpha^n}{(\alpha - \beta)(\alpha - \gamma)(\alpha - \delta)} + \frac{\beta^n}{(\beta - \gamma)(\beta - \delta)(\beta - \alpha)} \\ + \frac{\gamma^n}{(\gamma - \delta)(\gamma - \alpha)(\gamma - \beta)} + \frac{\delta^n}{(\delta - \alpha)(\delta - \beta)(\delta - \gamma)} \end{split}
解答
Open In Colab