Last active
August 17, 2017 20:31
-
-
Save OverlappingElvis/232b2a4b5e94e3a8aa60471655c84a1f to your computer and use it in GitHub Desktop.
Is This Bathroom Occupied?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // underscore.js helpers | |
| var _ = require('underscore'); | |
| // State aliases | |
| var OCCUPIED = true; | |
| var VACANT = false; | |
| // Sample a current state | |
| var knock = function(state) { | |
| if (state.sign === state.bathroom) { | |
| return _({}).extend(state, { correct: state.correct + 1 }); | |
| } | |
| if (state.sign && !state.bathroom) { | |
| return _({}).extend(state, { falsePositive: state.falsePositive + 1 }); | |
| } | |
| return _({}).extend(state, { falseNegative: state.falseNegative + 1 }); | |
| }; | |
| // Define behaviors as modifying the current state of the bathroom. Can be invoked as entering or exiting. | |
| // Don't change the state of the sign | |
| var unobservant = function(state, exit) { | |
| return _({}).extend(state, { | |
| sign: state.sign, | |
| bathroom: !exit | |
| }); | |
| }; | |
| // Change the sign when entering, but not on leaving | |
| var forgetful = function(state, exit) { | |
| return _({}).extend(state, { | |
| sign: OCCUPIED, | |
| bathroom: !exit | |
| }); | |
| }; | |
| // Make sure that the sign reflects the actual state of the bathroom | |
| var conscientious = function(state, exit) { | |
| return _({}).extend(state, { | |
| sign: !exit, | |
| bathroom: !exit | |
| }); | |
| }; | |
| // Each behavior is as likely to appear | |
| var chooseBehavior = _(_.sample).partial([unobservant, forgetful, conscientious]); | |
| // Create a person, randomly choosing a behavior | |
| var Person = function() { | |
| var behavior = chooseBehavior(); | |
| this.enterBehavior = behavior; | |
| // Behavior functionality is based on the exit flag, so we can reuse and partially apply that flag to the exit behavior | |
| this.exitBehavior = _(behavior).partial(_, true); | |
| }; | |
| // Enter bathroom | |
| Person.prototype.enterBathroom = function(state) { | |
| return this.enterBehavior(state); | |
| }; | |
| // Exit bathroom | |
| Person.prototype.exitBathroom = function(state) { | |
| return this.exitBehavior(state); | |
| }; | |
| // Use the bathroom and sample each step | |
| Person.prototype.useBathroom = function(state) { | |
| // Enter the bathroom and sample the result | |
| var enterState = knock(this.enterBathroom(state)); | |
| // Exit the bathroom and sample the result | |
| return knock(this.exitBathroom(enterState)); | |
| }; | |
| // Set number of runs for the model | |
| var numberOfRuns = 50000; | |
| // Sample twice per run | |
| var samples = numberOfRuns * 2; | |
| // Initial starting state | |
| var initialState = { | |
| sign: VACANT, | |
| bathroom: VACANT, | |
| correct: 0, | |
| falsePositive: 0, | |
| falseNegative: 0 | |
| }; | |
| // Get the results | |
| var results = _(numberOfRuns).chain() | |
| // Get a list with length numberOfRuns | |
| .range() | |
| // Generate as many people as runs | |
| .map(function() { | |
| return new Person(); | |
| }) | |
| // Work through the runs, returning a final state | |
| .reduce(function(memo, person) { | |
| return person.useBathroom(memo); | |
| }, initialState) | |
| .value(); | |
| // Reporting | |
| console.log('Results after ' + numberOfRuns + ' runs:'); | |
| console.log('Correct sign probability: ' + results.correct / samples); | |
| console.log('False positive probability: ' + results.falsePositive / samples); | |
| console.log('False negative probability: ' + results.falseNegative / samples); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment