0
@kkml_4220

【数値解析入門】C言語で漸化式で解く Vol.1

実現したいこと

今回はC言語で漸化式と解く.

この記事に掲載してあるソースコードは私のGitHubからダウンロードできます.
必要に応じて活用してください.

漸化式とは

Wikipediaに漸化式について次のように書かれている.

数学における漸化式(ぜんかしき、英: recurrence relation; 再帰関係式)は、各項がそれ以前の項の関数として定まるという意味で数列を再帰的に定める等式である。

引用:Wikipedia 漸化式

数学の学問的な範囲でいうならば,高校数学Bの「数列」の範囲で扱うことになるので,知っている人も多いかと思う.

漸化式の2つの顔

漸化式は引用にも示したような,再帰的な方程式を用いて一意的に定義することができる.
しかし,特別な漸化式において「一般項」というものが存在する.ただし,全ての漸化式においてこの一般項を定義したり求めることができるというわけではない.

基本的な漸化式

以下, nNとする.

一般項が簡単にもとまるという点で,高校数学でも扱う基本的な漸化式は次の3パターンが存在する

  1. 等差数列の漸化式
  2. 等比数列の漸化式
  3. 階差数列の漸化式

それぞれの漸化式について順に書きたいと思います.

等差数列の漸化式

等差数列の漸化式は以下のような形をしています.

an+1an=d(d)

これは等差数列の漸化式でありながら,等差数列の定義でもある.
この数列の一般項は次ののようになる.

初項 a1, 公差 d の等差数列 an の一般項は

an=a1+(n1)d

もし余裕があれば,証明を自分で確認して欲しい.

等比数列の漸化式

等比数列の漸化式は

an+1=ran(r)

等差数列同様,これが等比数列の定義式でもある.一般にr0,1を除く.もちろん,それらの場合でも等比数列といってもいいかもしれないが,初項をa1に対して,漸化式から
r=0の場合,
a1,0,0,

のように第2項以降が0になってしまうため,わざわざ,等比数列であると認識しなくてもよいかもしれない.

r=1の場合,

a1,a1,a1,

なので,定数列となる.これは等比数列の特殊な場合と捉えるのが妥当かもしれない.

とにかく先に進もう.

ここで等比数列の一般項は
初項 a1, 公比 r の等比数列 an の一般項は

an=a1rn1

である.これも自分で証明を確認されたい.

階差数列の漸化式

階差数列の定義は,数列{an}に対して隣り合う2つの項の差

bn=an+1an

を項とする数列{bn}を数列{an}の階差数列と定義する.

階差数列の漸化式は,f(n)を階差数列の一般項として,次のような形で表される.

an+1=an+f(n)

そして階差数列の一般項

