(7-1)[C] 繰り返し計算の考え方
状態の設計
繰り返しコード設計の基本は,
- 求める値に至る(それを特別な場合として含む)計算の途中状態を設計し、
- 計算の途中結果の値を保持するための変数と
- 計算の進行状態を保持する変数を用意し,
- これら変数を更新するコードを書く
ことである。以下,簡単な例について、その計算状態と変数の設計方針を示す.
- 1からnまでの積を求める(前のfactorialの例)
- 計算結果を持つ変数:s
- 計算の状態を保持する変数:i
- 途中の計算状態:s = i * (i + 1) * ... * n
- 最終状態:i = 0
- 1からnまでの総和を求める
- 計算結果を持つ変数:s
- 計算の状態を保持する変数:i
- 途中の計算状態:s = i + (i+1) + ... + n
- 最終状態:i = 0
- 1からnまでの整数の2乗の和を求める
- 計算結果を持つ変数:s
- 計算の状態を保持する変数:i
- 途中の計算状態:s = i*1 + (i+1)*(i+1) + ... + n * n
- 最終状態:i = 0
- 1からnまでの整数の3乗の和を求める
- 計算結果を持つ変数:s
- 計算の状態を保持する変数:i
- 途中の計算状態:s = i * i * i + (i+1) * (i+1) * (i+1) + ... + n*n*n
- 最終状態:i = 0
- xとyの積を足し算を使って計算する(積はyをx回足したもの)
- 計算結果を持つ変数:s
- 計算の状態を保持する変数:i
- 途中の計算状態:s = xをi回足した結果
- 最終状態:i = x
- 与えられた数nをk乗する.
- 計算結果を持つ変数:s
- 計算の状態を保持する変数:i
- 途中の計算状態:s = nの(k - i)乗
- 最終状態:i = 0
- 初項A(0)が1公差が2の等差数列のn番目の項A(n).(注)
- 計算結果を持つ変数:s
- 計算の状態を保持する変数:i
- 途中の計算状態:s = i番目の項
- 最終状態:i > n
- 初項B(0)が1公比が2の等比数列のn番目の項B(n).
- 計算結果を持つ変数:s
- 計算の状態を保持する変数:i
- 途中の計算状態:s = i番目の項
- 最終状態:i > n
- 初項C(0) = 1でC(n) = 2 + 3 * C(n-1)なる漸化式の一般公
- 計算結果を持つ変数:s
- 計算の状態を保持する変数:i
- 途中の計算状態:s = i番目の項
- 最終状態:i > n
(注1)高等学校などの数列の定義では,項には1から始まる番号がつけられるが,計算機でのプログラミングでは,番号は0から付けるほうが,自然で便利なことが多い.
ループによる実現
以上の方針が決まれば、対応する変数を宣言し、最終状態をループの終了条件とし、途中状態が保たれるようにループ内の文で変数を更新するコードを書けばよい。以下がその例である。
- 1からnまでの積を求める(前のfactorialの例)
- 計算結果を持つ変数:s
- 計算の状態を保持する変数:i
- 途中の計算状態:s = i * (i + 1) * ... * n
- 最終状態:i = 0
int factorial (int n) {
int s = 1, i = n;
while (i > 0) {
s = s * i;
i = i - 1;
}
return s;
}
- 1からnまでの総和を求める
- 計算結果を持つ変数:s
- 計算の状態を保持する変数:i
- 途中の計算状態:s = i + (i+1) + ... + n
- 最終状態:i = 0
int sum (int n) {
int s = 0, i = n;
while (i > 0) {
s = s + i;
i = i - 1;
}
return s;
}
- 1からnまでの整数の2乗の和を求める
- 計算結果を持つ変数:s
- 計算の状態を保持する変数:i
- 途中の計算状態:s = i*1 + (i+1)*(i+1) + ... + n * n
- 最終状態:i = 0
int sigma2 (int n) {
int s = 0, i = n;
while (i > 0) {
s = s + i*i;
i = i - 1;
}
return s;
}
- 1からnまでの整数の3乗の和を求める
- 計算結果を持つ変数:s
- 計算の状態を保持する変数:i
- 途中の計算状態:s = i * i * i + (i+1) * (i+1) * (i+1) + ... + n*n*n
- 最終状態:i = 0
int sigma3 (int n) {
int s = 0, i = n;
while (i > 0) {
s = s + i*i*i;
i = i - 1;
}
return s;
}
- xとyの積を足し算を使って計算する(積はyをx回足したもの)
- 計算結果を持つ変数:s
- 計算の状態を保持する変数:i
- 途中の計算状態:s = xをi回足した結果
- 最終状態:i = x
int times (int x, int y) {
int s = 0, i = x;
while (i > 0) {
s = s + y;
i = i - 1;
}
return s;
}
- 与えられた数nをk乗する.
- 計算結果を持つ変数:s
- 計算の状態を保持する変数:i
- 途中の計算状態:s = nの(k - i)乗
- 最終状態:i > n
int power (int n, int k) {
int s = 1, i = k;
while (i > 0) {
s = s * n;
i = i - 1;
}
return s;
}
- 初項A(0)が1公差が2の等差数列のn番目の項A(n).(注)
- 計算結果を持つ変数:s
- 計算の状態を保持する変数:i
- 途中の計算状態:s = i番目の項
- 最終状態:i > n
int A (int n) {
int s = 1, i = 1;
while (i <= n) {
s = s + 2;
i = i + 1;
}
return s;
}
- 初項B(0)が1公比が2の等比数列のn番目の項B(n).
- 計算結果を持つ変数:s
- 計算の状態を保持する変数:i
- 途中の計算状態:s = i番目の項
- 最終状態:i > n
int B (int n) {
int s = 1, i = 1;
while (i <= n) {
s = s * 2;
i = i + 1;
}
return s;
}
- 初項C(0) = 1でC(n) = a + b * C(n-1)なる漸化式の一般公
- 計算結果を持つ変数:s
- 計算の状態を保持する変数:i
- 途中の計算状態:s = i番目の項
- 最終状態:i > n
int C (int n) {
int s = 1, i = 1;
while (i <= n) {
s = 2 + 3 * s;
i = i + 1;
}
return s;
}
テスト
これら関数を,これらを呼び出す以下のようなmain関数とともにファイルloops.cに定義してテストしてみよう.
main() {
printf("factorial(10):%d\n", factorial(10));
printf("sum(10):%d\n", sum(10));
printf("sigma2(10):%d\n", sigma2(10));
printf("sigma3(10):%d\n", sigma3(10));
printf("times(2,3):%d\n", times(2,3));
printf("power(2,3):%d\n", power(2,3));
printf("A(10):%d\n", A(10));
printf("B(10):%d\n", B(10));
printf("C(10):%d\n", C(10));
}
コンパイル実行すると以下のような結果が得られるはずである.
C:\SMLSharpAndC\c>gcc loops.c gcc loops.c C:\SMLSharpAndC\c>a.exe a.exe factorial(10):3628800 sum(10):55 sigma2(10):385 sigma3(10):3025 times(2,3):6 power(2,3):8 A(10):21 B(10):1024 C(10):118097
Keyword(s):
References:[ML&Cチュートリアル 目次] [(7)[C] 関数定義] [(10-1)[ML] 補足:末尾再帰をマスターする] [(9-1)[ML] 補足:再帰的関数の考え方]