日出前行走時,我會攜帶兩支手電筒;每個都有大約 5 小時的電池,步行持續約一個小時。 我嘗試使用同一個手電筒,直到它耗盡為止,大約每隔五天我就會在步行過程中切換燈。 舊燈開始充電,我試著記住第二天要使用哪一盞。 這個計劃實際上效果很好,即使我第二天忘記關燈。
但如果我每天隨機選擇一個手電筒會發生什麼事? 我預計多久會有兩次 15 分鐘的燈光,而後半段步行沒有燈光? 所附的 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 以下的紅點顯示兩個手電筒均故障的位置以及嚴重程度
標題列顯示此模擬適用於兩個燈,一個具有 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 notesOutput from maketable() function showing (negative) hours and notesOutput from maketable() function showing (negative) hours and notesOutput 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 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 小時可能永遠不夠,即使有 10 個手電筒
我發現更多的燈和更好的電池會有所幫助。 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 小時容量的手電筒或四個具有 8 小時容量的手電筒就可以解決問題。 但如果只有兩個手電筒,隨機選擇一盞燈,並且只有在沒電時才充電,可能沒有足夠的容量讓燈保持亮著。