HDS#08 乱数の生成(シミュレーション向け)

 

こんにちは、開発Gの杉原です。

 

 

4月も半ばに差し掛かり、ちょっと今更な話題ではありますが、弊社にも新入社員がやってまいりました。

まだ研修中のため、まだ一緒に仕事はしていないので、どんな人たちが入ってきたのかとても気になりますね。

 

さて今回の【How to Demo3D Scripting】は「乱数の生成(シミュレーション向け)」をお送りします。

今回はDemo3Dというよりは、.NET Frameworkのお話になるので、新人プログラマー向けな内容でもあります。

Demo3DにはMicrosoftの.NET Framworkが利用されています。

そのため、スクリプトを作成する際には、それがJScript、C#どちらかにかかわらず、.NET Frameworkの機能を使用することができるようになっています(ただし全てではないのですが、基本的なものはおおよそ使えると思います)。

 

.NET Frameworkにも乱数を生成するためのクラスが存在します。

それがSystem名前空間にあるRandomクラスです。

var rnd : System.Random = new System.Random(1);
var value = rnd.Next(10); // 0~9の範囲での乱数値を生成します
print(value); // 生成された値を出力
System.Random rnd = new System.Random(1);
int value = rnd.Next(10);  // 0~9の範囲での乱数値を生成します
print(value.ToString()); // 生成された値を出力

 

最初にRandom型のインスタンスを生成する以外は、前回C#用のスクリプトで紹介した「app.Document.Random」に似ていますね。

 

ではこのRandomクラスを使って、前回作成した「モデルを実行したときに10回乱数を生成して、メッセージログに出力するスクリプト」と同じものを作ってみます。

function Box1_OnInitialize( sender : Demo3D.Visuals.BoxVisual )
{
    var rnd : System.Random = new System.Random(1);
    for(var cnt = 1; cnt <= 10; cnt++)
    {
        print(cnt +"回目:" + rnd.Next(10));
    }
}
[Auto]
void OnInitialize(Visual sender){
    System.Random rnd = new System.Random(1);
    for(int cnt = 1; cnt <= 10; cnt++)
    {
        print(cnt + "回目:" + rnd.Next(10));
    }
}

 

ではこのモデルを一度実行してログを確認し、リセットしてもう一度ログを確認してみます。

結果は上記のようになりました。

 

どちらも同じ結果ですね。

 

確かにこの挙動はデモンストレーションならいいのですが、今回はシミュレーション向けに実行する値を変えたいわけです。

 

ではどうするかというと、Randomクラスのインスタンスを作るときの引数に注目してください。

先ほどからサンプルプログラムでは引数に「1」という数値を指定していました。

これについて特に何も説明はしませんでしたが、まずは試しにこの値を変えてみてください。

依然同じ結果を出力し続けますが、「1」を指定したときと結果が変わっているかと思います。

 

引数に指定した値は「シード」といって、このシードが同じ値であれば、実行するたびに同じ乱数パターンを生成し続けるのです。

これが実行するたびに同じ結果となる理由です。

 

そして実はRandomクラスのコンストラクタには「引数無し」のパターンも存在します。

そこで、次のようにRandomクラスのコンストラクタに引数を指定しないプログラムに変更してみます。

function Box1_OnInitialize( sender : Demo3D.Visuals.BoxVisual )
{
    var rnd : System.Random = new System.Random(); // 引数を指定しない
    for(var cnt = 1; cnt <= 10; cnt++)
    {
        print(cnt +"回目:" + rnd.Next(10));
    }
}
[Auto]
void OnInitialize(Visual sender){
    System.Random rnd = new System.Random(); // 引数を指定しない
    for(int cnt = 1; cnt <= 10; cnt++)
    {
        print(cnt + "回目:" + rnd.Next(10));
    }
}

 

実行した結果が下図になります。

1度目の実行と2度目の実行で結果が異なるのが確認できました。

 

