Skip to content

Instantly share code, notes, and snippets.

@OverlappingElvis
Last active August 17, 2017 20:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save OverlappingElvis/232b2a4b5e94e3a8aa60471655c84a1f to your computer and use it in GitHub Desktop.
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) {
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