演習1 について

返却したレポートには, コメントを書きこんだものがあります. 「☆」がついたコメントは, プログラムに誤りがあることを, 「☆」のないコメントは改善点などを示しています.

よくあるコメントは「2-a」などの記号で示してあります. それぞれの記号の意味を以下に示します.

0-a
レポートには, 手書きでよいから説明をつけることを求めています. プログラムのプリントアウトだけでは不十分です. 説明としては, たとえば以下のようなものがありましょう.

1-a
補助的な変数はあまり使わない方がいいでしょう. ひとつの代入文で済むものはその方がいいと思います. たとえば,
c = (a+1) / 100;
b = c + 1;
よりは
b = (a+1) / 100 + 1;
のほうがシンプルです.

2-a
整数と指定されている値を入れる変数は int 型にしたほうがよい.

☆2-b
素数を求める次のコードを見て下さい. これは二つの点で間違っています.
main()
{
  int n, i, c;

  scanf("%d", &n);
  for(i=2; i<n; i++){
    c = c * (n % i);
  }
  if(c != 0){
    printf("%d is prime\n", n);
  }
}
アイデアとしては, n を順次の整数で割ったあまりを掛け合わせていくことで, ひとつでもゼロが現れたら結果がゼロになる, というもので, 数学的には正しい. しかし,

☆2-c
main()
{
  int n, i, r;

  scanf("%d", &n);
  for(i=2; i<n; i++){
    r = n % i;
    if(r == 0) break;
  }
  if(r != 0){
    printf("%d is prime\n", n);
  }
}
これはほとんどの場合うまく動きますが, n が 2 の場合に限って問題があります. そのとき, for ループは一度も実行されません. したがって, 変数 r は初期値(この場合は不定)のままで最後の if文に到達することに なります. 偶然 r が 0 だった場合には間違った判断になります. ループに入る前に, 「r = 1;」のように適当な初期値を与えておけばOKです.

あるいは, このプログラムでは, 「割り切れたので途中で脱出」と, 「割り切れないまま全回まわって終了」を区別できればいいので, 最後の if の条件として 「i == n」を使えばいいでしょう.

教訓

3-a
整数と指定されている値を入れる変数は int 型にしたほうがよい.

3-b
課題3 レベル3 で, 重複する組合せを取り除くのに, a<=b となるものだけを 選ぶ必要があります. この場合に, a, b, c をすべて 1 から 100 まで変化させて全組合せを試し, そのなかで a<=b となる場合だけ結果を出力するという方法もあります.

しかし, これでは無駄が多いので, ループの繰り返し条件を工夫して,

for(a=1; a<100; a++){
  for(b=a; b<100; b++){
のようにすると, 最初から a<=b となるものしか試さずに済みます.

3-c
課題3 レベル2 では, a, b, c のすべての組合せを試すわけですから, 三重のループになりますが, その初期値は論理的にはすべて 1 からとすべきでしょう. (実際には, a*a+b*b==c*c をみたすのはもうすこし先の値からですが)

3-d
直角三角形の条件を調べるのに, 「c == sqrt(a*a+b*b)」とするよりは, 「c*c==a*a+b*b」のテストの方がシンプルだし, 実数型の精度の問題なども心配せずに済みます.

3-e
課題3 レベル3で, 三重ループのうちの c の初期値として, 1 から始めている ケースがありますが, c*c==a*a+b*b をみたすわけですから, すくなくとも b よりは大きいはずで, 初期値として b+1 くらいを与えてもいいと思います.

3-f
課題3 レベル3で, a<=b というのが重複を除外するための条件になる わけですが, これを a<b としているケースがあります. 実際にはこの問題に限っていえば c*c==a*a+a*a には絶対にならないので, これで問題はありませんが, 一般にすべての組合せを試そうとする場合には 注意しましょう.

prev index next