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) { | |
var correct = state.sign === state.bathroom; | |
if (correct && state.sign === OCCUPIED) { | |
return _({}).extend(state, { truePositive: state.truePositive + 1 }); | |
} | |
if (correct && state.sign === VACANT) { | |
return _({}).extend(state, { trueNegative: state.trueNegative + 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); | |
}; | |
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 = 500000; | |
// Sample twice per run | |
var samples = numberOfRuns * 2; | |
// Initial starting state | |
var initialState = { | |
sign: VACANT, | |
bathroom: VACANT, | |
truePositive: 0, | |
trueNegative: 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(state, person) { return person.useBathroom(state); }, initialState) | |
.value(); | |
var tp = results.truePositive / samples; | |
var tn = results.trueNegative / samples; | |
var fp = results.falsePositive / samples; | |
var fn = results.falseNegative / samples; | |
// Reporting | |
console.log('Results after ' + numberOfRuns + ' runs:'); | |
console.log('Probability of the bathroom being occupied when the sign reads occupied: ' + (tp / (tp + fp)).toPrecision(4)); | |
console.log('Probability of the bathroom being vacant when the sign reads vacant: ' + (tn / (tn + fn)).toPrecision(4)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment