◆PROCESSING 逆引きリファレンス
カテゴリー:演算処理
乱数を扱うには
【解説】
GAMEなどで敵の出現位置を決めたり、怪物や主人公のパラメータを決める処理で、適切な乱数を発生させることは、とても重要な処理となります。
PROCESSINGでも、擬似乱数を扱うことが可能です。PROCESSINGには、random() と noise() という2種類の乱数発生機能があります。
またPROCESSINGはJavaをベースにした言語であるため、Javaの乱数発生機能を利用することも可能です。
noise() については、「パーリンノイズを扱うには」で説明します。
【構文】
PROCESSINGでrandom命令を使う
●シードを変更します
void randomSeed( int seed ) ;
●疑似乱数を発生させます
float r1 = random( float high ) ;
float r2 = random( float low, float high ) ;
r1には 0.0 から hight まで(hightは含まない)範囲の値が返却されます。
r2には low から hight まで(hightは含まない)範囲の値が返却されます。
JavaでRandomクラスを使う※
●乱数操作用インスタンスを生成します
Random rs = new Random( ) ;
Random rs = new Random( long seed ) ;
●シードを変更します
void rs . setSeed( long seed ) ;
●疑似乱数を取得します
float r3 = rs . nextFloat( ) ;
int r4 = rs . nextInt( int hight ) ;
r3 には、0.0 から 1.0まで(1.0は含まない)範囲の値が返却されます。
r4 には、0 から hight まで(hightは含まない)範囲の値が返却されます。
※代表例のみ掲載しています。詳しくは以下を参照してください。
JavaでMath.randomを使う
●疑似乱数を発生させます
double r5 = Math . random( );
0.0 から 1.0まで(1.0は含まない)範囲の値が返却されます。
【注意】
JavaのRandomクラスやPROCESSINGのrandam()命令では、seedと呼ばれる乱数の「種(たね)」を与えることで、同じ系列の乱数を得ることが可能です。
seedを指定すると、常に同じ順番で同じ値が得られます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import java.util.Random; void setup(){ //JPROCESSINGの乱数。seedを固定 randomSeed( 0 ); for( int i = 0; i < 5; i++){ //(1)0.0 - 1.0 の間で、毎回同じ値が同じ順番で表示される println( random( 1.0f ) ); } println("-----------------------"); //Javaの乱数。seedを固定 Random rs = new Random( 0 ); for( int i = 0; i < 5; i++){ //(2)0.0 - 1.0 の間で、毎回同じ値が同じ順番で表示される println( rs.nextFloat() ); } } void draw(){ } |
上記結果は以下の通りです。
この結果は、プログラムを終了して再起動しても変わりません。なぜなら、seedがいつも一緒(この例では0)だからです。
結果だけを見ると、どうやらPROCESSINGのrandom() 命令は、JavaのRandomクラスと等価なようですね(笑)。
一方で、JavaのRandomクラスをNewする際に引数を省略した場合や、PROCESSINGでrandomSeed() 命令を使わずにいきなりrandom() 命令を使うと、seedは毎回自動的に割り当てられます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import java.util.Random; void setup(){ //PROCESSINGの乱数。seedは「おまかせ」 for( int i = 0; i < 5; i++){ //(3)0.0 - 1.0 の間で、毎回異なる値が表示される println( random( 1.0f ) ); } println("----------------------"); //Javaの乱数。seedは「おまかせ」 Random rn = new Random(); for( int i = 0; i < 5; i++){ //(4)0.0 - 1.0 の間で、毎回異なる値が表示される println( rn.nextFloat() ); } } void draw(){ } |
上記処理では、共に seed を指定していない(おかかせ状態)ので、new Random() される、あるいは random() 命令が実行されるたびに新しい seed が採用され、毎回異なる値が表示されます。
ただしPROCESSINGの random() では、一度 seed を指定すると、これを取り消す事(PROCESSINGにおまかせにする事)ができません。ここは注意ですね。
またJavaのMath.random( ) では seed を指定する事ができません。Math.random( )は、命令が実行される毎に、毎回異なる seed がJavaにより自動的に割り当てられます。
乱数については、ウンチクを「コラム:乱数について」にまとめました。よろしければ参考としてください。
【関連記事】
サンプルプログラム
PROCESSINGのrandomを利用する例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
final int ELLIPSE_SIZE = 10; void setup(){ size(400, 400); smooth(); background(0); for(int x = 0; x < width; x += ELLIPSE_SIZE){ float y = random( height ); ellipse(x, y, ELLIPSE_SIZE, ELLIPSE_SIZE); } } void draw(){ } |
直径10の円を左から右に向かって、順番に描きます。円を描く上下の位置を乱数で取得しています。
JavaのRandomクラスを利用する例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import java.util.Random; final int ELLIPSE_SIZE = 10; void setup(){ size(400, 400); smooth(); background(0); Random rs = new Random(); for(int x = 0; x < width; x += ELLIPSE_SIZE){ float y = rs.nextFloat() * height; ellipse(x, y, ELLIPSE_SIZE, ELLIPSE_SIZE); } } void draw(){ } |
先程の例を、JavaのRandomクラスを使って実装したものです。
JavaのMath.randomを利用する例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import java.lang.Math; final int ELLIPSE_SIZE = 10; void setup(){ size(400, 400); smooth(); background(0); for(int x = 0; x < width; x += ELLIPSE_SIZE){ float y = (float)Math.random() * height; ellipse(x, y, ELLIPSE_SIZE, ELLIPSE_SIZE); } } void draw(){ } |
JavaのMath.randomを利用しています。Math.randomは double 型の値を戻すため、floatにキャストしています。
sfmtライブラリを使う例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import java.time.LocalDateTime; import java.time.temporal.ChronoField; final int ELLIPSE_SIZE = 10; void setup(){ size(400, 400); smooth(); background(0); //ローカル日時取得 LocalDateTime ldt = LocalDateTime.now(); //seed は実行時のミリ秒 Sfmt sfmt = new Sfmt( ldt.get( ChronoField . MILLI_OF_SECOND )); for(int x = 0; x < width; x += ELLIPSE_SIZE){ float y = (float)sfmt.NextUnif() * height; ellipse(x, y, ELLIPSE_SIZE, ELLIPSE_SIZE); } } void draw(){ } |
和田維作のホームページ 様に紹介されている Sfmt.java が組み込まれている事が前提です。
プログラム実行時のミリセカンドを seedとして与えています。
コラム:乱数について
乱数はとても奥が深い要素です。コンピュータで、完全にバラツキのある予測不能な値を返すのは、とても難しいようです。
上記で挙げた命令はすべて、「擬似乱数(乱数のように見える値)」を返すものになります。
JavaのMath . random( )で発生させる乱数、JavaのRandomクラスを利用した乱数、PROCESSINGのrandom()命令で発生させる乱数は、すべて線形合同法と呼ばれる手法で乱数を発生させています。
このため、値に偏りがあり、同じ数値が現れるまでの周期が短いなどの欠点を持っているため、厳密な用途で使う(統計処理、科学技術計算、暗号化処理など)には向きません。
特に、JavaのRandomクラスとPROCESSINGのrandom()命令については、連続して出る値にかなりの偏りがあるため、バラバラである事が重要な意味をもつゲームなどでも、利用は避けたほうが良いでしょう。
じゃぁ、Javaで精度の高いバラツキがある乱数が欲しい時はどーするのさ?・・・という事になりますが、真面目にやるならメルセンヌ・ツイスタ法、または XorShift 法を使うのがオススメです。
ゲームなどライトな用途で使う場合でも、標準のrandom命令よりは、Math.randomを使うほうがマシでしょう(汗)。
また暗号化処理などに使うのであれば、java.security.SecureRandom クラスを用いるのが良いと言われています。
参考までに、参照させていただいたサイト様を紹介しておきます。
●乱数全般について
●メルセンヌ・ツイスタ法
メルセンヌ・ツイスタ法とXorShift 法については、各言語から利用できるソースプログラムが、上記「和田維作のホームページ 様」に紹介されています。大変助かります。ありがとうございます。
上記で紹介されていた メルセンヌ・ツイスタ法を用いた SfmtライブラリとXorShift ライブラリを試してみましたが、PROCESSINGでもちゃんと動作しました(笑)。
PROCESSINGでSfmtライブラリ等を使う方法については、「コラム:Sfmtライブラリを使う」を参照してください。
コラム:Sfmtライブラリを使う
(1)まずライブラリをダウンロードします。
「和田維作のホームページ 様」に行き、smft.zip :ライブラリ一式をダウンロードします。
解凍すると SfmtJAVA というフォルダに Sfmt.java というソースコードがありますので、これをエディタで開いて全行コピーします。
(2)PROCESSINGで新規クラスを作成します。
PROCESSINGの標準エディタを開き、スケッチ名のタブの右横にある▼をクリックします。メニューが開くので「新規タブ」を選択します。
タブ名を入力する窓が表示されるので、好きな名前(例:Sfmt)を入力し、OKボタンを押します。
(3)できたタブにSfmt.java の内容を貼り付けます。
本ページで利用しているアイコン画像は、下記サイト様より拝借しております。各画像の著作権は、それぞれのサイト様および作者にあります。