Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

486 lines (442 sloc) 17.459 kb
<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js ie6" lang="en"> <![endif]-->
<!--[if IE 7]> <html class="no-js ie7" lang="en"> <![endif]-->
<!--[if IE 8]> <html class="no-js ie8" lang="en"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Random Choice</title>
<meta name="description" content="Random Choice, Erlang Factory Lite 2012 (Moscow, Russia)">
<meta name="author" content="Bob Ippolito">
<meta name="viewport" content="width=1024, user-scalable=no">
<!-- Core and extension CSS files -->
<link rel="stylesheet" href="imakewebthings-deck.js-9accb16/core/deck.core.css">
<link rel="stylesheet" href="imakewebthings-deck.js-9accb16/extensions/goto/deck.goto.css">
<link rel="stylesheet" href="imakewebthings-deck.js-9accb16/extensions/menu/deck.menu.css">
<link rel="stylesheet" href="imakewebthings-deck.js-9accb16/extensions/navigation/deck.navigation.css">
<link rel="stylesheet" href="imakewebthings-deck.js-9accb16/extensions/status/deck.status.css">
<link rel="stylesheet" href="imakewebthings-deck.js-9accb16/extensions/hash/deck.hash.css">
<link rel="stylesheet" href="imakewebthings-deck.js-9accb16/extensions/scale/deck.scale.css">
<!-- Style theme. More available in /themes/style/ or create your own. -->
<link rel="stylesheet" href="imakewebthings-deck.js-9accb16/themes/style/swiss.css">
<!-- Transition theme. More available in /themes/transition/ or create your own. -->
<link rel="stylesheet" href="imakewebthings-deck.js-9accb16/themes/transition/horizontal-slide.css">
<script src="imakewebthings-deck.js-9accb16/modernizr.custom.js"></script>
<style type="text/css">
.slide h1 > p.subtitle {
font-size: 0.25em;
font-weight: bold;
margin-bottom: .25em;
color: #888;
}
#fair-coin p.heads, #fair-coin p.tails {
display: none;
}
#fair-coin .demo.heads p.heads, #fair-coin .demo.tails p.tails {
display: block;
}
#fair-coin .demo p { font-size: 2em; }
#die-roll .demo p { font-size: 2em; }
div.demo .nomargin { margin-bottom: 0em; line-height: 1.25; }
div.demo .big { font-size: 3em; line-height: 1.25; }
tr.selected { background-color: #ffaaaa; }
td.selected { background-color: #cc6666; }
</style>
</head>
<body class="deck-container">
<!-- Begin slides -->
<section class="slide" id="title-slide">
<h1>Random Choice
<p class="subtitle">(случайный выбор)</p>
<p class="subtitle">Bob Ippolito (<a href="http://twitter.com/etrepum">@etrepum</a>)</p>
<p class="subtitle">Erlang Factory Lite 2012, Moscow</p>
</h1>
</section>
<section class="slide" id="why-this-talk">
<h2>Why this talk?</h2>
<ul>
<li>I was rewriting an IRC bot</li>
<li>He "learns" how to chat</li>
<li>Brain is based on Markov chains (Це́пь Ма́ркова)</li>
<li>Random choice is essential to this and many other interesting applications</li>
</ul>
</section>
<section class="slide" id="markov">
<h2>Andrey Andreyevich Markov</h2>
<h3>Андре́й Андре́евич Ма́рков</h3>
<!-- http://en.wikipedia.org/wiki/File:AAMarkov.jpg -->
<img src="img/AAMarkov.jpg" />
</section>
<section class="slide" id="markov-chain">
<h2>Markov chain</h2>
<!-- http://en.wikipedia.org/wiki/File:Markovkate_01.svg -->
<img src="img/Markovkate_01.svg"/>
</section>
<section class="slide" id="really">
<h2>Really?</h2>
<ul>
<li>(Most) ad servers also use random choice</li>
<li>Mochi Media built this kind of ad server in Erlang</li>
<li>Was fun to come up with an efficent way to do it</li>
</ul>
</section>
<section class="slide" id="random-module">
<h2>Erlang's <tt>random</tt> module</h2>
<ul>
<li>Wichmann-Hill AS183 algorithm from 1982</li>
<li>… designed for 16 bit computers with limited arithmetic</li>
<li>It has poor results and you probably shouldn't use it</li>
<li>Consider the <tt>crypto</tt> module, or a third party library</li>
<li>Despite this, I will use <tt>random</tt> for the examples</li>
</ul>
</section>
<section class="slide" id="fair-coin-code">
<h2>Fair coin</h2>
<ul>
<li><tt>uniform()</tt> returns a float <tt>0.0 ≤ X &lt; 1.0</tt></li>
<li>○ (heads) when <tt>X &lt; 0.5</tt>, ● (tails) otherwise</li>
</ul>
</section>
<section class="slide" id="fair-coin">
<h2>Fair coin</h2>
<div class="demo">
<button>Flip Coin</button>
<h3 class="nomargin">X = <span class="x">…</span></h3>
<p class="heads big">○ (heads), X &lt; 0.5</p>
<p class="tails big">● (tails), X ≥ 0.5</p>
</div>
</section>
<section class="slide" id="die-roll-code">
<h2>Die roll</h2>
<ul>
<li><tt>uniform(N)</tt> returns an integer <tt>1 ≤ X ≤ N</tt></li>
<li><tt>uniform(N) -> 1 + trunc(N * uniform()).</tt></li>
</ul>
</section>
<section class="slide" id="die-roll">
<h2>Die roll</h2>
<div class="demo">
<button>Roll Die</button>
<h3 class="nomargin">X = <span class="x">…</span></h3>
<span class="die big"></span>
</div>
</section>
<section class="slide" id="unweighted-selection-code">
<h2>Random selection without replacement</h2>
<pre><code>choose(L) ->
Nth = random:uniform(length(L)),
{H, [Choice | T]} = lists:split(Nth, L),
{Choice, H ++ T}.</code></pre>
</section>
<section class="slide" id="unweighted-selection">
<h2>Random selection without replacement</h2>
<div class="demo">
<button>Select</button>
<h3 class="nomargin">Selections = </h3>
<span class="selections big">&nbsp;</span>
<h3 class="nomargin">L = </h3>
<span class="l big">&nbsp;</span>
</div>
</section>
<section class="slide" id="unweighted-selection-notes">
<h2>Random selection without replacement</h2>
<ul>
<li>Can be used to shuffle a list (very slowly)</li>
<li>Or simulate a deck of cards</li>
</ul>
</section>
<section class="slide" id="weighted-selection">
<h2>Weighted selection without replacement</h2>
<ul>
<li><tt>{sum(Weight), [{Key, Weight}, …]}</tt></li>
<li><pre><code>weight_split(N, L) -> weight_split(N, L, []).
weight_split(N, L, [H={K, W} | T], Acc) ->
case N - W of
N1 when N1 > 0 ->
weight_split(N1, T, [H | Acc]);
_ ->
{K, lists:reverse(Acc, T)}
end.</pre></code>
</ul>
</section>
<section class="slide" id="unweighted-selection-repl-notes">
<h2>Random selection with replacement</h2>
<pre><code>choose(L) ->
lists:nth(random:uniform(length(L)), L).
</code></pre>
<ul>
<li>Does not change list</li>
<li>Similar to rolling a die</li>
</ul>
</section>
<section class="slide" id="unweighted-selection-repl">
<h2>Random selection with replacement</h2>
<div class="demo selection-without-repl" data-initial='["☃", "♖", "♗", "☺", "✈", "☭"]'>
<button>Select</button>
<h3 class="nomargin">Selections = </h3>
<span class="selections big">&nbsp;</span>
<h3 class="nomargin">L = </h3>
<span class="l big">&nbsp;</span>
</div>
</section>
<section class="slide" id="weighted-selection-repl-1">
<h2>Weighted selection, naive</h2>
<ul>
<li>With replacement, weight can be implemented simply</li>
<li>Just add the item to the list multiple times</li>
<li><tt>[a, a, a, b, c, d]</tt></li>
<li>50% <tt>a</tt>, ~16.6% <tt>b</tt>, …</tt>
</ul>
</section>
<section class="slide" id="weighted-selection-repl-1">
<h2>Weighted selection with replacement</h2>
<div class="demo selection-without-repl" data-initial='["☃", "☃", "☃", "☺", "✈", "☭"]'>
<button>Select</button>
<h3 class="nomargin">Selections = </h3>
<span class="selections big">&nbsp;</span>
<h3 class="nomargin">L = </h3>
<span class="l big">&nbsp;</span>
</div>
</section>
<section class="slide" id="weighted-selection-repl-2-notes">
<h2>Optimizing for memory</h2>
<ul>
<li>Naive solution uses a lot of memory</li>
<li>Can do better by counting each unique choice</li>
<li><tt>{6, [{a, 3}, {b, 1}, {c, 1}, {d, 1}]}</tt></li>
<li>Tradeoff - it's harder to seek to Nth choice</li>
</ul>
</section>
<section class="slide" id="alias-notes">
<h2>Alias method</h2>
<ul>
<li>Create N coins, one for each unique choice</li>
<li>Choose coin (by die roll), then flip weighted coin</li>
<li>Uses O(N) memory, has O(1) selection!</li>
</ul>
</section>
<section class="slide" id="alias">
<h2>Alias method</h2>
<div class="demo">
<div style="float: left">
<button>Choose</button>
<h3>Die: <span class="die"></span></h3>
<h3>Coin: <span class="coin"></span></h3>
</div>
<table style="float: right">
<thead>
<tr><th>head</th><th>tail</th></tr>
</thead>
<tbody>
<tr><td><tt>a</tt> 1</td><td><tt>undefined</tt></td></tr>
<tr><td><tt>b</tt> ⅔</td><td><tt>a</tt> ⅓</td></tr>
<tr><td><tt>c</tt> ⅔</td><td><tt>a</tt> ⅓</td></tr>
<tr><td><tt>d</tt> ⅔</td><td><tt>a</tt> ⅓</td></tr>
</tbody>
</table>
</div>
</section>
<section class="slide" id="handling-updates">
<h2>Handling updates</h2>
<ul>
<li>For our IRC bot, the choices update for every word of text</li>
<li>Alias method is <tt>O(N)</tt> to update</li>
<li>High <tt>O(N)</tt> garbage, no sharing at all</li>
</ul>
</section>
<section class="slide" id="naive-tree-update">
<h2>Using a tree</h2>
<ul>
<li>Can build a tree for efficient updates</li>
<li>Fast <tt>O(log N)</tt> updates</li>
<li>Low <tt>O(log N)</tt> garbage, good sharing</li>
<li>Slow <tt>O(N)</tt> seeks, since sort is by key</li>
</ul>
</section>
<section class="slide" id="max-heap-notes">
<h2>Simple max heap</h2>
<ul>
<li>Seems to be a good compromise between speed and memory</li>
<li>Highest weights first</li>
<li>Highest weights are most likely to be updated</li>
<li>Worst case <tt>O(N)</tt> for every operation</li>
<li>But very good common case, near head of the list</li>
</ul>
</section>
<section class="slide" id="max-heap">
<h2>Simple max heap</h2>
<div class="demo">
<div style="float: left">
<button>Choose</button>
<form>
<input type="text" placeholder="Enter words" /><br />
<span class="big output"></span>
</form>
</div>
<table style="float: right">
<thead>
<tr><th>Key&nbsp;</th><th>Weight</th></tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</section>
<section class="slide" id="info">
<h2>Questions?</h2>
<h3>Slides:<br/>
&nbsp;&nbsp;<a href="http://bob.ippoli.to/rndchoice_efl_2012">etrepum.github.com/rndchoice_efl_2012</a></h3>
<h3>Code:<br/>
&nbsp;&nbsp;<a href="http://github.com/rndchoice_efl_2012">github.com/etrepum/rndchoice_efl_2012</a></h3>
<h3>Twitter:<br/>
&nbsp;&nbsp;<a href="http://twitter.com/etrepum">@etrepum</a></h3>
</section>
<!-- deck.navigation snippet -->
<a href="#" class="deck-prev-link" title="Previous">&#8592;</a>
<a href="#" class="deck-next-link" title="Next">&#8594;</a>
<!-- deck.status snippet -->
<p class="deck-status">
<span class="deck-status-current"></span>
/
<span class="deck-status-total"></span>
</p>
<!-- deck.goto snippet -->
<form action="." method="get" class="goto-form">
<label for="goto-slide">Go to slide:</label>
<input type="text" name="slidenum" id="goto-slide" list="goto-datalist">
<datalist id="goto-datalist"></datalist>
<input type="submit" value="Go">
</form>
<!-- deck.hash snippet -->
<a href="." title="Permalink to this slide" class="deck-permalink">#</a>
<!-- jQuery -->
<script src="imakewebthings-deck.js-9accb16/jquery-1.7.min.js"></script>
<!-- Deck Core and extensions -->
<script src="imakewebthings-deck.js-9accb16/core/deck.core.js"></script>
<script src="imakewebthings-deck.js-9accb16/extensions/hash/deck.hash.js"></script>
<script src="imakewebthings-deck.js-9accb16/extensions/menu/deck.menu.js"></script>
<script src="imakewebthings-deck.js-9accb16/extensions/goto/deck.goto.js"></script>
<script src="imakewebthings-deck.js-9accb16/extensions/status/deck.status.js"></script>
<script src="imakewebthings-deck.js-9accb16/extensions/navigation/deck.navigation.js"></script>
<script src="imakewebthings-deck.js-9accb16/extensions/scale/deck.scale.js"></script>
<!-- Initialize the deck -->
<script>
$(function() {
$.deck('.slide');
$("#fair-coin .demo").on('click', function (event) {
var n = Math.random();
$(this).find("span.x").html(n);
$(this
).toggleClass('heads', n < 0.5
).toggleClass('tails', !(n < 0.5));
return false;
});
$("#die-roll .demo").on('click', function (event) {
var n = Math.floor(Math.random() * 6);
$(this).find("span.x").html(1 + n);
$(this).find("span.die").html(['','','','','',''][n]);
return false;
});
$("#unweighted-selection .demo").on('click', function (event) {
var $l = $(this).find('span.l');
var $s = $(this).find('span.selections');
var lst = $l.data('list');
var sel = $s.data('list');
if (lst === undefined || !lst.length) {
lst = ['', '', '', '', '', ''];
sel = [];
} else {
var n = Math.floor(Math.random() * lst.length);
sel = sel.concat(lst.splice(n, 1));
}
$l.data('list', lst);
$l.html('[' + lst.join(', ') + ']');
$s.data('list', sel);
$s.html('[' + sel.join(', ') + ']');
});
$("div.demo.selection-without-repl").on('click', function (event) {
var $l = $(this).find('span.l');
var $s = $(this).find('span.selections');
var lst = $l.data('list');
var sel = $s.data('list');
if (lst === undefined || !lst.length || sel.length > 9) {
lst = $(this).data('initial');
sel = [];
} else {
var n = Math.floor(Math.random() * lst.length);
sel.push(lst[n]);
}
$l.data('list', lst);
$l.html('[' + lst.join(', ') + ']');
$s.data('list', sel);
$s.html('[' + sel.join(', ') + ']');
});
$("#alias div.demo").on('click', function (event) {
$(this).find('.selected').toggleClass('selected', false);
var $rows = $(this).find('tbody tr');
var die = Math.floor(Math.random() * $rows.length);
var coin = Math.random();
$(this).find("span.die").html(1 + die);
$(this).find("span.coin").html(coin);
var $die = $rows.eq(die);
$die.toggleClass('selected', true);
if (die == 0 || coin < 2.0/3.0) {
$die.find('td:eq(0)').toggleClass('selected', true);
} else {
$die.find('td:eq(1)').toggleClass('selected', true);
}
});
var heap = [];
var total = 0;
var handleInput = function (word) {
var count = 1;
total += 1;
for (var i = 0; i < heap.length; i++) {
if (heap[i][0] === word) {
count += heap[i][1];
heap.splice(i, 1);
break;
}
}
for (i = i - 1; i >= 0 && heap[i][1] <= count; i--) {
}
heap.splice(i + 1, 0, [word, count]);
var repl = function (str) {
return str
.replace(/&/g, '&amp;')
.replace(/>/g, '&gt;')
.replace(/</g, '&lt;')
.replace(/"/g, '&quot;');
};
var fmt = function (elem) {
return '<tr><td>' + repl(elem[0]) + '</td><td>' + elem[1] + '</td></tr>';
};
$("#max-heap div.demo tbody").html(heap.map(fmt).join(''));
};
$("#max-heap div.demo form").on('submit', function (event) {
var $input = $(this).find('input');
$input.val().split(/\s+/).forEach(handleInput);
$input.val('');
return false;
});
$("#max-heap div.demo button").on('click', function (event) {
var n = 1 + Math.floor(Math.random() * total);
var $tbody = $("#max-heap div.demo table tbody");
$tbody.find('.selected').toggleClass('selected', false);
$rows = $tbody.find('tr');
var n1 = n;
for (var i = 0; i < heap.length; i++) {
$rows.eq(i).toggleClass('selected', true);
n1 -= heap[i][1];
if (n1 <= 0) {
$rows.eq(i).find('td').toggleClass('selected', true);
break;
}
}
return false;
});
});
</script>
</body>
</html>
Jump to Line
Something went wrong with that request. Please try again.