日の出前に歩くときは懐中電灯を2つ持ち歩きます。それぞれ約 5 時間のバッテリーを持ち、歩行は約 1 時間続きます。 私は懐中電灯が切れるまで同じものを使うようにしており、約5日ごとに散歩中にライトを切り替えるようにしています。 古いライトが充電されるので、翌日どれを使用するかを思い出そうとします。 この計画は、たとえ翌日照明を切り替えるのを忘れたとしても、実際にはかなりうまくいきます。
しかし、毎日懐中電灯をランダムに選んだ場合はどうなるでしょうか? 15 分間のライトが 2 回点灯し、散歩の後半にはライトが点灯しないことがどれくらいの頻度で発生すると予想されますか? 添付の JSL は、質問に答えようとするシミュレーションです。まず答えを見てみましょう:
The red dots below 0 show where the two flashlights both failed, and how badlyThe red dots below 0 show where the two flashlights both failed, and how badlyThe red dots below 0 show where the two flashlights both failed, and how badlyThe red dots below 0 show where the two flashlights both failed, and how badly0 の下の赤い点は、2 つの懐中電灯が両方とも故障した場所とその程度を示しています。
タイトル バーには、このシミュレーションが 2 つのライト (1 つはバッテリーが 4 時間以上、もう 1 つは 5 時間以上) を対象としたことが示されています。 ほとんどの日は、ペアのライトが数時間残っています。 JSL はユーザー関数から始まります。メイク可能, コードは以下で別のグラフを作成するために再利用されるためです。
maketable = function({capacity},// an array with the battery capacities expressed as time
{dt,charge,ontime,today,remainingWalkTime,message,light,usage,deadBatteries},//locals
最初の { 中括弧 } のセットは関数へのパラメータを保持します。容量これは、各懐中電灯の時間容量を含む小さな行列になります。 2 番目のセットには、関数のローカル変数が保持されます。関数を呼び出す JSL がたまたま同じ変数名を使用している場合、呼び出し元の変数はこの関数によって変更されません。
charge = capacity; // charge[] tracks the remaining charge
// expression that computes random on time for flashlight
ontime = Expr(
As Date( In Hours( Min( 2, Max( .5, Random Normal( 1, .1 ) ) ) ) ) // usually about 1-hour walk
);
// table to collect results
dt = New Table( "Light Schedule",
Add Rows( 1 ),
New Column( "day", Numeric, "Ordinal", Format( "m/d/y", 10 ), Input Format( "m/d/y" ), Set Values( {1feb2020} ) ),
New Column( "hoursAvailable", Numeric, "Continuous", Format( "hr:m", 12 ), Set Values( {sum(charge)} ) ),
New Column( "notes", Character, Set Values( {"Two fully charged lights"} ), Set Display Width( 500 ) ),
Set Label Columns( :notes )
);
上記では、バッテリーはコピーすることで最初に完全に充電されます。容量の中へ充電。 次に、式が代入されます時間通りに、これは私の歩行時間の分布を表します...多かれ少なかれ 1 時間。 そして最後に、 dtは、この関数が最終的に呼び出し元に返す新しいデータ テーブルに設定されます。 この表には、日数カウンターの列、散歩後に懐中電灯で照らせる時間が何時間あったかを示す列、何が起こったかについてコンピューターで生成されたメモの列が含まれています。
// run the simulation for about 10 years
For( today = 2feb2020, today <= 2dec2029, today += In Days( 1 ),
remainingWalkTime = ontime; // gets a new random duration for the length of an early morning walk
message = Format( remainingWalkTime, "hr:m" ) || " --"; // total walk duration
While( remainingWalkTime > 0 & Sum( charge ) > 0, // also give up when there are no charged lights remaining
// choosing randomly might be too slow if there are many uncharged lights, but there are only 2 or 3...
light = Random Integer( N Rows( capacity ) ); // choose from available lights
If( charge[light] > 0, // if it is a charged light, use it
usage = Min( remainingWalkTime, charge[light] ); // only take what is needed, up to available charge
remainingWalkTime -= usage;
charge[light] -= usage;
message ||= " light " || Char( light ) || "(" || Format( usage, "hr:m" ) || ")";
);
);
上のシミュレーション ループの最初の部分は、2020 年から 2030 年までの期間をループします。 (日付は、グラフにラベルを付ける以外には実際には使用されません。期間はおよそ 10*365 日です。後でテーブルに約 3500 行が表示されます。)残り歩行時間は約 1 時間に設定され、の初期値になります。メッセージ、その日のメモとなるテキストがさらに含まれます。 For ループ内には、散歩の一部として懐中電灯をランダムに選択する While ループがあります。 For ループは、散歩が終了するか、充電が残っている懐中電灯がなくなるまで、懐中電灯を選択し続けます。 選択したライトは使い果たされるか、歩行を完了するのに十分な量が使用されます。 次に、どのライトがどのくらいの時間使用されたかを示すメッセージが更新されます。
// update data table
dt << addrows( 1 );
If( remainingWalkTime > 0,
Row State() = Color State( "red" );
// the next line makes the charge go below zero on the first battery;
// that makes column 1 have negative numbers, and makes the graph do
// a better job of visualizing how long the walk in the dark lasted.
message ||= " walked in the dark(" || Format( remainingWalkTime, "hr:m" ) || ")";
charge[1] -= remainingWalkTime;
remainingWalkTime = 0; // maybe the sun is coming up
);
dt:day = today;
dt:hoursAvailable = sum(charge);
dt:notes = message;
// recharge any dead batteries
deadBatteries = Loc( charge <= 0 );
charge[deadBatteries] = capacity[deadBatteries];
message ||= " recharged" || Char( deadBatteries );
);
上記のシミュレーションの最後の部分では、表に行を追加します。今日があれば赤く色付けします残り歩行時間残ったものを設定します日、時間あり、 そしてノート新しい行の場合。 最後に、バッテリーが再充電されます。のロック関数は、ゼロ以下になった電荷行列内のインデックスまたは位置を見つけます。 その後、それらの場所についてのみ、充電値が容量値にリセットされます。
dt; // return value
);
そして、これで (上記の) 機能が終了します。最後のステートメントの値が呼び出し元に返されます。 dt 、それ自体は、次のように評価される非常に単純なステートメントです。 dt 。 return 関数を使用することもできます。戻り値(dt);関数内のどこでも。
// the following line determines how many flashlights are in the simulation
capacity = matrix({In Hours( 4.3 ),In Hours( 5.6 )}); // one battery is much older, less capacity.
dt = maketable(capacity);
// graph the results
dt << Graph Builder(
Size( 1388, 500 ),
Show Control Panel( 0 ),
Show Legend( 0 ),
Variables( X( :day ), Y( :hoursAvailable ) ),
Elements( Points( X, Y, Legend( 9 ) ) ),
SendToReport(
Dispatch( {}, "Graph Builder", OutlineBox, {Set Title( title="h:m battery capacities: "; for(icap=1,icap<=nRows(capacity),icap+=1,title||=format(capacity[icap],"hr:m")||" ";); title )} ),
Dispatch({},"day",ScaleBox, {Format( "m/d/y", 10 ), Min( 3660681600 ), Max( 3976300800 ), Interval( "Year" ), Inc( 2 ), Minor Ticks( 0 )}),
Dispatch( {}, "graph title", TextEditBox, {Set Text( "Available flashlight time at end of walk" )} ),
Dispatch( {}, "Y title", TextEditBox, {Set Text( "Hours:Minutes" )} ),
Dispatch( {}, "400", LegendBox, {Set Title( "" )} )
)
);
最後に、上記で呼び出しますメイク可能とともに容量これは 2 つのバッテリーを定義します。 のdt = の左側はグローバル変数で、= の右側にある関数の結果が代入されます。 のdt関数内の は関数の先頭のローカル リストにあり、グローバル リストを変更しません。 dt 。 データ テーブルが作成されると、グラフ ビルダーでグラフを作成できます (上記の方法を参照)。グラフ ビルダー スクリプトを最初から作成しないでください。 GUI を使用して必要なものを取得し、スクリプトを保存します。 これを編集して、バッテリー容量を含むカスタム アウトライン タイトルを追加しました。
グラフ ビルダー (上記) が使用したテーブルは次のようになります。
Output from maketable() function showing (negative) hours and notesOutput from maketable() function showing (negative) hours and notesOutput from maketable() function showing (negative) hours and notesOutput from maketable() function showing (negative) hours and notes(負の) 時間とメモを示す maketable() 関数からの出力
maketable() はバッテリーの容量を引数に取るので、複数回実行できます。 追加の懐中電灯を持ち歩くつもりはありませんが、どのくらいの容量の懐中電灯を何個使用すれば問題を解決するのが最適かを知りたいのです。 以下はループから maketable を呼び出し、maketable が返すテーブルから別の回答テーブルを収集する JSL です。
// make a table-of-tables; use from 1 to 10 flashlights with batteries from 1/16 to 32 hours
dtreport = New Table( "lights",
New Column( "nBats", Numeric, "Continuous", Format( "Best", 12 ), Set Values( [] ) ),
New Column( "batSize", Numeric, "Continuous", Format( "Best", 12 ), Set Values( [] ) ),
New Column( "nfailures", Numeric, "Continuous", Format( "Best", 12 ), Set Values( [] ) ),
New Column( "darkHours", Numeric, "Continuous", Format( "Best", 12 ), Set Values( [] ) )
);
dtreport集計された回答を保持するデータ テーブルです。
For( nLights = 1, nLights <= 10, nLights += 1,
For( batLife = 1 / 16, batLife <= 32, batLife *= 2,
dt = maketable( Repeat( inhours(batLife), nLights ) );
上では、1 組のループが 100 の組み合わせを実行し、新しいものを作成します。 dt毎回。
// count the failures and duration of failures
remaining = dt[0, "hoursAvailable"];
failures = Loc( remaining < 0 );
nfail = N Rows( failures );
darkSum = As Date( -Sum( remaining[failures] ) );
dtreport<<ADDROWS></ADDROWS>
その上、残りを掴む時間あり列を行列として使用できるようにするloc()もう一度場所を特定します失敗。失敗の数と失敗の合計時間を試してみたところ、グラフに同様の結果が得られました。 に行を追加します。 dtreport表を作成し、記入してください。
Close( dt, "nosave" );
)
);
必要がなくなったらループ内の dt テーブルを閉じて、100 個のウィンドウが開かれないようにします。
作成したばかりのテーブルは次のようになります。
.0625 hour will probably never be enough, even with 10 flashlights.0625 hour will probably never be enough, even with 10 flashlights.0625 hour will probably never be enough, even with 10 flashlights.0625 hour will probably never be enough, even with 10 flashlights10 個の懐中電灯があっても、0.0625 時間ではおそらく十分ではありません。
より多くのライトとより良いバッテリーが役立つことがわかります。 JSL (グラフビルダーの GUI から保存) は次のようになります。
dtreport<<GRAPH builder=""></GRAPH>
そして得られたグラフは次のとおりです。
More flashlights or more batteries keeps me out of the darkMore flashlights or more batteries keeps me out of the darkMore flashlights or more batteries keeps me out of the darkMore flashlights or more batteries keeps me out of the dark懐中電灯やバッテリーを増やせば暗闇から逃れられる
等高線プロットは 100 回のシミュレーションの結果を示します。 17 時間使用できる懐中電灯が 3 つ、または 8 時間使用できる懐中電灯が 4 つあれば十分だと思われます。 しかし、懐中電灯が 2 つしかなく、ライトをランダムに選択し、切れたときにのみ充電する場合、ライトを点灯し続ける容量がない可能性があります。