次へ: 5 チェック関数 上へ: AWK による数合てゲームの作成 前へ: 3 おおまかな構造 (PDF ファイル: awk3.pdf)


4 課題文の作成

今回も乱数を使って課題を作成しますが、 タイプ練習ソフトと違うのは同じ数字が使えない、という点です。 [2] で紹介した方法で 0 から $(L-1)$ までの乱数を作ることは 容易にできますが、 同じ数字を使わないようにするには、以下のような方法があります。
  1. 新たにランダムにとった数が既に使われていれば、 それを捨ててまた新たな乱数を取り直す
  2. まだ使っていない数字の中からランダムに選ぶ

1. の方がプログラミングは楽ですが、 取り直しが必要なので少し無駄なような気がしますし、 選択肢が少なくなってくると取り直しをする回数が増えてきて、 いつ取り直しが終わるかわからないとか、 何回で終わるかの正確な評価ができない、という欠点があります。

2. は、例えば以下のようにすれば実現できます。

  1. $a[0]=\cdots=a[L-1]=0$ とする
  2. $1\sim L$ の乱数 $x$ を生成し、 $a[j]=0$ であるような $x$ 番目の $j=j_1$ を取り、$a[j_1]=1$ とする。
  3. $1\sim (L-1)$ の乱数 $y$ を生成し、 $a[j]=0$ であるような $y$ 番目の $j=j_2$ を取り、$a[j_2]=1$ とする。
  4. これを $N$ 回繰り返す。
$a[\;]$ が使用した数字かどうかを保存する配列で、 $a[j]=1$ の場合 $j$ は使用した数字、としています。

乱数が一様乱数であると仮定すれば、 最初の数字は $L$ 個の数字からそれぞれ $1/L$ の確率で選び、 次の数字は残りの $(L-1)$ 個の数字からそれぞれ $1/(L-1)$ の確率で選び、 ということになります。 これですべての数字列が確実に等確率で得られることになります。

上記の方法 (サブルーチン) は、以下のようにコード化されます。

  function mkkadai(N,L,kadai,     j,k,x,m,a,L1)
  {
      srand()
      for(j=0;j<L;j++) a[j]=0
      L1=L   # 使っていない数字の個数
      for(j=1;j<=N;j++){
          x=int(rand()*L1)+1   # 1 から L1 までの乱数
          m=0;
          for(k=0;k<L;k++){    # x 番目の使っていない数の検索
              if(a[k]==0) m++
              if(m==x) break
          }
          if(k>=L) return -1   # エラー
          if(a[k]!=0) return -2   # エラー
          a[k]=1
          kadai[j]=k    # j 番目の課題数字を k とする
          L1--
      }
      return 0
  }

なお、このコードの中に、「エラー」と書いた文がありますが、 今回の設計や考え方が正しければこの if 文の条件に合うことはないはずです。 しかし、プログラムの設計段階では誰しも間違いをするものですから、 そのようなときに、そのテスト用、デバッグ用にこのような if 文を入れておくと 便利です。

「こうなるはずだ」とコーディングしていくと、 少しのミスで無限ループやシステムエラーを引き起こしがちですが、 このように「万が一エラーが起きた場合」のエラー処理のコードを入れておくことで それを防ぐことができます。

こういう例外処理を一つ一つ埋めておくことが、 「ちゃんとした」コードを作るコツの一つです。


次へ: 5 チェック関数 上へ: AWK による数合てゲームの作成 前へ: 3 おおまかな構造
竹野茂治@新潟工科大学
2006年6月8日