Je porte deux lampes de poche lorsque je marche avant le lever du soleil ; chacun a environ 5 heures de batterie et la promenade dure environ une heure. J'essaie d'utiliser la même lampe de poche jusqu'à ce qu'elle soit épuisée, et environ tous les cinq jours, j'allume la lumière pendant la promenade. L’ancienne lampe se charge et j’essaie de me rappeler laquelle utiliser le lendemain. Ce plan fonctionne plutôt bien, même si j’oublie d’allumer la lumière le lendemain.
Mais que se passerait-il si je choisissais au hasard une lampe de poche chaque jour ? À quelle fréquence devrais-je m’attendre à avoir deux lumières de 15 minutes et aucune lumière pendant la seconde moitié de la marche ? Le JSL ci-joint est une simulation qui tente de répondre à la question ; Voyons d'abord la réponse :
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 badlyLes points rouges en dessous de 0 indiquent où les deux lampes de poche sont tombées en panne et à quel point
La barre de titre indique que cette simulation concernait deux lumières, l'une avec une batterie de plus de 4 heures et l'autre avec une batterie de plus de 5 heures. La plupart du temps, il reste plusieurs heures dans la paire de lumières. Le JSL commence par une fonction utilisateur, table à créer , car le code sera réutilisé ci-dessous pour réaliser un autre graphique.
maketable = function({capacity},// an array with the battery capacities expressed as time
{dt,charge,ontime,today,remainingWalkTime,message,light,usage,deadBatteries},//locals
Le premier ensemble d'{accolades} contient le paramètre de la fonction, capacité , qui sera une petite matrice avec la capacité horaire de chaque lampe de poche. Le deuxième ensemble contient les variables locales de la fonction ; si le JSL qui appelle la fonction utilise les mêmes noms de variables, les variables de l'appelant ne seront pas modifiées par cette fonction.
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 )
);
Ci-dessus, les batteries sont initialement complètement chargées en copiant capacité dans charge . Ensuite, une expression est attribuée à à temps , qui représente la répartition de mes temps de marche... une heure, plus ou moins. Et enfin, dt est défini sur une nouvelle table de données que cette fonction renverra éventuellement à l'appelant. Le tableau comprend une colonne de compteur de jours, une colonne qui montre combien d'heures étaient disponibles dans les lampes de poche après la marche et une colonne avec des notes générées par ordinateur sur ce qui s'est passé.
// 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 première partie de la boucle de simulation, ci-dessus, parcourt les jours de 2020 à 2030. (Les dates ne sont pas vraiment utilisées, sauf pour étiqueter le graphique ; la durée est d'environ 10*365 jours... vous verrez environ 3 500 lignes dans le tableau plus tard.) temps de marche restant est réglé sur environ une heure et devient la valeur initiale pour message , qui contiendra un peu plus de texte qui deviendra les notes de la journée. À l’intérieur de la boucle For, il y a une boucle While qui choisit au hasard une lampe de poche pour une partie de la promenade. La boucle For continue de choisir les lampes de poche jusqu'à ce que la promenade soit terminée ou qu'il n'y ait plus de lampes de poche avec une charge restante. La lumière choisie est soit épuisée, soit suffisamment utilisée pour terminer la balade. Ensuite, le message est mis à jour pour indiquer quelle lumière a été utilisée et pendant combien de temps.
// 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 dernière partie de la simulation, ci-dessus, ajoute une ligne au tableau pour représenter aujourd'hui , le colore en rouge s'il y avait temps de marche restant restant et définit le jour , heuresDisponible , et Remarques pour la nouvelle ligne. Enfin, les batteries sont rechargées ; le lieu La fonction trouve les index, ou emplacements, dans la matrice de charges qui sont tombés en dessous de zéro. Ensuite, pour ces emplacements uniquement, la valeur de charge est réinitialisée à la valeur de capacité.
dt; // return value
);
Et cela (ci-dessus) termine la fonction ; la valeur de la dernière instruction est renvoyée à l'appelant. dt , à lui seul, est une déclaration très simple qui s'évalue à ... dt . Vous pouvez également utiliser la fonction return : retour(dt); n'importe où dans la fonction.
// 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( "" )} )
)
);
Enfin, ci-dessus, appelez table à créer avec un capacité qui définit deux batteries. Le dt à gauche du = se trouve une variable globale, à laquelle est attribuée le résultat de la fonction à droite du =. Le dt dans la fonction est dans la liste locale au début de la fonction, et ne change pas le global dt . Une fois la table de données créée, le générateur de graphiques peut créer le graphique (tout en haut.) N'écrivez pas un script de création de graphiques à partir de zéro ! Utilisez l'interface graphique pour obtenir ce que vous voulez, puis enregistrez le script. J'ai édité celui-ci pour ajouter le titre du plan personnalisé avec les capacités de la batterie.
Le tableau utilisé par le générateur de graphiques (ci-dessus) ressemble à ceci :
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 notesSortie de la fonction maketable() affichant les heures et les notes (négatives)
Puisque maketable() prend un argument pour la capacité des batteries, je peux l'exécuter plusieurs fois. Je n'ai pas vraiment l'intention de transporter des lampes de poche supplémentaires, mais je veux savoir combien de lampes de poche, de quelle capacité, résolvent le mieux le problème. Voici un JSL qui appelle maketable à partir d'une boucle et collecte un autre tableau de réponses à partir des tableaux renvoyés par 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( [] ) )
);
déport est le tableau de données qui contiendra les réponses agrégées.
For( nLights = 1, nLights <= 10, nLights += 1,
For( batLife = 1 / 16, batLife <= 32, batLife *= 2,
dt = maketable( Repeat( inhours(batLife), nLights ) );
Ci-dessus, une paire de boucles parcourra 100 combinaisons, créant ainsi une nouvelle dt chaque fois.
// 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>
Au-dessus de, restant attrape le heuresDisponible colonne comme matrice pour que je puisse l'utiliser local() encore une fois pour localiser le les échecs.J'ai essayé le nombre d'échecs et la durée totale des échecs et j'ai obtenu des résultats similaires dans le graphique. Ajoutez une ligne au déport tableau et remplissez-le.
Close( dt, "nosave" );
)
);
Fermez les tables dt dans la boucle lorsqu'elles ne sont plus nécessaires afin qu'il n'y ait pas 100 fenêtres ouvertes.
Maintenant, le tableau que nous venons de créer ressemble à ceci :
.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 heure ne suffira probablement jamais, même avec 10 lampes de poche
Je peux voir que plus de lumières et de meilleures batteries sont utiles. Le JSL (enregistré à partir de l'interface graphique du générateur de graphiques) ressemble à ceci
dtreport<<GRAPH builder=""></GRAPH>
Et le graphique résultant :
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 darkPlus de lampes de poche ou plus de piles me protègent du noir
Le tracé de contour montre les résultats des 100 simulations ; il semble que trois lampes de poche d'une capacité de 17 heures ou quatre d'une capacité de 8 heures pourraient faire l'affaire. Mais avec seulement deux lampes de poche, en choisissant une lumière au hasard et en ne chargeant qu'une fois éteintes, il se peut qu'il n'y ait pas une capacité qui permettrait de garder les lumières allumées.