日出前行走時,我會攜帶兩支手電筒;每個都有大約 5 小時的電池,步行持續約一個小時。 我嘗試使用同一個手電筒,直到它耗盡為止,大約每隔五天我就會在步行過程中切換燈。 舊燈開始充電,我試著記住第二天要使用哪一盞。 這個計劃實際上效果很好,即使我第二天忘記關燈。
但如果我每天隨機選擇一個手電筒會發生什麼事? 我預計多久會有兩次 15 分鐘的燈光,而後半段步行沒有燈光? 所附的 JSL 是一個試圖回答這個問題的模擬;首先我們來看看答案:
The red dots below 0 show where the two flashlights both failed, and how badly0 以下的紅點顯示兩個手電筒均故障的位置以及嚴重程度
標題列顯示此模擬適用於兩個燈,一個具有 4 小時以上的電池,另一個具有 5 小時以上的電池。 大多數日子裡,這對燈還剩下幾個小時。 JSL 以使用者函數開始,製作表,因為下面將重複使用該程式碼來製作另一個圖表。
maketable = function({capacity},// an array with the battery capacities expressed as time
{dt,charge,ontime,today,remainingWalkTime,message,light,usage,deadBatteries},//locals第一組 { 大括號 } 儲存函數的參數,容量,這將是一個小矩陣,其中包含每個手電筒的小時容量。 第二組保存函數的局部變數;如果呼叫函數的 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 )
);上面,電池透過複製最初充滿電容量進入收費。 然後將表達式分配給準時,它代表我步行時間的分佈……一個小時,或多或少。 最後, 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 行。)剩餘步行時間設定為大約一個小時並成為初始值訊息,其中將有更多文本成為當天的筆記。 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 。 您也可以使用返回函數:返回(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( "" )} )
)
);最後,上面調用製作表與一個容量定義了兩個電池。 這dt = 左邊是全域變量,賦值為 = 右邊函數的結果。 這dt在函數中是在函數開頭的局部列表中,並且不會改變全域dt 。 建立資料表後,圖形產生器就可以製作圖形(如上所述)。不要從頭開始編寫圖形產生器腳本! 使用 gui 取得所需內容,然後儲存腳本。 我編輯了這個以添加帶有電池容量的自訂大綱標題。
圖形產生器(上方)所使用的表格如下所示:
Output from maketable() function showing (negative) hours and notesmaketable() 函數的輸出顯示(負)時間與註釋
由於 maketable() 採用電池容量參數,因此我可以多次運行它。 我並不打算攜帶額外的手電筒,但我確實想知道多少個手電筒、多大容量的手電筒最能解決問題。 以下是一些從循環中呼叫 maketable 的 JSL,並從 maketable 傳回的表中收集另一個答案表:
// 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( [] ) )
);
數據報告是將保存聚合答案的資料表。
For( nLights = 1, nLights <= 10, nLights += 1,
For( batLife = 1 / 16, batLife <= 32, batLife *= 2,
dt = maketable( Repeat( inhours(batLife), nLights ) );
上面,一對循環將運行 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>
多於,其餘的抓住了可用時間列作為矩陣,這樣我可以使用洛克()再次定位失敗。我嘗試了失敗次數和失敗總時間,並在圖表中得到了類似的結果。 新增一行到數據報告表並填寫。
Close( dt, "nosave" );
)
);
當不再需要時關閉循環中的 dt 表,這樣就不會開啟 100 個視窗。
現在剛剛建立的表格如下所示:
.0625 hour will probably never be enough, even with 10 flashlights.0625 小時可能永遠不夠,即使有 10 個手電筒
我發現更多的燈和更好的電池會有所幫助。 JSL(從圖形產生器的 GUI 中儲存)看起來像這樣
dtreport<<GRAPH builder=""></GRAPH>
以及結果圖:
More flashlights or more batteries keeps me out of the dark更多手電筒或更多電池讓我遠離黑暗
等高線圖顯示了 100 次模擬的結果;看來三個具有 17 小時容量的手電筒或四個具有 8 小時容量的手電筒就可以解決問題。 但如果只有兩個手電筒,隨機選擇一盞燈,並且只有在沒電時才充電,可能沒有足夠的容量讓燈保持亮著。