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 文を入れておくと 便利です。
「こうなるはずだ」とコーディングしていくと、 少しのミスで無限ループやシステムエラーを引き起こしがちですが、 このように「万が一エラーが起きた場合」のエラー処理のコードを入れておくことで それを防ぐことができます。
こういう例外処理を一つ一つ埋めておくことが、 「ちゃんとした」コードを作るコツの一つです。