1. の方がプログラミングは楽ですが、 取り直しが必要なので少し無駄なような気がしますし、 選択肢が少なくなってくると取り直しをする回数が増えてきて、 いつ取り直しが終わるかわからないとか、 何回で終わるかの正確な評価ができない、という欠点があります。
2. は、例えば以下のようにすれば実現できます。
乱数が一様乱数であると仮定すれば、 最初の数字は 個の数字からそれぞれ の確率で選び、 次の数字は残りの 個の数字からそれぞれ の確率で選び、 ということになります。 これですべての数字列が確実に等確率で得られることになります。
上記の方法 (サブルーチン) は、以下のようにコード化されます。
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 文を入れておくと 便利です。
「こうなるはずだ」とコーディングしていくと、 少しのミスで無限ループやシステムエラーを引き起こしがちですが、 このように「万が一エラーが起きた場合」のエラー処理のコードを入れておくことで それを防ぐことができます。
こういう例外処理を一つ一つ埋めておくことが、 「ちゃんとした」コードを作るコツの一つです。