Porto due torce elettriche quando cammino prima dell'alba; ognuno ha una batteria da circa 5 ore e la camminata dura circa un'ora. Cerco di usare la stessa torcia finché non si scarica, e circa ogni cinque giorni riesco a cambiare le luci durante la passeggiata. La vecchia luce si carica e cerco di ricordare quale usare il giorno dopo. Questo piano funziona davvero abbastanza bene, anche se mi dimentico di cambiare le luci il giorno successivo.
Ma cosa accadrebbe se ogni giorno scegliessi a caso una torcia? Quanto spesso mi aspetterei di avere due luci da 15 minuti e nessuna luce per la seconda metà della camminata? La JSL allegata è una simulazione che cerca di rispondere alla domanda; prima vediamo la risposta:
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 badlyI punti rossi sotto lo 0 mostrano dove le due torce hanno fallito entrambe e quanto gravemente
La barra del titolo dice che questa simulazione era per due luci, una con una batteria da 4+ ore e l'altra con una batteria da 5+ ore. Nella maggior parte dei giorni restano diverse ore nella coppia di luci. Il JSL inizia con una funzione utente, realizzabile , perché il codice verrà riutilizzato di seguito per creare un altro grafico.
maketable = function({capacity},// an array with the battery capacities expressed as time
{dt,charge,ontime,today,remainingWalkTime,message,light,usage,deadBatteries},//locals
Il primo set di {parentesi graffe} contiene il parametro della funzione, capacità , che sarà una piccola matrice con la capacità oraria di ciascuna torcia. Il secondo insieme contiene le variabili locali per la funzione; se il JSL che chiama la funzione utilizza gli stessi nomi di variabile, le variabili del chiamante non verranno modificate da questa funzione.
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 )
);
Sopra, le batterie vengono inizialmente caricate completamente tramite la copia capacità in carica . Quindi viene assegnata un'espressione puntuale , che rappresenta la distribuzione dei tempi di camminata...un'ora, più o meno. E infine, dt è impostato su una nuova tabella dati che questa funzione restituirà eventualmente al chiamante. La tabella include una colonna del contatore dei giorni, una colonna che mostra quante ore erano disponibili nelle torce dopo la camminata e una colonna con note generate dal computer su ciò che è accaduto.
// 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 prima parte del ciclo di simulazione, sopra, si svolge nei giorni dal 2020 al 2030. (Le date non vengono realmente utilizzate se non per etichettare il grafico; la durata è di circa 10*365 giorni... vedrai circa 3500 righe nella tabella più avanti.) tempo di percorrenza rimanente è impostato su circa un'ora e diventa il valore iniziale di Messaggio , che avrà qualche testo in più che diventerà gli appunti della giornata. All'interno del ciclo For c'è un ciclo While che sceglie casualmente una torcia per parte della camminata. Il ciclo For continua a scegliere le torce finché la passeggiata non termina o non ci sono torce con carica rimanente. La luce scelta è esaurita o utilizzata abbastanza per terminare la passeggiata. Quindi il messaggio viene aggiornato per indicare quale luce è stata utilizzata e per quanto tempo.
// 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 );
);
L'ultima parte della simulazione, sopra, aggiunge una riga alla tabella da rappresentare Oggi , lo colora di rosso se c'era tempo di percorrenza rimanente rimasto e imposta il file giorno , oreDisponibile , E Appunti per la nuova riga. Infine le batterie vengono ricaricate; IL loc la funzione trova gli indici, o posizioni, nella matrice delle tariffe che sono scesi sotto lo zero. Quindi, solo per quelle posizioni, il valore di addebito viene reimpostato sul valore di capacità.
dt; // return value
);
E questo (sopra) termina la funzione; il valore dell'ultima istruzione viene restituito al chiamante. dt , da sola, è un'affermazione davvero semplice che vale... dt . Puoi anche usare la funzione di ritorno: ritorno(dt); ovunque nella funzione.
// 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( "" )} )
)
);
Infine, sopra, chiama realizzabile con un capacità che definisce due batterie. IL dt a sinistra di = c'è una variabile globale, a cui viene assegnato il risultato della funzione a destra di =. IL dt nella funzione si trova nell'elenco locale all'inizio della funzione e non modifica l'elenco globale dt . Una volta creata la tabella dati, il generatore di grafici può creare il grafico (in alto). Non scrivere uno script di creazione di grafici da zero! Usa la GUI per ottenere ciò che desideri, quindi salva lo script. Ho modificato questo per aggiungere il titolo della struttura personalizzata con le capacità della batteria.
La tabella utilizzata dal generatore di grafici (sopra) è simile alla seguente:
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 notesOutput dalla funzione maketable() che mostra ore e note (negative).
Poiché maketable() accetta un argomento per la capacità delle batterie, posso eseguirlo più volte. In realtà non ho intenzione di portare torce aggiuntive, ma voglio sapere quante torce e di quale capacità risolvono meglio il problema. Ecco alcuni JSL che chiamano maketable da un ciclo e raccolgono un'altra tabella di risposte dalle tabelle restituite da 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( [] ) )
);
dtreport è la tabella dati che conterrà le risposte aggregate.
For( nLights = 1, nLights <= 10, nLights += 1,
For( batLife = 1 / 16, batLife <= 32, batLife *= 2,
dt = maketable( Repeat( inhours(batLife), nLights ) );
Sopra, una coppia di cicli eseguirà 100 combinazioni, creandone una nuova dt ogni volta.
// 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>
Sopra, residuo afferra il oreDisponibile colonna come matrice in modo da poterla utilizzare loc() di nuovo per individuare il fallimenti.Ho provato il numero di guasti e il tempo totale dei guasti e ho ottenuto risultati simili nel grafico. Aggiungi una riga al file dtreport tabella e compilarla.
Close( dt, "nosave" );
)
);
Chiudi le tabelle dt nel ciclo quando non sono più necessarie, quindi non ci saranno 100 finestre aperte.
Ora la tabella appena creata si presenta così:
.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 flashlightsProbabilmente 0,0625 ore non saranno mai sufficienti, nemmeno con 10 torce elettriche
Vedo che più luci e batterie migliori aiutano. Il JSL (salvato dalla GUI del costruttore di grafici) si presenta così
dtreport<<GRAPH builder=""></GRAPH>
E il grafico risultante:
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 darkPiù torce elettriche o più batterie mi tengono lontano dal buio
Il contour plot mostra i risultati delle 100 simulazioni; sembra che tre torce con capacità di 17 ore o quattro con capacità di 8 ore potrebbero risolvere il problema. Ma con solo due torce, scegliendo una luce a caso e caricandola solo quando è spenta, potrebbe non esserci una capacità che mantenga le luci accese.