an={a1(n=1)a1+k=1n1bk(n2)

となる.

これも証明を確認しよう.

ここまで基本的な漸化式を紹介してきたが,これらをあえて数値解析で扱いたいと思う.

基本的な漸化式の数値解析

等差数列

次のような等差数列のa100を求めよ.

{an}:1,5,9,13,

ここではあえて一般項を用いず,ひたすら漸化式で第100項まで計算することにします.

tousa/iterative.c
#include <stdio.h>

#define N 100

int main(void)
{
    int an;

    an = 1; // 初項
    for (int n = 1; n <= N; n++)
    {
        printf("a[%d] = %d\n", n, an);
        an = an + 4;
    }
    return 0;
}

実行結果(一部)は次のようになる.

result
a[95] = 377
a[96] = 381
a[97] = 385
a[98] = 389
a[99] = 393
a[100] = 397

一般項の公式から求めても a100=397 なので正しく実行できていることがわかる.

実行結果としてはうまく行っているのでこれで終わりとしてもよいがこれではあまり面白くない.

というのも,漸化式そのものが再帰的なものなので,再帰関数でこれを扱いたい.

再帰(さいき)は、あるものについて記述する際に、記述しているものそれ自身への参照が、その記述中にあらわれることをいう。

引用: Wikipedia 再帰関数

実際に再帰関数化したものは次のようになる.

tousa/recursive.c
#include <stdio.h>

#define N 100

/* プロトタイプ宣言 */
int an(int n);

int main(void)
{
    for (int n = 1; n <= N; n++)
        printf("a[%d] = %d\n", n, an(n));
    return 0;
}

/* 漸化式(再帰関数) */
int an(int n)
{
    if (n == 1)
        return 1;
    else
        return (an(n - 1) + 4);
}

これも結果は先ほどの実行結果と同じようになる.

引数にnを受け取り,戻り値にan(n1)+4を返す.
これぞ漸化式と言わんばかりの形をしている.私はこの書き方の方がしっくりくるが人それぞれかもしれない.

等比数列

次のような等比数列のa10を求めよ.

{an}:1,3,9,27,

これも,普通に書くと

touhi/iterative.c
#include <stdio.h>

#define N 10

int main(void)
{
    int an;

    an = 1;
    for (int n = 1; n <= N; n++)
    {
        printf("a[%d] = %d\n", n, an);
        an = an * 3;
    }
    return 0;
}

実行結果は

a[7] = 729
a[8] = 2187
a[9] = 6561
a[10] = 19683

となり,これもあっている.

再帰関数で表現すると,

touhi/recursive.c
#include <stdio.h>

#define N 10

/* プロトタイプ宣言 */
int an(int n);

int main(void)
{
    for (int n = 1; n <= N; n++)
        printf("a[%d] = %d\n", n, an(n));
    return 0;
}

/* 漸化式(再帰関数) */
int an(int n)
{
    if (n == 1)
        return 1;
    else
        return (an(n - 1) * 3);
}

階差数列

次のような階差数列のa10を求めよ.

{an}:6,11,18,27,38

階差数列の定義にしたがって階差数列(=bn)を考えると,

bn=an+1an

より,
{bn}:5,7,9,11

となるので,これで計算してみる.
ちなみに一般項は

an=n2+2n+3

である.

kaisa/iterative.c
#include <stdio.h>

#define N 10

int main(void)
{
    int an, bn;

    an = 6;
    bn = 5;
    for (int n = 1; n <= N; n++)
    {
        printf("a[%d] = %d\n", n, an);
        an = an + bn;
        bn = bn + 2;
    }
    return 0;
}

実行結果は

a[7] = 66
a[8] = 83
a[9] = 102
a[10] = 123

となり,一般項の値と一致する.

再帰で表現してみる.

kaisa/recursive.c
#include <stdio.h>

#define N 10

/* プロトタイプ宣言 */
int an(int n);
int bn(int b);

int main(void)
{
    for (int n = 1; n <= N; n++)
        printf("a[%d] = %d\n", n, an(n));
    return 0;
}

/* 漸化式(再帰関数) */
int an(int n)
{
    if (n == 1)
        return 6;
    else
        return (an(n - 1) + bn(n - 1));
}

/* 漸化式(再帰関数) */
int bn(int n)
{
    if (n == 1)
        return 5;
    else
        return (bn(n - 1) + 2);
}

これは再帰関数の中で再帰関数を呼び出しているので,沢山計算させていることになるが,これくらいはパソコンはなんなくやってくれるのが文明の利器といったところだろうか.

おわりに

今回はここら辺で一旦区切ろうかと思う.

ここで扱った内容はとても簡単だったかもしれないが,とても重要な概念なのでここでしっかりとイメージをつかみい.

自分の復習のためでもあり,学科の友達に共有するためにこの記事を書いている.続きも書く予定なので,ぜひチェックしていただけるとありがたい.

0
ユーザー登録して、Qiitaをもっと便利に使ってみませんか。
  1. あなたにマッチした記事をお届けします
    ユーザーやタグをフォローすることで、あなたが興味を持つ技術分野の情報をまとめてキャッチアップできます
  2. 便利な情報をあとで効率的に読み返せます
    気に入った記事を「ストック」することで、あとからすぐに検索できます
kkml_4220
エンジニアになりたい大学生。数学科。専門は計算機数学なので若干、応用数学っぽい。

コメント

この記事にコメントはありません。
あなたもコメントしてみませんか :)
ユーザー登録
すでにアカウントを持っている方はログイン
記事投稿イベント開催中
新人プログラマ応援 - みんなで新人を育てよう!
~
Java開発者のためのAzure入門
~