日出前行走时,我会携带两个手电筒;每个都有大约 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 小时容量的手电筒就可以解决问题。 但如果只有两个手电筒,随机选择一盏灯,并且只有在没电时才充电,可能没有足够的容量让灯保持亮着。