Skip to content

Instantly share code, notes, and snippets.

@OverlappingElvis
Last active August 17, 2017 20:31
Show Gist options
  • Select an option

  • Save OverlappingElvis/232b2a4b5e94e3a8aa60471655c84a1f to your computer and use it in GitHub Desktop.

Select an option

Save OverlappingElvis/232b2a4b5e94e3a8aa60471655c84a1f to your computer and use it in GitHub Desktop.
Is This Bathroom Occupied?
// 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