プログラミング言語 Standard ML 入門 (問題の解答例)
2 関数を用いたプログラミング

2.3 再帰的関数

問 2.2

factorial 関数が正しく動作することを, factorial 0factorial 2factorial 3 の各場合の動作をトレースすることによって確かめよ.

解答例  factorial 3の呼び出しは以下のようなトレースを生成する.

   factorial 3
    > 3 * factorial 2
    >      > 2 * factorial 1
    >      >      > 1 * factorial 0
    >      >      >      > 1
    >      >      > 1 * 1
    >      >      > 1
    >      > 2 * 1
    >      > 2
    > 3 * 2
    > 6
問 2.3

以下の各数列Snについて,Snが満たすべき性質をnに関する漸化式として記述し, それに対応する再帰的関数を記述することによって,Snを求めるプログラムを 書け.

  1. 1.

    Sn=1+2++n

  2. 2.

    Sn=1+(1+2)+(1+2+3)++(1+2++n)

解答例 

  1. 1.

    漸化式は

    S0 = 0
    Sn = n+Sn-1

    と与えられる.よって,この関数をfとすると,以下の コードで実現できる.

       fun f 0 = 0
         | f n = n + f (n - 1);
    
  2. 2.

    漸化式は、上記の漸化式の解をSnとして、

    T0 = 0
    Tn = Tn-1+Sn

    と与えられる.よって,この関数をgとすると,以下のコードで実現できる.

       fun g 0 = 0
         | g n = g (n - 1) + f n;
    
問 2.4

2.3で定義した2つの関数が,それぞれ正しく数列の和 を計算することを,入力nに関する帰納法で証明せよ.

解答例 

  1. 1.

    Sn=1+2+...+n

    1. (a)

      (基底)n = 0のとき.S0=0, f 0=𝟶で正しい.

    2. (b)

      (帰納段階)n = kのとき正しいと仮定する.

      Sk+1=1+2+...+k+(k+1)=Sk+(k+1)

      である. 一方f (k + 1) = (k + 1) + f kであり,帰納法の仮定より f k=Skであるから, f (k + 1)=Sk+1である.

  2. 2.

    Sn=1+(1+2)+(1+2+3)+...+(1+2+...+n)

    1. (a)

      (基底)n=0のとき, S0=0, f 0=0で正しい.

    2. (b)

      (帰納段階)n=kのとき正しいと仮定する.

      Sk+1 = 1+(1+2)+(1+2+3)+...+(1+2+...+k)+(1+2+...+k+(k+1))
      = Sk+(1+2+...+k+(k+1))

      である. 一方f (k + 1)=f k + g (k + 1)であり, 帰納法の仮定と前項の結果より f k=Sk かつ g (k+1)=(1+2+...+k+(k+1))であるから, f (k + 1)=Sk+1である.

問 2.5

以下のように再帰的に定義される数列をフィボナッチ数列と呼ぶ.

F0 = 0
F1 = 1
Fn = Fn-2+Fn-1(n2)

負でない整数nを受け取りFnを計算する関数 fib を定義せよ.

解答例 

   fun fib n = if n = 0 then 0
               else if n = 1 then 1
               else fib (n - 1) + fib (n - 2)
問 2.6

kを与えられた負でない整数とし,任意の0nkについて fib nFnを計算することを,kに関する数学的帰納法で示せ. これを用いて,fib が,0以上の任意の自然数に対して正しく Fnの値を計算することを確認せよ.

解答例  任意の0nkについてfib nFnを計算することのkに関する帰納法による証明

  • k=0,1の時. fib 0=0=F0 かつ fib 1=1=F1 であり成立.

  • k=i(i1)の時成立すると仮定する. fib (i + 1)=fib i+fib (i - 1) である. 帰納法の仮定より, fib i=Fiかつ fib (i - 1)=Fi-1 である. また定義より, Fi=Fi-1+Fi-2 である. よって,fib i=Fiとなる. 従って,任意の0ni+1 について fib n=Fnであり,成立する.

以上より,0以上の任意のkに対して, 任意の0nk について fib n=Fnであるから, 0以上の任意の自然数に対して正しく Fnの値を計算する.

問 2.7

フィボナッチ数列Fnの再帰的定義をそのまま再帰的関数として実現す ると,Fnの計算に掛かる時間はnが大きくなるに従って指数関数的に増大す る. Fnnに比例する時間で計算する関数 fastFib を以下の洞察を 参考に定義せよ.

  1. 1.

    フィボナッチ数列の定義は,連続する2つの数から次の数を決めるような 構成になっている. したがって,FkFk+1が与えられればFk+2が即座に求められ る. この作業をn-1回繰り返せば,FkFk+1からFk+nが求めら れるはずである.

  2. 2.

    前項の考え方を参考に,与えられた連続する2つのフィボナッチ数 FkFk+1から, Fkn個先のフィボナッチ数を求める関数 G(n,Fk,Fk+1) を定義する. n0の場合はFkを返せばよい. n1の場合はFk+1を返せばよい. n2以上の場合は,FkFk+1からFk+2を求め, Fk+1Fk+2から,Fk+1n-1個先のフィボナッチ数を求めればよ いから,G(n-1,Fk+1,Fk+Fk+1) を呼び出せばよい.

  3. 3.

    FnF0からn個先のフィボナッチ数であるから,G(n,F0,F1) である. したがって fastFib は,上記の動作をする補助関数 G を呼 び出すことによって実現できる.

解答例 

   fun G(n,fk,fk1) = if n = 0 then fk
                     else if n = 1 then fk1
                     else G(n-1,fk1,fk+fk1);
   fun fastFib n = G(n,0,1);
問 2.8

負でない任意の整数nに対してG(n,Fk,Fk+1)Fk+nを計算することを,nに関する数学的帰納法で証明せよ. この性質を使って,fastFib nが正しくフィボナッチ数Fnを計 算することを示せ.

解答例 

  • n=0,1の時。

    G(0,Fk,Fk+1) = Fk
    G(1,Fk,Fk+1) = Fk+1

    であり成立する.

  • n=i+1(i1)の時。 Gの定義より

    G(i+1,FkFk+1)=G(iFk+1Fk+Fk+1)

    である. Fnの定義より, Fk+Fk+1=Fk+2 である. よって, G(i+1FkFk+1)=G(i,Fk+1,Fk+2) である. 帰納法の仮定より, G(i+1,Fk,Fk+1)=Fk+i+1 となり成立する.

以上の特殊な場合として, G(n,F0,F1)=Fn であり,したがって, fastFib n=Fnである.