これはなぜかというと、引数を指定せずに生成されたRandomクラスのインスタンスは、PCが起動されてからの経過時間をシード値として使用するためです。

注意したい点としては、この経過時間は「ミリ秒」単位なので、1ミリ秒未満のタイミングで2つの引数無しRandomインスタンスが生成された場合、2つのインスタンスは同じ結果を出し続けるということです。

それを確認したサンプルスクリプトと結果が次になります。

function Box1_OnInitialize( sender : Demo3D.Visuals.BoxVisual )
{
    var rnd1 : System.Random = new System.Random();
    var rnd2 : System.Random = new System.Random();
    for(var cnt = 1; cnt <= 10; cnt++)
    {
        print("乱数1 " + cnt +"回目:" + rnd1.Next(10));
        print("乱数2 " + cnt +"回目:" + rnd2.Next(10));
    }
}
[Auto]
void OnInitialize(Visual sender){
    System.Random rnd1 = new System.Random();
    System.Random rnd2 = new System.Random();
    for(int cnt = 1; cnt <= 10; cnt++)
    {
        print("乱数1 " + cnt +"回目:" + rnd1.Next(10));
        print("乱数2 " + cnt +"回目:" + rnd2.Next(10));
    }
}

この問題を解決するには、2つのRandomインスタンスのシードが違う値になるようにする必要があります。

しかしシードを明示的に指定すると、実行するたびに同じ結果が得られてしまいます。

 

どうすればいいかというと、シードを乱数で作る方法が考えられるかと思います。

function Box1_OnInitialize( sender : Demo3D.Visuals.BoxVisual )
{
    var rnd0 : System.Random = new System.Random();

    var rnd1 : System.Random = new System.Random(rnd0.Next());
    var rnd2 : System.Random = new System.Random(rnd0.Next());
    for(var cnt = 1; cnt <= 10; cnt++)
    {
        print("乱数1 " + cnt +"回目:" + rnd1.Next(10));
        print("乱数2 " + cnt +"回目:" + rnd2.Next(10));
    }
}
[Auto]
void OnInitialize(Visual sender){
    System.Random rnd0 = new System.Random();

    System.Random rnd1 = new System.Random(rnd0.Next());
    System.Random rnd2 = new System.Random(rnd0.Next());
    for(int cnt = 1; cnt <= 10; cnt++)
    {
        print("乱数1 " + cnt +"回目:" + rnd1.Next(10));
        print("乱数2 " + cnt +"回目:" + rnd2.Next(10));
    }
}

まずはシード値なしのRandomインスタンスを作成します。

そしてこのインスタンスから乱数を取得し、それをシードとして2つのインスタンスを作成しています。

 

今までNextメソッドに引数を指定していましたが、指定しない場合はint型の最大値までの乱数値を作成します。

そのため、2つのシード値に使われる乱数が重複する可能性は低いとは思いますが、可能性は0ではないことは覚えておいてください。

 

さて、これを実行すると次のようになりました。

これならばシミュレーション用途に使っても問題ないのではないでしょうか?

 

 

さて、2週にわたって乱数の話をしてきましたが、「今月は乱数」と前回言ってみたものの、そんな使う機会のないものを長々と話すネタもないわけで。

軽はずみに「月ごとにテーマ決めてやっていこう」と思ったのが浅はかでしたね。

 

ということで、次回からはよく使う「Transfer」についてご紹介していこうと考えています。

このTransferはDemo3Dが「物流」を表現するうえで必要不可欠な存在であり、ほとんどのVisualがこれを使ってワークを搬送しています。

Transferを制したものがDemo3Dを制するといっても過言ではないのかもしれません。

そのため、長きにわたってTransferについてのお話になるかと思いますが、ぜひお付き合いのほどよろしくお願いします。


 

ということで次回の【How to Demo3D Scripting】は「Transferの概要」をお送りします。

更新は4/19(木)、ご期待ください。