Created
July 10, 2012 19:15
-
-
Save staylor/3085598 to your computer and use it in GitHub Desktop.
Multi-Armed Bandit
This file contains 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
<?php | |
class HouseAd extends ModuleType { | |
const TYPE_QUERY_STRING = 0; | |
const TYPE_URL_PATH = 1; | |
const TYPE_COOKIE = 2; | |
const OPERATOR_EQUALS = 100; | |
const OPERATOR_NOT_EQUALS = 101; | |
const OPERATOR_GTE = 102; | |
const OPERATOR_LTE = 103; | |
const OPERATOR_LESS_THAN = 104; | |
const OPERATOR_GREATER_THAN = 105; | |
const CONDITION_AND = 200; | |
const CONDITION_OR = 201; | |
const STRATEGY_AB = 300; | |
const STRATEGY_EPSILON_GREEDY = 301; | |
const STRATEGY_EPSILON_FIRST = 302; | |
const STRATEGY_EPSILON_DECR = 303; | |
protected function __construct() { | |
$this->post_type = 'emusic_house_ad'; | |
$this->meta_name = 'house_ad_meta'; | |
parent::__construct(); | |
} | |
function init() { | |
parent::init(); | |
add_action( 'wp_ajax_new-house-ad-ruleset', array( $this, 'new_ruleset' ) ); | |
add_action( 'wp_ajax_new-house-ad-rule', array( $this, 'new_rule' ) ); | |
add_action( "add_meta_boxes_{$this->post_type}", array( $this, 'actions' ) ); | |
} | |
function actions() { | |
add_action( 'admin_head', array( $this, 'inline_js' ), 20 ); | |
} | |
function inline_js() { | |
?> | |
<style type="text/css"> | |
#rulesets .ruleset {padding: 10px; margin: 10px 0; background: #fff} | |
</style> | |
<script type="text/javascript"> | |
(function ($) { | |
var rulesets; | |
function new_ruleset() { | |
$.ajax({ | |
url : ajaxurl, | |
data : { | |
action : 'new-house-ad-ruleset', | |
set : $('.ruleset').length | |
}, | |
success : function (data) { | |
rulesets.append($(data)); | |
} | |
}); | |
return false; | |
} | |
function new_rule() { | |
var set = $(this).parents('.ruleset'); | |
$.ajax({ | |
url : ajaxurl, | |
data : { | |
action : 'new-house-ad-rule', | |
set : set.attr('data-set'), | |
rule : set.find('.rule').length | |
}, | |
success : function (data) { | |
set.find('table').append($(data)); | |
} | |
}); | |
return false; | |
} | |
function add_creative() { | |
} | |
$(document).ready(function ($) { | |
rulesets = $('#rulesets'); | |
$('.add-new-ruleset').click(new_ruleset); | |
$('.add-new-rule').live('click', new_rule); | |
window.add_media_callback = add_creative; | |
}); | |
}(jQuery)); | |
</script> | |
<?php | |
} | |
function type() { | |
$this->supports = array( 'title', 'thumbnail' ); | |
parent::type( array( | |
'show_in_menu' => 'emusic-miscellaneous', | |
'labels' => emusic_inflection( 'House Ad' ), | |
'taxonomies' => array( 'region' ), | |
'callback' => array( $this, 'box' ) | |
) ); | |
} | |
function box() { | |
add_meta_box( $this->post_type . '_meta_id', | |
__( 'Configuration', 'emusic' ), | |
array( $this, 'box_callback' ), | |
$this->post_type, | |
'normal', | |
'low' | |
); | |
} | |
function new_ruleset() { | |
extract( $_REQUEST ); | |
$this->ruleset( $set ); | |
exit(); | |
} | |
function new_rule() { | |
extract( $_REQUEST ); | |
$this->rule( $set, $rule ); | |
exit(); | |
} | |
function ruleset( $set, $data = null ) { | |
?> | |
<div class="ruleset" data-set="<?php echo $set ?>"> | |
<?php if ( 0 < (int) $set ): ?> | |
<p>Condition: <select name="ruleset[<?php echo $set ?>][condition]"> | |
<option value="<?php echo self::CONDITION_AND ?>" <?php | |
selected( $data && self::CONDITION_AND === $data->condition ) ?>>AND</option> | |
<option value="<?php echo self::CONDITION_OR ?>" <?php | |
selected( $data && self::CONDITION_OR === $data->condition ) ?>>OR</option> | |
</select></p> | |
<?php endif ?> | |
<table> | |
<tr> | |
<th>Type</th> | |
<th>Cookie (optional)</th> | |
<th>Object</th> | |
<th>Operator</th> | |
<th>Value</th> | |
</tr> | |
<?php | |
if ( empty( $data ) ) { | |
$this->rule( $set ); | |
} else { | |
$rules = $this->get_rules( $data->id ); | |
if ( empty( $rules ) ) { | |
$this->rule( $set ); | |
} else { | |
foreach ( $rules as $index => $the_rule ) | |
$this->rule( $set, $index, $the_rule ); | |
} | |
} | |
?> | |
</table> | |
<p><a class="add-new-rule button">Add a New Rule</a></p> | |
</div> | |
<?php | |
} | |
function rule( $set = 0, $rule = 0, $data = null ) { | |
?> | |
<tr class="rule"> | |
<?php | |
if ( $data ) | |
$data->type = (int) $data->type; | |
?> | |
<td> | |
<select name="ruleset[<?php echo $set ?>][rule][<?php echo $rule ?>][type]"> | |
<option value="">-- SELECT MATCH TYPE --</option> | |
<option value="<?php echo self::TYPE_QUERY_STRING ?>" <?php | |
selected( $data && self::TYPE_QUERY_STRING === $data->type ) ?>>Query String</option> | |
<option value="<?php echo self::TYPE_URL_PATH ?>" <?php | |
selected( $data && self::TYPE_URL_PATH === $data->type ) ?>>URL Path</option> | |
<option value="<?php echo self::TYPE_COOKIE ?>" <?php | |
selected( $data && self::TYPE_COOKIE === $data->type ) ?>>Cookie</option> | |
</select> | |
</td> | |
<td> | |
<input type="text" size="25" class="widefat" name="ruleset[<?php echo $set ?>][rule][<?php echo $rule ?>][parent]" value="<?php | |
esc_attr_e( $data && !empty( $data->parent ) ? $data->parent : '' ) ?>"/> | |
</td> | |
<td> | |
<input type="text" size="25" class="widefat" name="ruleset[<?php echo $set ?>][rule][<?php echo $rule ?>][name]" value="<?php | |
esc_attr_e( $data ? $data->name : '' ) ?>"/> | |
</td> | |
<?php | |
if ( $data ) | |
$data->operator = (int) $data->operator; | |
?> | |
<td> | |
<select name="ruleset[<?php echo $set ?>][rule][<?php echo $rule ?>][operator]"> | |
<option value="<?php echo self::OPERATOR_EQUALS ?>" <?php | |
selected( $data && self::OPERATOR_EQUALS === $data->operator ) ?>> = </option> | |
<option value="<?php echo self::OPERATOR_NOT_EQUALS ?>" <?php | |
selected( $data && self::OPERATOR_NOT_EQUALS === $data->operator ) ?>> != </option> | |
<option value="<?php echo self::OPERATOR_GTE ?>=" <?php | |
selected( $data && self::OPERATOR_GTE === $data->operator ) ?>> >= </option> | |
<option value="<?php echo self::OPERATOR_LTE ?>=" <?php | |
selected( $data && self::OPERATOR_LTE === $data->operator ) ?>> <= </option> | |
<option value="<?php echo self::OPERATOR_LESS_THAN ?>" <?php | |
selected( $data && self::OPERATOR_LESS_THAN === $data->operator ) ?>> < </option> | |
<option value="<?php echo self::OPERATOR_GREATER_THAN ?>" <?php | |
selected( $data && self::OPERATOR_GREATER_THAN === $data->operator ) ?>> > </option> | |
</select> | |
</td> | |
<td> | |
<input type="text" size="25" class="widefat" name="ruleset[<?php echo $set ?>][rule][<?php echo $rule ?>][value]" value="<?php | |
esc_attr_e( $data ? $data->value : '' ) ?>"/> | |
</td> | |
</tr> | |
<?php | |
} | |
function box_callback( $post ) { | |
$code = ''; | |
if ( !empty( $post->ID ) ) | |
$code = get_post_meta( $post->ID, 'omniture_code', true ); | |
?> | |
<div class="emusic-admin"> | |
<p> | |
<label><strong>Description of this Ad</strong> (optional)</label> | |
<input class="widefat" type="text" name="house_ad_desc" value="<?php esc_attr_e( $post ? $post->post_excerpt : '' ) ?>"/> | |
</p> | |
<?php | |
$guid = ''; | |
if ( $post && !strstr( $post->guid, '?post_type=emusic_house_ad' ) ) | |
$guid = $post->guid; | |
?> | |
<p> | |
<label><strong>Click URL</strong></label> | |
<input class="widefat" type="text" name="house_ad_url" value="<?php esc_attr_e( $guid ) ?>"/> | |
</p> | |
<p> | |
<label><strong>Click Map</strong> (optional)</label> | |
<input class="widefat" type="text" name="house_ad_map" value="<?php esc_attr_e( $post ? $post->post_content_filtered : '' ) ?>"/> | |
</p> | |
<p> | |
<label><strong>Tracking Code</strong></label> | |
<input class="widefat" type="text" name="house_ad_code" value="<?php esc_attr_e( $code ) ?>"/> | |
</p> | |
<div class="creative"> | |
<?php | |
if ( $post && $post->ID ) { | |
$images = get_images( $post->ID ); | |
if ( !empty( $images ) ) { | |
echo '<ul>'; | |
foreach ( $images as $image ) | |
printf( '<li><a class="ad-creative">%s</a></li>', get_the_post_thumbnail( $image->ID ) ); | |
echo '</ul>'; | |
} | |
} | |
?> | |
<div id="creative-trigger"> | |
<?php $this->_image_id_input( __( 'Add More Creative' ), 'new-creative', 'new-creative', false, true, false ) ?> | |
</div> | |
</div> | |
<div id="strategy"> | |
<p> | |
<select name="house_ad_strategy"> | |
<option value=""> --- SELECT A STRATEGY --- </option> | |
<option value="<?php echo self::STRATEGY_AB ?>" <?php | |
selected( $post && self::STRATEGY_AB === $post->menu_order ) ?>> A / B </option> | |
<option value="<?php echo self::STRATEGY_EPSILON_GREEDY ?>" <?php | |
selected( $post && self::STRATEGY_EPSILON_GREEDY === $post->menu_order ) ?>> Epsilon-greedy </option> | |
<option value="<?php echo self::STRATEGY_EPSILON_FIRST ?>" <?php | |
selected( $post && self::STRATEGY_EPSILON_GREEDY === $post->menu_order ) ?>> Epsilon-first </option> | |
<option value="<?php echo self::STRATEGY_EPSILON_DECR ?>" <?php | |
selected( $post && self::STRATEGY_EPSILON_DECR === $post->menu_order ) ?>> Epsilon-decreasing </option> | |
</select> | |
</p> | |
<p> | |
<strong>Number of Impressions Per Test (if applicable)</strong> | |
<input type="text" name="house_ad_impressions"/> | |
</p> | |
</div> | |
<div id="rulesets"> | |
<p><a class="add-new-ruleset button">Add a New Ruleset</a></p> | |
<?php | |
if ( !empty( $post->ID ) ): | |
$rulesets = $this->get_rulesets( $post->ID ); | |
if ( !empty( $rulesets ) ): | |
foreach ( $rulesets as $index => $ruleset ) | |
$this->ruleset( $index, $ruleset ); | |
endif; | |
endif; | |
?> | |
</div> | |
</div> | |
<?php | |
} | |
function callback( $id, $post ) { | |
global $wpdb; | |
if ( $this->post_type === $post->post_type ) { | |
$params = array( | |
'post_content' => '', | |
'post_excerpt' => '', | |
'guid' => '', | |
'post_content_filtered' => '', | |
'menu_order' => '' | |
); | |
if ( !empty( $_POST['house_ad_code'] ) ) | |
$params['post_excerpt'] = stripslashes( $_POST['house_ad_code'] ); | |
if ( !empty( $_POST['house_ad_desc'] ) ) | |
$params['post_content'] = stripslashes( $_POST['house_ad_desc'] ); | |
if ( !empty( $_POST['house_ad_url'] ) ) | |
$params['guid'] = stripslashes( $_POST['house_ad_url'] ); | |
if ( !empty( $_POST['house_ad_map'] ) ) | |
$params['post_content_filtered'] = stripslashes( $_POST['house_ad_map'] ); | |
if ( !empty( $_POST['house_ad_strategy'] ) ) | |
$params['menu_order'] = stripslashes( $_POST['house_ad_strategy'] ); | |
if ( !empty( $params ) ) { | |
$wpdb->update( $wpdb->posts, $params, array( 'ID' => $id ) ); | |
clean_post_cache( $id ); | |
} | |
if ( !empty( $_POST['ruleset'] ) ) { | |
$rulesets = stripslashes_deep( $_POST['ruleset'] ); | |
$rows = $this->get_rulesets( $id ); | |
$sets = count( $rows ); | |
$ruleset_count = 0; | |
foreach ( $rulesets as $ruleset ) { | |
if ( empty( $ruleset['rule'] ) ) | |
continue; | |
$params = array( | |
'object_id' => $id, | |
'order' => $ruleset_count, | |
'condition' => empty( $set['condition'] ) ? '' : $set['condition'] | |
); | |
if ( $ruleset_count >= $sets || !array_key_exists( $ruleset_count, $rows ) ) { | |
$wpdb->insert( 'wp_rulesets', $params ); | |
$ruleset_id = $wpdb->insert_id; | |
} else { | |
$wpdb->update( 'wp_rulesets', $params, array( 'object_id' => $id, 'order' => $ruleset_count ) ); | |
$ruleset_id = $rows[$ruleset_count]->id; | |
unset( $rows[$ruleset_count] ); | |
} | |
$rule_count = 0; | |
$rules = $this->get_rules( $ruleset_id ); | |
foreach ( $rules as $rule ) | |
$rules[$rule->order] = $rule; | |
$num_rules = count( $rules ); | |
foreach ( $ruleset['rule'] as $rule ) { | |
if ( '' === $rule['value'] || '' === $rule['type'] ) | |
continue; | |
if ( self::TYPE_URL_PATH !== (int) $rule['type'] && empty( $rule['name'] ) ) | |
continue; | |
$params = array( | |
'ruleset_id'=> $ruleset_id, | |
'operator' => $rule['operator'], | |
'type' => $rule['type'], | |
'parent' => $rule['parent'], | |
'name' => $rule['name'], | |
'value' => $rule['value'], | |
'order' => $rule_count | |
); | |
if ( $rule_count >= $num_rules || !array_key_exists( $rule_count, $ruleset['rule'] ) ) { | |
$wpdb->insert( 'wp_rules', $params ); | |
} else { | |
$wpdb->update( 'wp_rules', $params, array( 'id' => $rules[$rule_count]->id ) ); | |
unset( $rules[$rule_count] ); | |
} | |
$rule_count++; | |
} | |
if ( !empty( $rules ) ) { | |
$ids = join( ',', wp_list_pluck( $rules, 'id' ) ); | |
$wpdb->query( "DELETE FROM wp_rules WHERE ruleset_id IN (" . $ids . ")" ); | |
} | |
$ruleset_count++; | |
} | |
if ( !empty( $rows ) ) { | |
$ids = join( ',', wp_list_pluck( $rows, 'id' ) ); | |
$wpdb->query( "DELETE FROM wp_rules WHERE ruleset_id IN (" . $ids . ")" ); | |
$wpdb->query( "DELETE FROM wp_rulesets WHERE id IN (" . $ids . ")" ); | |
} | |
wp_cache_flush_group( 'post-type-ids' ); | |
} | |
} | |
} | |
function do_operator( $operator, $value1, $value2 ) { | |
$matches = false; | |
switch ( $operator ) { | |
case self::OPERATOR_EQUALS: | |
$matches = $value1 == $value2; | |
break; | |
case self::OPERATOR_NOT_EQUALS: | |
$matches = $value1 != $value2; | |
break; | |
case self::OPERATOR_GTE: | |
$matches = $value1 >= $value2; | |
break; | |
case self::OPERATOR_LTE: | |
$matches = $value1 <= $value2; | |
break; | |
case self::OPERATOR_LESS_THAN: | |
$matches = $value1 < $value2; | |
break; | |
case self::OPERATOR_GREATER_THAN: | |
$matches = $value1 > $value2; | |
break; | |
} | |
return $matches; | |
} | |
function parse_query_string( $rule ) { | |
$qs = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_QUERY ); | |
parse_str( $qs, $pieces ); | |
if ( !empty( $pieces ) ) | |
return $this->do_operator( $rule->operator, $pieces[$rule->name], $rule->value ); | |
} | |
function parse_url_path( $rule ) { | |
$path = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH ); | |
return strstr( $path, $rule->value ); | |
} | |
function parse_cookie( $rule ) { | |
if ( empty( $rule->parent ) ) { | |
return $this->do_operator( $rule->operator, $_COOKIE[$rule->name], $rule->value ); | |
} else { | |
parse_str( $_COOKIE[$rule->parent], $pieces ); | |
return $this->do_operator( $rule->operator, $pieces[$rule->name], $rule->value ); | |
} | |
} | |
function parse_rules( $post_id ) { | |
$rulesets = $this->get_rulesets( $post_id ); | |
if ( empty( $rulesets ) ) | |
return; | |
$matched = false; | |
foreach ( $rulesets as $index => $ruleset ) { | |
$rules = $this->get_rules( $ruleset->id ); | |
if ( empty( $rules ) ) | |
continue; | |
$truthy = 0; | |
foreach ( $rules as $rule ) { | |
switch ( $rule->type ) { | |
case self::TYPE_QUERY_STRING: | |
if ( $this->parse_query_string( $rule ) ) | |
$truthy++; | |
break; | |
case self::TYPE_URL_PATH: | |
if ( $this->parse_url_path( $rule ) ) | |
$truthy++; | |
break; | |
case self::TYPE_COOKIE: | |
if ( $this->parse_cookie( $rule ) ) | |
$truthy++; | |
break; | |
} | |
} | |
$matched = $truthy === count( $rules ); | |
if ( isset( $rulesets[$index + 1] ) ) { | |
switch ( $rulesets[$index + 1]->condition ) { | |
case self::CONDITION_AND: | |
if ( !$matched ) | |
return $matched; | |
break; | |
case self::CONDITION_OR: | |
if ( $matched ) | |
return $matched; | |
break; | |
} | |
} | |
} | |
return $matched; | |
} | |
function pick_creative( $post_id ) { | |
$choices = get_images( $post_id ); | |
if ( empty( $choices ) ) | |
return; | |
$ad = get_post( $post_id ); | |
if ( !empty( $ad->menu_order ) ) { | |
$bandit = new MultiArmedBandit( $post_id, $choices ); | |
switch ( $ad->menu_order ) { | |
case self::STRATEGY_AB: | |
$winner = $bandit->epsilon_ab(); | |
break; | |
case self::STRATEGY_EPSILON_GREEDY: | |
$winner = $bandit->epsilon_greedy(); | |
break; | |
case self::STRATEGY_EPSILON_FIRST: | |
$winner = $bandit->epsilon_first(); | |
break; | |
case self::STRATEGY_EPSILON_DECR: | |
$winner = $bandit->epsilon_decreasing(); | |
break; | |
} | |
} else { | |
$winner = $choices[0]->ID; | |
} | |
return $winner; | |
} | |
function get_rulesets( $post_id ) { | |
global $wpdb; | |
return $wpdb->get_results( "SELECT r.* FROM wp_rulesets r WHERE r.object_id = {$post_id} ORDER BY r.order ASC" ); | |
} | |
function get_rules( $ruleset_id ) { | |
global $wpdb; | |
return $wpdb->get_results( "SELECT r.* FROM wp_rules r WHERE r.ruleset_id = {$ruleset_id} ORDER BY r.order ASC" ); | |
} | |
} | |
function house_ad_parse_rules( $post_id ) { | |
return get_emusic()->house_ad->parse_rules( $post_id ); | |
} | |
function house_ad_pick_creative( $post_id ) { | |
return get_emusic()->house_ad->pick_creative( $post_id ); | |
} | |
get_emusic()->house_ad = HouseAd::get_instance(); |
This file contains 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
<?php | |
add_action( 'wp_ajax_nopriv_bandit-add-reward', array( 'MultiArmedBandit', 'add_reward' ) ); | |
add_action( 'wp_ajax_bandit-add-reward', array( 'MultiArmedBandit', 'add_reward' ) ); | |
class MultiArmedBandit { | |
const KEY_REWARDS = 'r'; | |
const KEY_PULLS = 'p'; | |
const KEY_EXPECTATION = 'x'; | |
const cache_group = 'bandit'; | |
var $cache_key; | |
var $trial_key; | |
var $choices; | |
var $history; | |
var $trials; | |
var $trial_limit; | |
function __construct( $id, $choices, $trial_limit = 0 ) { | |
$this->cache_key = $this->create_key( $id ); | |
$this->choices = $choices; | |
$this->retrieve_history(); | |
if ( !empty( $trial_limit ) ) { | |
$this->trial_limit = $trial_limit; | |
$this->trial_key = $this->create_trial_key( $id ); | |
$this->increment_trials(); | |
} | |
} | |
function create_key( $id ) { | |
return 'history-' . $id; | |
} | |
function create_trial_key( $id ) { | |
return 'trials-' . $id; | |
} | |
function retrieve_history() { | |
$base_value = array( | |
self::KEY_REWARDS => 1, | |
self::KEY_PULLS => 1, | |
self::KEY_EXPECTATION => 1 | |
); | |
$this->history = wp_cache_get( $this->cache_key, self::cache_group ); | |
if ( empty( $this->history ) ) { | |
$this->history = array(); | |
foreach ( $this->choices as $choice ) | |
$this->history[$choice] = $base_value; | |
} else { | |
foreach ( $this->choices as $choice ) | |
if ( !isset( $this->history[$choice] ) ) | |
$this->history[$choice] = $base_value; | |
} | |
} | |
function save_history() { | |
wp_cache_set( $this->cache_key, $this->history, self::cache_group ); | |
} | |
function increment_trials() { | |
$this->trials = wp_cache_get( $this->trial_key, self::cache_group ); | |
if ( empty( $this->trials ) ) | |
$this->trials = 0; | |
$this->trials++; | |
wp_cache_set( $this->trial_key, $this->trials, self::cache_group ); | |
} | |
/** | |
* Implement Epsilon-greedy | |
* | |
*/ | |
/** | |
* Reward is added via AJAX call fired by click | |
* User does NOT need to be logged in. | |
* | |
*/ | |
function add_reward() { | |
extract( $_REQUEST ); | |
if ( empty( $id ) || empty( $choice ) ) | |
exit( 'Badly-formed request.' ); | |
$key = $this->create_key( $id ); | |
$history = wp_cache_get( $key, self::cache_group ); | |
$lever =& $history[$choice]; | |
self::increment_lever( $lever ); | |
wp_cache_set( $key, $history, self::cache_group ); | |
exit( 1 ); | |
} | |
/** | |
* This is the equivalent of an impression | |
* | |
*/ | |
function increment_lever( &$lever ) { | |
$lever[self::KEY_PULLS] += 1; | |
$lever[self::KEY_EXPECTATION] = $lever[self::KEY_REWARDS] / $lever[self::KEY_PULLS]; | |
} | |
/** | |
* Implements the Mersenne Twister random number generator | |
* | |
*/ | |
function mt_random() { | |
$min = 0; | |
$max = 1; | |
return ( $min + mt_rand() / mt_getrandmax() * ( $max - $min ) ); | |
} | |
function pick_random() { | |
/** | |
* Copy so we don't alter with by-reference functions | |
* | |
*/ | |
$choices = $this->choices; | |
/** | |
* Shuffle the array() to offset PHP's supposed bias | |
* | |
*/ | |
shuffle( $choices ); | |
/** | |
* Get a random element from the array | |
* | |
*/ | |
$winner = array_rand( $choices ); | |
$this->increment_lever( $this->history[$winner] ); | |
$this->save_history(); | |
return $winner; | |
} | |
function pick_winner() { | |
$winner = $greatest_expectation = 0; | |
foreach ( $this->choices as $choice ) { | |
if ( empty( $winner ) || $choice[self::KEY_EXPECTATION] > $greatest_expectation ) { | |
$greatest_expectation = $choice[self::KEY_EXPECTATION]; | |
$winner = $choice; | |
} | |
} | |
return $winner; | |
} | |
function epsilon_ab() { | |
if ( $this->trials < $this->trial_limit ) | |
$winner = $this->pick_random(); | |
return $this->pick_winner(); | |
} | |
function epsilon_greedy() { | |
if ( $this->mt_random() < 0.1 ) | |
return $this->pick_random(); | |
return $this->pick_winner(); | |
} | |
function epsilon_first() { | |
// TODO: implement this | |
return $this->pick_winner(); | |
} | |
function epsilon_decreasing() { | |
// TODO: implement this | |
return $this->pick_winner(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment