プロが教える店舗&オフィスのセキュリティ対策術

Flash Pro CS5
AS3.0
で記述しています。


1~10の整数をランダムかつ重複せずに配列に格納したいと考えています。



そこで,ネット上で参考になるソースを見つけ,
以下のように書き直しました。




var int_a = new Array();
var int_b = new Array();


function RandomInt():void{

//ここだけ変更すればよい
var maxN:Number = 10;//乱数の最大値

//0~maxNの数字を全部配列に入れる
for (var i:int=0; i< maxN; i++) {
int_a[i] = i+1;
}

var j:Number = 0;
var a_length:Number = int_a.length;

//要は配列をシャッフルする
while (a_length) {
var int_r:Number = Math.floor(Math.random()*(maxN+1-j));

//乱発生した整数を配列int_bに順番に入れ、int_aから削除する
int_b[j] = int_a.splice(int_r, 1);
j++;

//配列int_a内の数字が一つずつ減っていく
a_length = int_a.length;
}

//ここで配列int_bがシャッフルされた

trace(int_b);
}


RandomInt();



としました。


しかし出力結果がなぜか
8,2,4,9,,7,6,5,10,3,1のように抜けている部分があり,


次のフレームで
for(var j:int=1; j <= 10; j++){
  trace(int_b[j]);
}


として確認してもやはり
2
4
9
7
6
5
10
3
1

となってしまします。

どの部分がおかしいのか教えていただきたいです。
また,乱数発生の部分で
Math.floor(Math.random()*(maxN+1-j));
という風に記述してあったのですが,ここは間違いではないのでしょうか?
jを引いていくと発生する乱数の範囲が徐々に狭くなっていってしまうと思ったのですが;



それとも元のソースコードを使って
ttp://www.renowan.com/blog/?p=143
0~9までの乱数を発生させてそれぞれに1を足す方が簡単でしょうか?





よろしくお願いします。

A 回答 (1件)

その式ですと、乱数の範囲が、配列変数のインデックス(管理番号)の最大値よりも 1 だけ広くなってしまいます。


RandomInt 関数内の乱数を求める式を

 Math.floor( Math.random() * ( maxN - j ) );

に、変更してみてください。

 
たった 1 の違いが、何が問題なのかと言いますと。
ご提示の式ですと、0 ~ 配列変数の要素の総数、の範囲で乱数を求めることになります。
例えば int_a が 10 個の要素を持っている時、0 ~ 10 までの乱数を求めてランダムに要素を1つ取るとすると、最大の 10 が出た時には int_a[ 10 ] を取ることになります。
しかし、配列変数のインデックスは 0 から始まるので int_a は 0 から 9 番までしかなく、10 番を指定したのでは値の取得に失敗してしまいます。
関数内で、乱数で選んだインデックス(変数 int_r )と int_a の要素の総数を表す a_length の値を監視してみると分かるのですが、値の取得に失敗して「 ,, 」と歯抜けになるのは乱数(= int_r )と a_length が同じになった場合で、運よく1度も同じにならなかった時はこの問題は起こりません。

- - - - -

Math.random で任意の範囲の乱数を発生させる式が、Adobe 社のホームページで紹介されています。

 ・Math.random() でランダムな整数を取得する方法
  http://kb2.adobe.com/jp/cps/228/228622.html

ご参考になさった作例では、この式を利用して配列変数 int_a のインデックスの範囲内で乱数を決めて任意の要素を1つ抜き取り、それを別の配列変数 int_b の先頭から順に詰めていく、といった方法をとっていると思われます。

int_a の要素は最初は変数 maxN と同じ数、ご提示のスクリプトですと 10 個あるのですが、配列変数のインデックスは 0 から始まるので、最後の要素は int_a[ 9 ] です。
ですから、抜き取る要素を決める乱数は 0 ~ 9 の範囲で求めます。最大値の 9 とは、maxN - 1 のことです。
すると、先の式に当てはめますと、

 Math.floor( Math.random() * ( ( maxN - 1 ) - 0 + 1 ) ) + 0

これを整理しますと、

 Math.floor( Math.random() * maxN );

になります。

int_a から splice メソッドで要素を抜き取ると配列変数の要素が少なくなっていきますから、要素が減った分だけ乱数の範囲も狭めていかなければなりません。
抜き取ってなくなった数は、値を抜き取った時にカウントを取っている変数 j を見ると分かります。
合わせますと、int_a の 0 ~最大インデックスの間で乱数を求める式は

 Math.floor( Math.random() * ( maxN - j ) );
 
となります。


* * *

今回の件での変数 maxN とは、初期状態の int_a の要素の総数と同じ意味です。
配列変数の要素の総数は length プロパティに記録されており、要素を削除・追加した時には length プロパティは自動的に修正されます。
この点を利用して、

(↓ 各行頭に全角のスペースが入っています。コピーする際はご注意ください)


//***

 //抜き取った値をint_bに詰める時のインデックス
 var j:Number = 0;

 //int_aの要素が尽きるまでループ
 while( int_a.length )
 {
  //抜き取る要素をランダムに選ぶ
  var int_r:Number = Math.floor( Math.random() * int_a.length );

  //int_aの要素を抜き取り、int_bの先頭から詰めていく
  int_b[ j ] = int_a.splice( int_r , 1 );

  //値を詰めるインデックスを更新
  j++;
 }

//***

と、書くこともできます。


なお、繰り返しますが、配列変数のインデックスは”0 から”始まり、最後は”要素の総数 - 1 ”です。
配列変数 int_b の値を for ループで全部取り出す時は、

 for( i = 0 ; i < int_b.length ; i++ )

というように書きます。
先頭は1番ではありませんので、ご注意ください。
    • good
    • 0
この回答へのお礼

丁寧でわかりやすい解説ありがとうございました。
別の書きかたでのコードも大変参考になりました。

for( i = 0 ; i < int_b.length ; i++ )
の部分ですが,どうしても配列の0番目からではなく1番目~10番目に格納したかったので
そのように記述してしまいました。

しかし0~9番目に入れて後で新しい配列に入れなおせばよかったのですね。

ありがとうございました。

お礼日時:2011/04/14 17:03

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!