Created
October 29, 2012 00:01
-
-
Save g-k/3970533 to your computer and use it in GitHub Desktop.
Graphs for Kaprekar's Routine
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
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Kaprekar's Routine</title> | |
<link type="text/css" rel="stylesheet" href="style.css"> | |
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/2.10.0/d3.v2.min.js"></script> | |
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.3.1/underscore-min.js"></script> | |
<script src="kaprekate.js"></script> | |
<script src="renderSequence.js"></script> | |
<script src="renderLineChart.js"></script> | |
<script src="k.js"></script> | |
</head> | |
<body> | |
<h1>Kaprekar's Routine</h1> | |
<noscript>Please enable Javascript.</noscript> | |
<div id="description"> | |
<p>From the <a href="http://en.wikipedia.org/wiki/6174_(number)">Wikipedia Article</a>: | |
</p> | |
<ol> | |
<li>Take any number. (Leading zeros are allowed.) | |
<li>Arrange the digits in ascending order. | |
<li>Arrange the digits in descending order to get two numbers with the same number of digits (Add leading zeros if necessary). | |
<li>Subtract the smaller number from the bigger number. | |
<li>Repeat. | |
</ol> | |
</div> | |
<div id="input"> | |
<p>Starting value: | |
<input id="seed" class="number" type="number" value="900" autofocus> | |
</div> | |
<div id="graph"></div> | |
</body> | |
</html> |
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
var sequences = []; | |
var sequence; | |
var length = 3; | |
var radix = 10; | |
var max = Math.pow(radix, length); | |
for (var j = 0; j < max; j++) { | |
j = j.toString(); | |
while (j.length - length) { j = '0'+j; } | |
sequence = kaprekarSequence(j, kaprekated); | |
sequences.push({ | |
seed: j, | |
sequence: sequence, | |
lastValue: +sequence[sequence.length-1], | |
length: sequence.length | |
}); | |
} | |
var addSvg = function (id, height, width) { | |
return d3.select(id) | |
.append('svg') | |
.attr('width', width) | |
.attr('height', height); | |
}; | |
var init = function init() { | |
var seedEl = document.getElementById('seed'); | |
var margin = { top: 20, right: 10, bottom: 20, left: 60 }; | |
var width = 600; | |
var height = 800; | |
var svg = addSvg('#graph', height, width); | |
var sequence = svg.append('g') | |
.attr('transform', 'translate('+margin.left+',20)'); | |
var sequenceLength = svg.append('g') | |
.attr('transform', 'translate('+margin.left+',60)'); | |
var sequenceLengthOptions = { | |
width: 500, | |
height: 300, | |
yLabel: "Sequence Length", | |
yDataKey: 'length', | |
yTickScale: 1, | |
data: sequences, | |
selection: sequenceLength | |
}; | |
renderLineChart(sequenceLengthOptions); | |
var sequenceEnd = svg.append('g') | |
.attr('transform', | |
'translate('+margin.left+','+(120+sequenceLengthOptions.height)+')'); | |
var sequenceEndOptions = { | |
width: 500, | |
height: 300, | |
yLabel: "Sequence End or Repeat Value", | |
yDataKey: 'lastValue', | |
yTickScale: 50, | |
data: sequences, | |
selection: sequenceEnd | |
}; | |
renderLineChart(sequenceEndOptions); | |
var run = function run(event) { | |
var result; | |
if (event.keyCode !== 13) return; // Must press enter | |
result = kaprekarSequence(seedEl.value, kaprekated); | |
renderSequence(result, sequence); | |
}; | |
seedEl.addEventListener('keydown', run, false); | |
}; | |
document.addEventListener('DOMContentLoaded', init, false); |
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
var memoize = function (func, hasher, memo) { | |
// _.memoize but allow passing in a memo object | |
memo || (memo = {}); | |
hasher || (hasher = _.identity); | |
return function () { | |
var key = hasher.apply(this, arguments); | |
return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); | |
}; | |
}; | |
var arrayToNumber = function (a) { | |
// convert an array of digits to a number | |
var n = 0; | |
var place_value = 1; | |
var place = a.length; | |
while (place--) { | |
n += a[place] * place_value; | |
place_value *= 10; | |
} | |
return n; | |
}; | |
var kaprekate = function(seed) { | |
// runs a step of kaprekar's routine on seed string and returns a string | |
var n, | |
i, | |
smallToLarge, // array of digits from small to large | |
largeToSmall; // from large to small | |
n = seed; | |
i = seed.length; | |
// smallToLarge = [parseInt(digit) for each (digit in n)].sort(); | |
smallToLarge = []; | |
while (i--) { smallToLarge.unshift(n[i]); } | |
// sort() and reverse() work in-place | |
// so slice to make a second deep copy in memory | |
smallToLarge = smallToLarge.sort(); // sort as strings | |
largeToSmall = smallToLarge.slice().reverse(); | |
n = arrayToNumber(largeToSmall) - arrayToNumber(smallToLarge); | |
n = n.toString(); | |
// prepend zeros to maintain length | |
i = seed.length; | |
while (n.length - i) { n = '0'+n; } | |
return n; | |
}; | |
var kaprekated = {}; | |
var memoizedKaprekate = memoize(kaprekate, null, kaprekated); | |
var kaprekarSequence = function (seed, memo) { | |
var prev = seed; | |
var next; | |
var sequence = []; | |
var seen = {}; | |
sequence.push(prev); // Include seed | |
while (!_.has(seen, prev)) { | |
next = memoizedKaprekate(prev); | |
seen[prev] = next; | |
sequence.push(next); | |
prev = next; | |
} | |
return sequence; | |
}; |
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
var renderLineChart = function (options) { | |
var data = options.data; | |
var selection = options.selection; | |
var height = options.height; | |
var width = options.width; | |
var x = d3.scale.linear() | |
.range([0, width]) | |
.domain([parseInt(data[0].seed, 10), | |
parseInt(data[data.length-1].seed, 10)]); | |
var y = d3.scale.linear() | |
.range([height, 0]) | |
.domain([0, _.max(_.pluck(data, options.yDataKey))+options.yTickScale]); | |
var line = d3.svg.line() | |
.x(function (d) { | |
return x(parseInt(d.seed, 10)); }) | |
.y(function (d) { return y(d[options.yDataKey]); }); | |
selection.append("path") | |
.datum(data) | |
.attr("class", "line") | |
.attr("d", line); | |
var xAxis = d3.svg.axis() | |
.scale(x) | |
.orient("bottom"); | |
var yAxis = d3.svg.axis() | |
.scale(y) | |
.orient("left"); | |
selection.append("g") | |
.attr("class", "x axis") | |
.attr("transform", "translate(0," + height + ")") | |
.call(xAxis); | |
selection.append("g") | |
.attr("class", "y axis") | |
.call(yAxis) | |
.append("text") | |
.attr("transform", "rotate(-90)") | |
.attr("y", -40) | |
.attr("dy", ".71em") | |
.style("text-anchor", "end") | |
.text(options.yLabel); | |
}; |
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
var renderSequence = function (sequence, selection) { | |
var nodeSeparation = 50; | |
var nodeRadius = 4.5; | |
var newNodes, nodes, links; | |
// Redraw everything -> empty update selection | |
var dataKey = function (d, i) { return Math.random(); }; | |
var linkPath = function (d, i) { | |
if (i > 0) { | |
return [ | |
'M', (nodeSeparation*(i-1)+nodeRadius), 0, | |
'L', (nodeSeparation*i-nodeRadius), 0 | |
].join(' '); | |
} | |
return null; | |
}; | |
var delayByIndex = function (delay) { | |
return function (d, i) { return delay*i; }; | |
}; | |
var translateByIndex = function (d, i) { | |
return 'translate('+(nodeSeparation*i)+',0)'; | |
}; | |
var fadeOut = function (selection) { | |
// CSS exit class handles transition | |
return selection.attr('class', 'exit').remove(); | |
}; | |
links = selection.selectAll('.link') | |
.data(sequence, dataKey); | |
links.enter() | |
.append('path') | |
.attr('class', 'link') | |
.style('opacity', 0) | |
.transition() | |
.delay(delayByIndex(200)) | |
.duration(400) | |
.attr('d', linkPath) | |
.style('opacity', 1); | |
links.exit().call(fadeOut); | |
nodes = selection.selectAll('.node') | |
.data(sequence, dataKey); | |
newNodes = nodes.enter() | |
.append('g') | |
.attr('class', 'node') | |
.attr('transform', translateByIndex); | |
newNodes.append('text') | |
.attr("dx", 8).attr("dy", 10) | |
.style('opacity', 0) | |
.transition() | |
.duration(400) // Match CSS transition duration | |
.delay(delayByIndex(205)) | |
.text(function(d) { return d.toString(); }) | |
.style('opacity', 1); | |
newNodes.append('circle') | |
.transition() | |
.delay(delayByIndex(200)) | |
.duration(400) | |
.attr('r', nodeRadius); | |
nodes.exit().call(fadeOut); | |
}; |
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
/* From another block or d3 example*/ | |
body { | |
font: 14px Helvetica Neue; | |
text-rendering: optimizeLegibility; | |
margin-top: 1em; | |
overflow-y: scroll; | |
} | |
svg { | |
font: 10px sans-serif; | |
border: gray 1px solid; | |
} | |
.node circle { | |
fill: #fff; | |
stroke: steelblue; | |
stroke-width: 1.5px; | |
} | |
.exit { | |
transition-property opacity; | |
opacity: 0; | |
transition-duration: 0.4s; | |
} | |
.link { | |
fill: none; | |
stroke: #ccc; | |
stroke-width: 1.5px; | |
} | |
/* From: http://bl.ocks.org/3902569 */ | |
.line { | |
fill: none; | |
stroke: steelblue; | |
stroke-width: 1.5px; | |
} | |
.axis path, .axis line { | |
fill: none; | |
stroke: #000; | |
shape-rendering: crispEdges; | |
} | |
.y.axis path, .x.axis path { | |
display: none; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment