Llevo dos linternas cuando camino antes del amanecer; cada uno tiene una batería de aproximadamente 5 horas y la caminata dura aproximadamente una hora. Intento usar la misma linterna hasta que se agota y aproximadamente cada cinco días puedo cambiar las luces durante la caminata. La vieja luz se enciende y trato de recordar cuál usar al día siguiente. En realidad, este plan funciona bastante bien, incluso si me olvido de cambiar las luces al día siguiente.
Pero, ¿qué pasaría si eligiera al azar una linterna cada día? ¿Con qué frecuencia esperaría tener dos luces de 15 minutos y ninguna luz durante la segunda mitad de la caminata? El JSL adjunto es una simulación que intenta responder a la pregunta; primero veamos la respuesta:
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 badlyLos puntos rojos debajo de 0 muestran dónde fallaron las dos linternas y en qué medida
La barra de título dice que esta simulación fue para dos luces, una con una batería de más de 4 horas y la otra con una batería de más de 5 horas. La mayoría de los días quedan varias horas en el par de luces. El JSL comienza con una función de usuario, hacer mesa , porque el código se reutilizará a continuación para hacer otro gráfico.
maketable = function({capacity},// an array with the battery capacities expressed as time
{dt,charge,ontime,today,remainingWalkTime,message,light,usage,deadBatteries},//locals
El primer conjunto de {llaves} contiene el parámetro de la función, capacidad , que será una pequeña matriz con la capacidad horaria de cada linterna. El segundo conjunto contiene las variables locales de la función; Si el JSL que llama a la función utiliza los mismos nombres de variables, esta función no cambiará las variables de la persona que llama.
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 )
);
Arriba, las baterías inicialmente se cargan completamente copiando capacidad en cargar . Luego se asigna una expresión a a tiempo , que representa la distribución de mis tiempos de caminata... una hora, más o menos. Y finalmente, dt está configurado en una nueva tabla de datos que esta función eventualmente devolverá a la persona que llama. La tabla incluye una columna de contador de días, una columna que muestra cuántas horas estuvieron disponibles en las linternas después de la caminata y una columna con notas generadas por computadora sobre lo sucedido.
// 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" ) || ")";
);
);
La primera parte del ciclo de simulación, arriba, recorre los días de 2020 a 2030. (Las fechas realmente no se usan excepto para etiquetar el gráfico; la duración es aproximadamente 10*365 días... verá alrededor de 3500 filas en la tabla más adelante). tiempo restante para caminar se establece en aproximadamente una hora y se convierte en el valor inicial para mensaje , que tendrá algo más de texto que se convertirá en las notas del día. Dentro del bucle For, hay un bucle While que elige aleatoriamente una linterna para parte de la caminata. El bucle For continúa eligiendo linternas hasta que termina la caminata o no quedan linternas con carga restante. La luz elegida se agota o se usa lo suficiente para terminar la caminata. Luego, el mensaje se actualiza para indicar qué luz se utilizó y durante cuánto tiempo.
// 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 );
);
La última parte de la simulación, arriba, agrega una fila a la tabla para representar hoy , lo colorea de rojo si hubiera tiempo restante para caminar sobrante y establece el día , horasDisponible , y notas para la nueva fila. Finalmente, se recargan las baterías; el loc La función encuentra los índices, o ubicaciones, en la matriz de carga que estuvieron por debajo de cero. Luego, solo para esas ubicaciones, el valor del cargo se restablece al valor de capacidad.
dt; // return value
);
Y eso (arriba) finaliza la función; El valor de la última declaración se devuelve a la persona que llama. dt , por sí solo, es una declaración realmente simple que se evalúa como... dt . También puedes utilizar la función de retorno: retorno(dt); en cualquier parte de la función.
// 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( "" )} )
)
);
Finalmente, arriba, llama hacer mesa con un capacidad que define dos baterías. El dt a la izquierda de = hay una variable global, a la que se le asigna el resultado de la función a la derecha de =. El dt en la función está en la lista local al comienzo de la función y no cambia la lista global dt . Una vez creada la tabla de datos, el creador de gráficos puede crear el gráfico (muy arriba). ¡No escriba un script de creación de gráficos desde cero! Utilice la interfaz gráfica de usuario para obtener lo que desea y luego guarde el script. Edité este para agregar el título del esquema personalizado con las capacidades de la batería.
La tabla que utilizó el creador de gráficos (arriba) se ve así:
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 notesSalida de la función maketable() que muestra horas y notas (negativas)
Dado que maketable() toma un argumento para la capacidad de las baterías, puedo ejecutarlo varias veces. Realmente no planeo llevar linternas adicionales, pero sí quiero saber cuántas linternas, de qué capacidad, resuelven mejor el problema. Aquí hay algo de JSL que llama a maketable desde un bucle y recopila otra tabla de respuestas de las tablas que devuelve 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( [] ) )
);
dtreportar es la tabla de datos que contendrá las respuestas agregadas.
For( nLights = 1, nLights <= 10, nLights += 1,
For( batLife = 1 / 16, batLife <= 32, batLife *= 2,
dt = maketable( Repeat( inhours(batLife), nLights ) );
Arriba, un par de bucles recorrerán 100 combinaciones, creando una nueva dt cada vez.
// 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>
Arriba, restante agarra el horasDisponible columna como una matriz para que pueda usar ubicación() nuevamente para ubicar el fracasos.Probé el número de fallas y el tiempo total de las fallas y obtuve resultados similares en el gráfico. Añade una fila al dtreportar tabla y complétala.
Close( dt, "nosave" );
)
);
Cierre las tablas dt en el bucle cuando ya no las necesite para que no haya 100 ventanas abiertas.
Ahora la tabla recién creada se ve así:
.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 flashlights0,0625 horas probablemente nunca serán suficientes, ni siquiera con 10 linternas
Puedo ver que más luces y mejores baterías ayudan. El JSL (guardado desde la interfaz gráfica de usuario del generador de gráficos) se ve así
dtreport<<GRAPH builder=""></GRAPH>
Y el gráfico resultante:
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 darkMás linternas o más baterías me mantienen alejado de la oscuridad
El gráfico de contorno muestra los resultados de las 100 simulaciones; Parece que tres linternas con capacidad de 17 horas, o cuatro con capacidad de 8 horas, podrían ser la solución. Pero con solo dos linternas, eligiendo una luz al azar y cargándola solo cuando está apagada, es posible que no haya una capacidad que mantenga las luces encendidas.