最初に
フィボナッチ数列をメソッドに記述することを考えます。
フィボナッチ数列は
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55,……
と続いていく数列です。
式としては
F(0) = 0
F(1) = 1
F(n) = F(n-1) + F(n-2) (n>=2の場合)
と表されます。
※1から始める場合もあるようですが、今回は0から始める数列としました。
これをそのままC#で記述しようと思えば以下の様になります。
private int Fib(int n)
{
if (n == 0)
{
return 0;
}
else if (n == 1)
{
return 1;
}
else
{
Fib(n-1) + Fib(n-2);
}
}
しかし、このメソッドでは n>=2 の場合に行われる Fib(n-1) と Fib(n-2) の計算で重複しており、速度上の問題を抱えています。
メソッドを高速化する
簡単に高速化を考えるのであれば、計算した結果をキャッシュして置き、計算済みのものはそこから取得するようにすることが思いつきます。
private static readonly Dictionary<int, int> Cache = new Dictionary<int, int>();
private int Fib(int n)
{
int result;
if (Cache.TryGetValue(n, out result))
{
return result;
}
if (n == 0)
{
result = 0;
}
else if (n == 1)
{
result = 1;
}
else
{
result = Fib(n-1) + Fib(n-2);
}
Cache.Add(n, result);
return result;
}
このメソッドにおける問題としては、計算結果を保存しておく Cache が他のメソッド等から書き換えられる可能性があります。
※メソッドが複雑になったこと、非同期呼び出しについての意見もあると思いますが、それは別の検討事項としたいと思います。
メソッド内にメンバを閉じ込める
メソッド内に計算結果を保存する変数を閉じ込めることを検討します。
以下の様に記述します。
private static readonly Func<int, int> Fib = CreateFib();
private static Func<int, int> CreateFib()
{
Func<Dictionary<int, int>, int, int> fib = (cache, n) =>
{
int result;
if (cache.TryGetValue(n, out result))
{
return result;
}
if (n == 0)
{
result = 0;
}
else if (n == 1)
{
result = 1;
}
else
{
result = Fib(n - 1) + Fib(n - 2);
}
cache.Add(n, result);
return result;
};
return val => fib(new Dictionary<int, int>(), val);
}
これによりCreateFib内で作成されるDictionaryのインスタンスは他のメソッド等からアクセスされないため、安全に使えます。
最後に
実際にフィボナッチ数列を高速化するのであれば一般解を求め、一般解をメソッド化することが安全で高速な方法だと思います。(実際にフィボナッチ数列を業務で書く人もいないと思いますし)
定義を素直に記述すると再帰が発生し、遅くなる可能性がある例として利用しましたが、
この技法そのものは特定のメソッドでしか使用しない特定のメンバをメソッド内に閉じ込め、安全性を高めることに利用できると思います。