はじめに
シミュレーションとは、実験を行うために、研究対象の実際のシステムをモデルに置き換える方法です。 このようなモデルでは、個々の状況とそれらの多くの両方を失う可能性があります。 収集された統計は、システム内のプロセスについて結論を導き、最適化の方法を概説するのに役立ちます。
シミュレーションは多くの場合、一種の実験的テストと見なされますが、同時に安価であるため、パラメーターをすばやく変更し、シミュレーションされたシステムをダイナミクスで観察できます。
約半世紀の間、コンピューターモデルがシミュレーションで使用されてきました。 多くの異なるプログラムとフレームワークが開発用に作成されていますが、その中で最も開発されているのは、キューイングシステム(QS)をモデリングするためのツールです。 QS-GPSS World(汎用シミュレーションシステム-汎用モデリングシステム)のシミュレーション用の最もよく知られたシンプルなプログラムの1つは、リンク[1] 、 [2]で詳細に見つけることができます。
このプログラムの概念は、Goのシミュレーションフレームワークの基礎でした。
GPSSのシミュレーション
GPSSのモデルは、トランザクションが移動するシミュレートされたオブジェクトを記述する一連のブロック(コマンド)です。 トランザクションがブロックに入ると、モデル化されたオブジェクトの状態の変化、またはトランザクションの状態/パラメーターの変化につながるイベントが生成されます。
10個のオーダーのメインブロック:GENERATE、TERMINATE、ASSIGN、SEIZE、RELEASE、QUEUE、ADVANCE、DEPART、START。 合計で約30個のブロックがあります。 ブロックにはパラメーターがあります。パラメーターには、数値、関数名、シミュレーションプログラムのラベル、変数名を指定できます。 ブロックの詳細については、たとえばこちらをご覧ください 。
GPSSのオブジェクトには、標準の数値属性(NAV)と標準の論理属性(ALS)のセットがあります。 たとえば、キューの場合、NAVの1つは現在の長さであり、一部の機器のALSの例は、空き(TRUE)または占有(FALSE)になります。
GPSSの一部のバージョンでは、モデリングプロセスの視覚化が行われますが、ほとんどの場合、存在しません。 シミュレーション結果に基づいて、すべてのオブジェクトのNAVとALSを示すレポートがGPSSで生成されます。
Go実装
Goでの実装は、機能がGPSSブロックに類似した一連のオブジェクトの開発です。 1つ目はパイプライン-シミュレーションが実行されるオブジェクトです。
シミュレートされたシステムを記述するためのコンポーネントのリストを含む
map
基づきます。 シミュレーション中、トランザクションは厳密な順序で一連のブロックを通過する必要があるため、コンポーネントを
Append
に追加する方法では、トランザクションのアドレスを同時に示す追加手順を実装しました。 コンポーネントの名前は
map
キーとして使用されるため、各コンポーネントには一意の名前が必要です。
すべてのコンポーネントを追加した後、
Start
メソッドを使用してシミュレーションを開始できます。 その内部では、すべてのコンポーネントのサイクリックバイパスが所定のシミュレーション時間に実装されます。 シミュレーションの最後に、NAVとALSを含むレポートを印刷できます。
2番目の重要な要素は、シミュレーションを記述するための実際のコンポーネントです。 実装された:Generator-トランザクションを生成し、Advance-トランザクションのパスに遅延を作成する もちろん、このようなセットは複雑なシミュレーションモデルを作成するのに十分ではありませんが、ソリューションを作成してGPSSの結果と比較するには十分です。 すべてのコンポーネントは、IBaseObjインターフェイスを実装します。これは、最低限必要な機能をカバーしています。
各コンポーネントにはトランザクションキューがあります。 直接キューとして使用され、キューでのみ使用されます。他のコンポーネントの場合は単なるリポジトリです。 キューは
map
基づいて実装され
map
。 モデリングプロセスでは、コンポーネントは順番に渡され、次のコンポーネントに送信するためのトランザクションの準備状況(特定の条件の履行)を確認します。 転送は、次のコンポーネントの
AppendTransact
メソッドを介して実行されます。 転送が成功すると、トランザクションはそれぞれキューから削除され、次のコンポーネントが順番にそれを取得します。 各コンポーネントには複数の受信者が定義されているため、ある受信者にトランザクションを送信できなかった場合は、別の受信者に送信しようとします。
トランザクションの出現時間を決定し、遅延を作成するときにランダム変数を生成するには、GoのPRNG関数を使用します。
同時にモデリングを行うと、異なるコンポーネント間で多くのトランザクションが移動する可能性があるため、コンポーネント内でゴルーチンを使用するというアイデアが生まれました。 パイプラインは、コンポーネントを通過して、各コンポーネントの
HandleTransacts
ハンドラーを開始し、その内部にゴルーチンが作成されます。 すべてのゴルーチンが完了すると、モデル時間カウンターがインクリメントされ、
HandleTransacts
が
HandleTransacts
呼び出さ
HandleTransacts
ます。
最後のキーオブジェクトはトランザクション自体です。 彼には、識別子、出生時刻、所有者(現在の構成要素)、SCAおよびSCHLを計算するための多数のパラメーターがあります。
図 図1は、モデリング中のフレームワークの主要なオブジェクトの相互作用の構造図です。
図 1.シミュレーションにおける主要なオブジェクトの相互作用の一般化された構造図
シミュレーション例
美容師の仕事をシミュレートする必要があるとします。 これはGPSSの有名な例です。 訪問者は18±6分の頻度でランダムに行きますが、その数は事前にはわかりません。 美容師が1人いて、彼は16±4分を散髪に費やしています。 それで、彼は1営業日に何人の人をカットしますか? 何人並んでいますか? 散髪の平均時間はどれくらいですか? 多くの質問と簡単なシミュレーション。 図のブロック図 2。
図 2.美容師のモデリングの構造図
モデルを構築するためのコードは次のとおりです。
barbershop := NewPipeline("Barbershop", true) // clients := NewGenerator("Clients", 18, 6, 0, 0, nil) // chairs := NewQueue("Chairs") // master := NewFacility("Master", 16, 4) // hole := NewHole("Out") // barbershop.Append(clients, chairs) // barbershop.Append(chairs, master) // barbershop.Append(master, hole) // barbershop.Append(hole) // barbershop.Start(480) // <-barbershop.Done // barbershop.PrintReport()
シミュレーション結果はここにあります。
パイプライン名「理髪店」
シミュレーション時間480
オブジェクト名「Chairs」
最大コンテンツ1
総エントリー数26
ゼロエントリ11
ゼロエントリの永続化42.31%
キュー0
平均時間/トランス2.58
エントリなしの平均時間/トランス4.47
オブジェクト名「クライアント」
生成された26
オブジェクト名「マスター」
平均アドバンス16.46
平均使用率89.17
番号入力26.00
施設内のトランザクション26
オブジェクト名「Out」
キル25
平均アドバンス16.56
平均寿命19.44
シミュレーション時間480
オブジェクト名「Chairs」
最大コンテンツ1
総エントリー数26
ゼロエントリ11
ゼロエントリの永続化42.31%
キュー0
平均時間/トランス2.58
エントリなしの平均時間/トランス4.47
オブジェクト名「クライアント」
生成された26
オブジェクト名「マスター」
平均アドバンス16.46
平均使用率89.17
番号入力26.00
施設内のトランザクション26
オブジェクト名「Out」
キル25
平均アドバンス16.56
平均寿命19.44
25人の顧客にサービスを提供し、シミュレーションの完了時の26日目はまだマスターの椅子にいました。 キューは1人以下で、11人は待機せず(ゼロパス)、すぐにヘアカットを行いました。 平均して、人々はキューで2.58分を費やし、(ゼロパスではなく)待っていた人の4.47分を費やしました。 彼の時間の89.17%、美容師は集中的に剪断されました。
もちろん、別のシミュレーションを実行すると、結果は変わります。 しかし、一連のシミュレーション中に、マスターの負荷の全体的なレベルとサービスを受けるクライアントの数が表示されます。 GPSSでの同様のシミュレーションでも同様の結果が得られます。
別の例。 オフィスがあり、10人の従業員と1つのトイレがあります。 人々は90±60分ごとにトイレに行き、5±3分間トイレに行き、15±10分間トイレに行き、5±3分間オフィスに戻りたいと考えています。 図7で、9時間(8時間の作業+ 1時間の昼食)のシミュレーションを行います。 図3にブロック図を示します。
図 3.トイレ占有のモデルの構造図:左に1つのトイレ、右に2つのトイレ
左側に1つのトイレがあるモデル、右側に2つのトイレがあります。 以下はモデルコードです。
waterclosetsim := NewPipeline("Water Closet Simulation", false) office := NewGenerator("Office", 0, 0, 0, 10, nil) wantToToilet:= NewAdvance("Wanted to use the toilet", 90, 60) pathToWC := NewAdvance("Path to WC", 5, 3) queue := NewQueue("Queue to the WC") pathFromWC := NewAdvance("Path from WC", 5, 3) wc := NewFacility("WC", 15, 10) pathToOffice:= NewAdvance("Path from WC", 5, 3) waterclosetsim.Append(office, wantToToilet) waterclosetsim.Append(wantToToilet, pathToWC) waterclosetsim.Append(pathToWC, queue) waterclosetsim.Append(queue, wc) waterclosetsim.Append(wc, pathFromWC) waterclosetsim.Append(pathFromWC, wantToToilet) waterclosetsim.Start(540) <-waterclosetsim.Done waterclosetsim.PrintReport()
シミュレーション結果は次のとおりです。
パイプライン名「Water Closet Simulation」
シミュレーション時間540
オブジェクト名「Office」
生成された10
オブジェクト名「WCからのパス」
平均アドバンス5.77
オブジェクト名「WCへのパス」
平均アドバンス5.22
オブジェクト名「WCのキュー」
最大コンテンツ4
総エントリー数36
エントリーゼロ8
ゼロエントリの永続化22.22%
現在の内容4
平均コンテンツ1.78
平均時間/トランス24.11
エントリなしの平均時間/トランス31.00
オブジェクト名「WC」
平均アドバンス14.69
平均使用率87.04
番号入力32.00
施設内の取引2
オブジェクト名「トイレを使いたい」
平均アドバンス95.85
シミュレーション時間540
オブジェクト名「Office」
生成された10
オブジェクト名「WCからのパス」
平均アドバンス5.77
オブジェクト名「WCへのパス」
平均アドバンス5.22
オブジェクト名「WCのキュー」
最大コンテンツ4
総エントリー数36
エントリーゼロ8
ゼロエントリの永続化22.22%
現在の内容4
平均コンテンツ1.78
平均時間/トランス24.11
エントリなしの平均時間/トランス31.00
オブジェクト名「WC」
平均アドバンス14.69
平均使用率87.04
番号入力32.00
施設内の取引2
オブジェクト名「トイレを使いたい」
平均アドバンス95.85
最大4人が並んでおり、1人がすぐにトイレに入ったのは8回で、就業日中は87.04%でトイレが使用されました。 私の意見では、最も重要なことは、人々がトイレに並んで約30分(31分)待つことです。 おそらく、これはトイレが1つしかないという事実と、おそらく平均して14.69分間トイレに座っているという事実によるものです。
別のトイレを追加すると、キューが3人に削減され、29人がすぐにトイレに入ったことがわかりました。 しかし、最も重要なことは、期待がほぼ3倍減少したことです。
おわりに
膝の上に作成されたフレームワークは非常にシンプルであり、まだ制限されています。 GPSSのレベルまで機能を強化する計画。 フレームワークの実用的な価値は、Goでシミュレーションモデルをすばやく簡単に組み立てて結果を取得できることです。
コードはGitHubに投稿されています 。