tag:blogger.com,1999:blog-101061922024-03-14T09:12:17.960-04:00Andrew Tweddle's BlogI'm a South African software developer based in the technology hub of Kitchener-Waterloo, Ontario.
I enjoy solving mathematical and algorithmic puzzles. These are some of my thoughts...Andrew Tweddlehttp://www.blogger.com/profile/02458479910895055369noreply@blogger.comBlogger29125tag:blogger.com,1999:blog-10106192.post-40009484326280423072016-09-02T17:41:00.003-04:002023-12-21T21:35:44.817-05:00An animated visual explanation of the sum of squares formula<!-- From http://irrep.blogspot.com/2011/07/mathjax-in-blogger-ii.html: -->
<script src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML" type="text/javascript">
MathJax.Hub.Config({
HTML: ["input/TeX","output/HTML-CSS"],
TeX: { extensions: ["AMSmath.js","AMSsymbols.js"],
equationNumbers: { autoNumber: "AMS" } },
extensions: ["tex2jax.js"],
jax: ["input/TeX","output/HTML-CSS"],
tex2jax: { inlineMath: [ ['$','$'], ["\\(","\\)"] ],
displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
processEscapes: true },
"HTML-CSS": { availableFonts: ["TeX"],
linebreaks: { automatic: true } }
});
</script>
<script src="https://code.jquery.com/jquery-1.12.0.min.js"></script>
<style>
.ssq4-demo-table {
border-collapse: collapse;
background-color: white;
}
.ssq4-demo-table td, .ssq4-demo-table th {
border: solid;
border-width: 1px;
padding: 5px;
height: 20px;
}
.ssq4-demo-table__3cols {
width: 240px;
}
.ssq4-header-dark {
background-color: #aac3d5;
}
.ssq4-header-light {
background-color: #aad5d2;
}
.ssq4-demo-table--standard-column {
width: 80px;
}
.ssq4-demo-table--wide-column {
width: 150px;
}
.ssq4-demo-table__slider-column {
max-width: 25px;
background-color: white;
}
.ssq4-demo-table__vertslider {
margin-left: -40px;
width: 100px;
height: 8px;
padding: 0 5px;
transform: rotate(270deg);
-moz-transform: rotate(270deg);
}
.ssq4-demo-table--selected-row {
background-color: Pink;
color: Black;
}
.ssq4-demo-table--value {
text-align: center;
}
.ssq4-selectors-table td {
padding: 5px;
}
.ssq4-formula-parts-table {
border-collapse: collapse;
background-color: white;
}
.ssq4-formula-parts-table td, .ssq4-formula-parts-table th {
border: solid;
border-width: 1px;
padding: 15px;
}
.ssq4-grid-and-table {
display: inline-block;
margin: 15px;
}
.ssq4-grid {
border-collapse: collapse;
table-layout: fixed;
}
.ssq4-grid > tbody > tr > td {
width : 6px;
height: 6px;
/* For debugging layout...
border: solid;
border-width: thin;
*/
}
.ssq4-grid__cell {
border: solid DodgerBlue;
border-width: thin;
}
.ssq4-grid__top-left-of-square, .ssq4-grid__square {
background-color: white;
border: solid DodgerBlue;
border-width: thin;
}
.ssq4-grid__non-slice { background-color: White; }
.ssq4-grid__any-slice { background-color: Yellow; }
.ssq4-grid__valid-top-left-of-square { background-color: Gold; }
.ssq4-grid__top-left-of-square {
background-color: HotPink;
border: none;
border-width: thin;
}
.ssq4-grid__square {
background-color: Pink;
border: none;
border-width: thin;
}
.ssq4-grid__column-tick {
border-left-width: thin;
border-left-style: dotted;
border-left-color: DarkGray;
}
.ssq4-grid__column-index-td {
height: 17px;
width: 17px;
}
.ssq4-grid__column-outer-edge-top {
border-bottom-width: thin;
border-bottom-style: solid;
border-bottom-color: DodgerBlue;
}
.ssq4-grid__column-outer-edge-bottom {
border-top-width: thin;
border-top-style: solid;
border-top-color: DodgerBlue;
}
.ssq4-grid__column-outer-edge-left {
border-right-width: thin;
border-right-style: solid;
border-right-color: DodgerBlue;
}
.ssq4-grid__column-outer-edge-right {
border-left-width: thin;
border-left-style: solid;
border-left-color: DodgerBlue;
}
.ssq4-table {
border-collapse: collapse;
margin: 10px;
/*bgcolor: GhostWhite;*/
background-color: White;
}
.ssq4-table td, .ssq4-table th {
border: solid;
border-width: thin;
padding: 5px;
}
.blogger-clickTrap { display: none!important; }
</style>
<h3>Overview</h3>
<p>
This is the fourth in a series of posts about a recreational maths problem - counting the number of squares (of all sizes) on a chessboard. The first three posts have shown that the number of squares of various sizes in an nxn grid is $\sum_{{i}={1}}^{n}i^2 = \frac{n(n + 1)(2n + 1)}{6}$. In this post we will solve the problem in a different way that will explain the structure of that formula.
</p>
<h3>All posts in this series</h3>
<ul>
<li><a href="http://andrewtweddle.blogspot.com/2015/06/counting-squares-in-grid-using-sum-of.html">Counting squares in a grid</a></li>
<li><a href="http://andrewtweddle.blogspot.com/2015/12/guessing-closed-form-solution-for-sum.html">Guessing a closed form solution for the sum of squares</a></li>
<li><a href="http://andrewtweddle.blogspot.ca/2015/12/a-combinatorial-solution-to-sum-of.html">A combinatorial solution for counting squares in a grid</a></li>
</ul>
<hr />
<h3>A recap</h3>
<p>
In <a href="http://andrewtweddle.blogspot.com/2015/06/counting-squares-in-grid-using-sum-of.html">the first post of the series</a> I showed that the number of squares in an n by n grid is $\sum_{{i}={1}}^{n}i^2$.
</p>
<p>
In <a href="http://andrewtweddle.blogspot.com/2015/12/guessing-closed-form-solution-for-sum.html">the second post</a> I used algebra to show that this summation equals $\frac{n(n + 1)(2n + 1)}{6}$.
</p>
<p>
But can we find a more direct link between this formula and the number of squares in an n x n grid? Preferably one that explains each part of the formula.
</p>
<p>
In this post I'm going to use an animated graphical demo to show you how to get to the formula directly.
This will help us to build an intuition about the problem.
In later blog posts we'll use that intuition to derive a very elegant solution.
</p>
<hr />
<h3>tl;dr</h3>
<p>
If you don't feel like reading the full explanation, you can jump straight to the animations:
</p>
<a href="#ssq4-start-of-demo">Skip straight to the first demo!</a>
<br />
<a href="#ssq4-demo-2">Skip to the second demo</a>
<hr />
<h3>The solution approach</h3>
<p>
The formula can be rewritten as follows:
$$
\begin{align*}
f(n) & = \frac{n(n + 1)(2n + 1)}{6} \\
& = \frac{n(n + 1)}{2} \frac{2n + 1}{3} \\
& = \binom{n + 1}{2} . \frac{1}{3} . (2n + 1)\\
\end{align*}
$$
</p>
<p>
I'll explain the three parts of this formula separately:
<ol>
<li>There are $\binom{n + 1}{2}$ ways of selecting a vertical "slice" or subset of an n by n grid.</li>
<li>The $\frac{1}{3}$ is because we're going to do this in three different ways.</li>
<li>And we'll do this in a clever way, so that the number of squares that exactly fit the width of the 3 slices will always add up to $2n + 1$</li>
</ol>
</p>
<p>
Below is an animated demonstration for $n = 8$. You can see the 3 grids and an initial set of vertical slices of each grid.
</p>
<p>
When you click the start button, the demonstration will run through all the possible ways of generating vertical slices of the 3 grids.
A pink square will slide down each vertical slice.
The top left corner of the square is highlighted in a darker colour.
As the pink square slides down the yellow slice, the dark pink corner will leave behind a "residue" of golden squares.
This will make it easier to count the total number of squares that fit the width of the 3 grids.
</p>
<p>
Below the grids is a table which will be used to keep track of the total count.
As the animation is running, watch how the rightmost column of the table always adds up to 17 (i.e. $2n+1$).
</p>
<hr />
<h3>An interactive demonstration</h3>
<a name="ssq4-start-of-demo"></a>
<p>
<button id="ssq4-demo-button-start">Start</button>
<button id="ssq4-demo-button-pause">Pause</button>
<button id="ssq4-demo-button-step">Step</button>
<button id="ssq4-demo-button-resume">Resume</button>
Speed:
<input name="demoSpeeds" id="ssq4-demo__slow-radio-button" type="radio" value="0">
Slow
</input>
<input name="demoSpeeds" id="ssq4-demo__medium-radio-button" type="radio" value="1" checked="checked">
Medium
</input>
<input name="demoSpeeds" id="ssq4-demo__fast-radio-button" type="radio" value="2">
Fast
</input>
<input name="demoSpeeds" id="ssq4-demo__very-fast-radio-button" type="radio" value="3">
Very fast
</input>
<a href="#ssq4-start-of-demo">Align window to here</a>
<a href="#ssq4-demo-2">Next demo</a>
</p>
<div class="ssq4-grid-and-table">
<b>Grid A</b>
<p>
<table class="ssq4-grid" id="ssq4-demo-grid-1">
<tbody>
<!-- Header -->
<tr>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">0</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">1</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">2</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">3</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">4</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">5</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">6</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">7</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">8</div></td>
</tr>
<tr>
<td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td>
</tr>
<tr>
<td></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick" ></td>
</tr>
<!-- Row 0 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 1 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 2 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 3 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 4 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 5 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 6 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 7 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- footer row -->
<tr>
<td></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td></td>
</tr>
</tbody>
</table>
</p>
</div>
<div class="ssq4-grid-and-table">
<b>Grid B</b>
<p>
<table class="ssq4-grid" id="ssq4-demo-grid-2">
<tbody>
<!-- Header -->
<tr>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">0</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">1</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">2</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">3</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">4</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">5</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">6</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">7</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">8</div></td>
</tr>
<tr>
<td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td>
</tr>
<tr>
<td></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick" ></td>
</tr>
<!-- Row 0 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 1 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 2 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 3 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 4 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 5 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 6 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 7 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- footer row -->
<tr>
<td></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td></td>
</tr>
</tbody>
</table>
</p>
</div>
<div class="ssq4-grid-and-table">
<b>Grid C</b>
<p>
<table class="ssq4-grid" id="ssq4-demo-grid-3">
<tbody>
<!-- Header -->
<tr>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">0</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">1</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">2</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">3</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">4</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">5</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">6</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">7</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">8</div></td>
</tr>
<tr>
<td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td>
</tr>
<tr>
<td></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick" ></td>
</tr>
<!-- Row 0 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 1 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 2 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 3 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 4 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 5 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 6 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 7 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- footer row -->
<tr>
<td></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td></td>
</tr>
</tbody>
</table>
</p>
</div>
<table class="ssq4-demo-table">
<thead>
<tr class="ssq4-header-dark">
<th colspan="3" class="ssq4-demo-table__3cols">Combinations (5 at a time)</th>
<th colspan="3" class="ssq4-demo-table__3cols">Pink squares per slice</th>
<th>Total squares</th>
<th></th>
</tr>
<tr class="ssq4-header-light">
<th class="ssq4-demo-table--standard-column">#</th>
<th class="ssq4-demo-table--standard-column">i</th>
<th class="ssq4-demo-table--standard-column">j</th>
<th class="ssq4-demo-table--standard-column">A</th>
<th class="ssq4-demo-table--standard-column">B</th>
<th class="ssq4-demo-table--standard-column">C</th>
<th class="ssq4-demo-table--wide-column">A + B + C</th>
<th></th>
</tr>
</thead>
<tbody>
<!-- row 1 -->
<tr>
<td class="ssq4-demo-table-combination ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-i ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-j ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-A ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-B ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-C ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-ABC ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td rowspan="5" class="ssq4-demo-table__slider-column">
<input type="range" id="ssq4-combinationsSlider"
class="ssq4-demo-table__vertslider"
min="1" max="32" value="32"
/>
</td>
</tr>
<!-- row 2 -->
<tr>
<td class="ssq4-demo-table-combination ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-i ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-j ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-A ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-B ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-C ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-ABC ssq4-demo-table--value ssq4-demo-table__cell"></td>
</tr>
<!-- row 3 -->
<tr>
<td class="ssq4-demo-table-combination ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-i ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-j ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-A ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-B ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-C ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-ABC ssq4-demo-table--value ssq4-demo-table__cell"></td>
</tr>
<!-- row 4 -->
<tr>
<td class="ssq4-demo-table-combination ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-i ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-j ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-A ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-B ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-C ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-ABC ssq4-demo-table--value ssq4-demo-table__cell"></td>
</tr>
<!-- row 5 -->
<tr>
<td class="ssq4-demo-table-combination ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-i ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-j ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-A ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-B ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-C ssq4-demo-table--value ssq4-demo-table__cell"></td>
<td class="ssq4-demo-table-ABC ssq4-demo-table--value ssq4-demo-table__cell"></td>
</tr>
<tbody>
<tfoot>
<tr class="ssq4-header-light">
<td colspan="3" align="right">Total (all combinations):</td>
<td class="ssq4-demo-table-A-total ssq4-demo-table--value"></td>
<td class="ssq4-demo-table-B-total ssq4-demo-table--value"></td>
<td class="ssq4-demo-table-C-total ssq4-demo-table--value"></td>
<td class="ssq4-demo-table-ABC-total ssq4-demo-table--value"></td>
<td></td>
</tr>
<tr class="ssq4-header-dark">
<td colspan="3">$\binom{n + 1}{2} = \frac{n(n+1)}{2} = \frac{8 (8 + 1)}{2} = 36$</td>
<td colspan="3">$f(n) + f(n) + f(n) = 3 f(n)$</td>
<td>$ = \binom{n + 1}{2} (2n + 1)$</td>
<td></td>
</tr>
</tfoot>
</table>
<p>
<b>Note: </b>
Once the table has been fully populated, you will be able to:
<ul>
<li>use the slider to scroll through the rows</li>
<li>select an individual row by clicking on it</li>
</ul>
</p>
<hr />
<h3>Explanation</h3>
<p>
I'll explain the solution in detail further down. But the essence of it is to:
<ul>
<li>Choose a pair of numbers from the set {0, 1, ..., n}.</li>
<li>Find three different ways of defining a vertical "slice" of the grid using that pair of numbers.</li>
<li>Count the number of squares that fit the width $w$ of each slice exactly i.e. count how many $w$ x $w$ squares fit into the slice.</li>
<li>The number of squares in each of the slices depends on the width $w$ of that slice, which depends on the specific pair of numbers chosen.</li>
<li>But add the number of $w$ x $w$ squares in all three slices, and the answer is always $2n+1$, regardless of the numbers chosen.</li>
</ul>
</p>
<p>
I've created an interactive demonstration below which allows you to choose a pair of numbers and see the slices generated.
Play around with it. You may be able to understand the solution without needing the explanation that follows.
</p>
<h4>Choose a pair of distinct numbers between 0 and n</h4>
<p>
First we choose any two numbers from the set $\{0, 1, ..., n\}$.
</p>
<p>
Let $i$ be the smaller and $j$ the larger of the two, so that $0 \le i \lt j \le n$.
</p>
<h4>A data representation</h4>
<p>
For each of the 3 grids we will use $i$ and $j$ to generate a slice of the grid. We will label the left and right edges of each grid $p$ and $q$.
</p>
<p>
<img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgh7OmAYwndEJZ5dikXql2Ye-HNGwrPaWBliw9fNjz8M8hahXyXvUGWD37JSVdI0FYefepb_qzlpid6J1kVfHWivNaCXSIxhXS4ynWZCvSrDZqR8qWugSkHLx0aXP2aQWE-zH8/s1600/SumOfSquares_Slices_OriginalDataRepresentation.PNG" />
<img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhsyWSAKn11-lL7K1YJJ9iacVEvjZDI5jG5JSdxELdC7T0M1Dnlfd59v3L3Mvywnl6ATeFJf7WAsaZits_MVPPRoc6JfZliL0ydFfV-4e0oTuj7tSp3NO3fid6wAGDJRC5r9g/s1600/SumOfSquares_Slices_KeyToOriginalDataRepresentation.PNG" />
</p>
<h4>Another demonstration</h4>
The previous demo showed all possible pink squares that fit into each vertical slice.
This demo shows the possible top left hand corners of the squares in a golden colour.
The number of golden squares is the total number of squares that fit into the 3 slices for a particular combination of $(i, j)$.
<p>
<table class="ssq4-selectors-table">
<!-- Create an anchor to reposition to: -->
<tr>
<!-- The td is just a spacer to give a slight margin -->
<td height="5px;">
<!-- The anchor to align to: -->
<a name="ssq4-demo-2"></a>
</td>
</tr>
<tr>
<td>Choose: </td>
<td>$i$</td>
<td>
<select id="ssq4-i-select">
<option class="ssq4-i-option">0</option>
<option class="ssq4-i-option">1</option>
<option class="ssq4-i-option">2</option>
<option class="ssq4-i-option">3</option>
<option class="ssq4-i-option">4</option>
<option class="ssq4-i-option">5</option>
<option class="ssq4-i-option">6</option>
<option class="ssq4-i-option">7</option>
</select>
</td>
<td>$j$</td>
<td>
<select id="ssq4-j-select">
<option class="ssq4-j-option">1</option>
<option class="ssq4-j-option">2</option>
<option class="ssq4-j-option">3</option>
<option class="ssq4-j-option">4</option>
<option class="ssq4-j-option">5</option>
<option class="ssq4-j-option">6</option>
<option class="ssq4-j-option">7</option>
<option class="ssq4-j-option">8</option>
</select>
</td>
<td><a href="#ssq4-demo-2">Align window to here</a></td>
<td>
<a href="#ssq4-start-of-demo">Previous demo</a>
</td>
</tr>
</table>
</p>
<p>
<button id="ssq4-choose-i-and-j">
Choose random i and j
</button>
<button id="ssq4-animate-i-and-j">
Cycle through all i and j values
</button>
<button id="ssq4-stop-animating-i-and-j" disabled="true">
Stop cycling through values
</button>
</p>
<div class="ssq4-grid-and-table">
<b>Grid A</b>
<p>
<table class="ssq4-grid" id="ssq4-interactive-grid-1">
<tbody>
<!-- Header -->
<tr>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">0</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">1</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">2</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">3</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">4</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">5</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">6</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">7</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">8</div></td>
</tr>
<tr>
<td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td>
</tr>
<tr>
<td></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick" ></td>
</tr>
<!-- Row 0 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 1 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 2 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 3 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 4 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 5 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 6 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 7 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- footer row -->
<tr>
<td></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td></td>
</tr>
</tbody>
</table>
</p>
<table class="ssq4-table">
<thead>
<tr class="ssq4-header-dark">
<th>A</th>
<th width="100px">Formula</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>$p$</td>
<td>$i$</td>
<td id="ssq4-table1-p"></td>
</tr>
<tr>
<td>$q$</td>
<td>$j$</td>
<td id="ssq4-table1-q"></td>
</tr>
<tr>
<td>$w$</td>
<td>$j - i$</td>
<td id="ssq4-table1-w"></td>
</tr>
<tr>
<td>$s$</td>
<td>$n - j + i + 1$</td>
<td id="ssq4-table1-s"></td>
</tr>
</tbody>
</table>
</div>
<div class="ssq4-grid-and-table">
<b>Grid B</b>
<p>
<table class="ssq4-grid" id="ssq4-interactive-grid-2">
<tbody>
<!-- Header -->
<tr>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">0</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">1</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">2</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">3</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">4</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">5</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">6</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">7</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">8</div></td>
</tr>
<tr>
<td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td>
</tr>
<tr>
<td></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick" ></td>
</tr>
<!-- Row 0 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 1 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 2 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 3 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 4 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 5 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 6 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 7 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- footer row -->
<tr>
<td></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td></td>
</tr>
</tbody>
</table>
</p>
<table class="ssq4-table">
<thead>
<tr class="ssq4-header-dark">
<th>B</th>
<th width="100px">Formula</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>$p$</td>
<td>$n - j$</td>
<td id="ssq4-table2-p"></td>
</tr>
<tr>
<td>$q$</td>
<td>$n - j + i + 1$</td>
<td id="ssq4-table2-q"></td>
</tr>
<tr>
<td>$w$</td>
<td>$i + 1$</td>
<td id="ssq4-table2-w"></td>
</tr>
<tr>
<td>$s$</td>
<td>$n - i$</td>
<td id="ssq4-table2-s"></td>
</tr>
</tbody>
</table>
</div>
<div class="ssq4-grid-and-table">
<b>Grid C</b>
<p>
<table class="ssq4-grid" id="ssq4-interactive-grid-3">
<tbody>
<!-- Header -->
<tr>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">0</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">1</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">2</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">3</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">4</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">5</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">6</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">7</div></td> <td></td>
<td rowspan="2" colspan="2" class="ssq4-grid__column-index-td"><div class="grid-column-index">8</div></td>
</tr>
<tr>
<td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td>
</tr>
<tr>
<td></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick ssq4-grid__column-outer-edge-top" ></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-outer-edge-top"></td>
<td class="ssq4-grid__column-tick" ></td>
</tr>
<!-- Row 0 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 1 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 2 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 3 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 4 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 5 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 6 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- Row 7 -->
<tr>
<td class="ssq4-grid__column-outer-edge-left"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td rowspan="3" colspan="3" class="ssq4-grid__cell"></td>
<td class="ssq4-grid__column-outer-edge-right"></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<tr>
<td></td> <td></td>
</tr>
<!-- footer row -->
<tr>
<td></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td colspan="3" class="ssq4-grid__column-outer-edge-bottom"></td>
<td></td>
</tr>
</tbody>
</table>
</p>
<table class="ssq4-table">
<thead>
<tr class="ssq4-header-dark">
<th>C</th>
<th width="100px">Formula</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>$p$</td>
<td>$j - i - 1$</td>
<td id="ssq4-table3-p"></td>
</tr>
<tr>
<td>$q$</td>
<td>$n - i$</td>
<td id="ssq4-table3-q"></td>
</tr>
<tr>
<td>$w$</td>
<td>$n - j + 1$</td>
<td id="ssq4-table3-w"></td>
</tr>
<tr>
<td>$s$</td>
<td>$j$</td>
<td id="ssq4-table3-s"></td>
</tr>
</tbody>
</table>
</div>
<h4>Squares in all three slices</h4>
<!-- TODO: Re-enable after the presentation
<p>
Add up the number of squares which fit into the three slices.
This is the number of gold blocks in the three grids, since those are possible top left corners of the $w$ x $w$ squares.
You will find that the answer is always $2n + 1 = 17$:
</p>
-->
<p>
<table class="ssq4-table">
<thead class="ssq4-header-dark">
<tr>
<th>Grid</th>
<th>Size of squares ($w$ x $w$)</th>
<th width="100px">Formula for $s$</th>
<th>Value (# of squares)</th>
</tr>
</thead>
<tbody>
<tr>
<td>A</td>
<td id="ssq4-totals-table1-wxw" ></td>
<td>$n - j + i + 1$</td>
<td id="ssq4-totals-table1-s"></td>
</tr>
<tr>
<td>B</td>
<td id="ssq4-totals-table2-wxw" ></td>
<td>$n - i$</td>
<td id="ssq4-totals-table2-s"></td>
</tr>
<tr>
<td>C</td>
<td id="ssq4-totals-table3-wxw" ></td>
<td>$j$</td>
<td id="ssq4-totals-table3-s"></td>
</tr>
<tr>
<td></td>
<td><b>Total:</b></td>
<td>$2n + 1$</td>
<td>17</td>
</tr>
</tbody>
</table>
</p>
<h4>A few notes</h4>
<p>
I suggest taking a few moments to satisfy yourself that $p$ and $q$ are always valid for the three grids i.e. that $ 0 \le p \lt q \le n $.
This can be deduced from equation (1) i.e. $0 \le i \lt j \le n$.
</p>
<p>
To be completely rigorous, we should also verify that $p$ and $q$ can map to any pair of integers from the set {0, 1, ..., n}, otherwise there could be squares which aren't being counted.
I'm not going to go to those lengths, because the purpose of this blog post is to give you an intuitive understanding of why the sum of squares formula has the form it does.
<i>
[Hint: Show that the mappings from ($i$, $j$ ) -> ( $p$, $q$ ) are bijections (one-to-one).]
</i>
</p>
<p>
Also take a look at the table of calculations. Notice how the $i$ and $j$ variables cancel out when you add the three formulae.
This causes the total number of squares in the 3 slices to always be the same, no matter which pair of $i$ and $j$ values we choose.
</p>
<hr />
<h3>A detailed explanation</h3>
<p>
Recall the formula we derived previously:
$$
\begin{align*}
\frac{n(n + 1)(2n + 1)}{6} & = \frac{n(n + 1)}{2} \frac{2n + 1}{3} \\
& = \binom{n + 1}{2} \frac{1}{3} (2n + 1)\\
\end{align*}
$$
</p>
<p>
This gives a few clues for solving the problem. I'm going to break the formula into separate parts. Later I'll show you how to solve each part. Here's a brief overview of how each part of the formula will be used:
<table class="ssq4-formula-parts-table">
<tr class="ssq4-header-dark">
<th>#</th>
<th>Expression</th>
<th style="text-align:left">Notes</th>
</tr>
<tr>
<td>1.</td>
<td>$ \binom{n + 1}{2} $</td>
<td>
Select two numbers from the set {0, 1, ..., n}.<br/>
Treat these as the left and right edge of a vertical slice of the grid.<br/>
This combination gives all possible ways of slicing the grid.
</td>
</tr>
<tr>
<td>2.</td>
<td>$ \frac{1}{3} $</td>
<td>
Find two other ways of using the same two numbers to define vertical slices of the grid.<br/>
This gives three different ways of partitioning the grid into slices.<br/>
Add up all the squares that fit exactly in the 3 slices defined by the pair of numbers.<br/>
But that means that every square in the final total is going to be counted 3 times.<br/>
So we need to divide the total by 3.
</td>
</tr>
<tr>
<td>3.</td>
<td>$ 2n + 1 $</td>
<td>
Count the number of squares which fit into each slice exactly (i.e. with the same width as the slice).<br />
Each pair of numbers gives 3 different vertical slices.<br />
Each slice will accommodate a different number of squares.<br />
But together the 3 slices are always going to have exactly $2n + 1$ squares.
</td>
</tr>
</table>
</p>
<hr />
<h3>Conclusion</h3>
<p>
This post gives us an intuitive idea of why the formula for the sum of squares looks like $\frac{n(n + 1)(2n + 1)}{6}$.
</p>
<b>But can we do better than this?</b>
<p>
Where do the three mappings from $(i, j)$ to $(p, q)$ come from? Must we rely on trial and error to determine them. Or is there a more satisfying way of deriving them?
</p>
<p>
And where does the $2n + 1$ term come from? We used algebraic cancellation to determine it. But can we find a more intuitive explanation for why it must have that specific form?
</p>
<p>
In the next few blog posts, I am going to answer these questions. In the process we're going to derive a very elegant solution which addresses each of these questions.
</p>
<script>
(function() {
'use strict';
// ----------
// Constants:
const n = 8;
const COMBINATION_COUNT = n * (n + 1) / 2;
const GRID_NAMES_BY_GRID_NO = ["A", "B", "C"]
// ------------------
// Utility functions:
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// ===============
// Shared classes:
// -----------
// Grid class:
function Grid(grid_size, grid_prefix, grid_no, init_p, init_q) {
this.n = grid_size;
this.name = grid_prefix + grid_no;
this.gridNo = grid_no;
this.y = -1; // If -1, don't show a square, else y is the top row of the square that fits into the slice
this.areValidTopLeftSquaresVisible = false;
this.setSlice(init_p, init_q);
}
Grid.prototype.sliceWidth = function() { return this.q - this.p; }
Grid.prototype.squaresInSlice = function() { return this.n + 1 - this.q + this.p; }
Grid.prototype.show = function() {
function classByTdIndices(index) {
// NB: "index" is index of td across all rows, not just in the current tr
var startY = self.y;
var endY = -1;
if (startY >= 0) {
endY = startY + self.sliceWidth() - 1
}
var colIndex = $(this).index() - 1;
if ((colIndex < self.p) || (colIndex >= self.q)) {
return "ssq4-grid__non-slice";
}
var rowIndex = Math.floor( $(this).parent().index() / 3 ) - 1;
if (colIndex == self.p) {
if (rowIndex == startY) { return "ssq4-grid__top-left-of-square"; }
if ((rowIndex > startY) && (rowIndex <= endY)) { return "ssq4-grid__square"; }
if (self.areValidTopLeftSquaresVisible) {
var maxRowIndexOfValidTopLeftCorners = self.n - self.sliceWidth();
if (startY >= 0) {
maxRowIndexOfValidTopLeftCorners = startY - 1;
}
if (rowIndex <= maxRowIndexOfValidTopLeftCorners) { return "ssq4-grid__valid-top-left-of-square"; }
}
}
if ((rowIndex >= startY) && (rowIndex <= endY)) { return "ssq4-grid__square"; }
return "ssq4-grid__any-slice";
}
var tdSel = "table#" + this.name + " > tbody > tr > td.ssq4-grid__cell";
// debug: alert(tdSel);
var self = this;
$(tdSel).removeClass().addClass("ssq4-grid__cell").addClass(classByTdIndices);
if ("onSliceChanged" in this) {
this.onSliceChanged();
};
};
Grid.prototype.setSlice = function(new_p, new_q) {
this.p = new_p;
this.q = new_q;
this.show();
};
Grid.prototype.setSquareStartRow = function(new_y) {
this.y = new_y;
this.show();
}
// --------------
// TriGrid class:
function TriGrid(size, grid_name_prefix) {
this.n = size;
this.prefix = grid_name_prefix;
this.grids = [];
this.grids.push(new Grid(size, this.prefix, "1"));
this.grids.push(new Grid(size, this.prefix, "2"));
this.grids.push(new Grid(size, this.prefix, "3"));
};
TriGrid.prototype.setCombination = function(i, j) {
this.i = i;
this.j = j;
this.grids[0].setSlice(i, j);
this.grids[1].setSlice(n - j, n - j + i + 1);
this.grids[2].setSlice(j - i - 1, n - i);
if ("onCombinationChanged" in this) {
this.onCombinationChanged();
}
}
// ==============================
// Classes for the animated demo:
// ------------------
// Calculation class:
function Calculation(combinationNo, i, j, a, b, c) {
this.combinationNo = combinationNo;
this.i = i;
this.j = j;
this.squaresByGrid = [a, b, c];
}
Calculation.prototype.sumOfSquares = function() {
return this.squaresByGrid[0] + this.squaresByGrid[1] + this.squaresByGrid[2];
}
// -----------------
// Aggregator class:
function Aggregator() {
this.combinationNo = 0;
this.calculations = [];
var i = 0;
var j = 1;
for (var c = 1; c <= COMBINATION_COUNT; c++) {
var calc = new Calculation(c, i, j, 0, 0, 0);
this.calculations.push(calc);
j++;
if (j > n) {
i++;
j = i + 1;
}
}
this.totalSquaresByGrid = [0, 0, 0]
}
Aggregator.prototype.clear = function() {
this.combinationNo = 0;
this.calculations.forEach(function(c) { c.squaresByGrid = [0, 0, 0] })
this.totalSquaresByGrid = [0, 0, 0]
}
Aggregator.prototype.incrementSquare = function(gridNo) {
this.calculations[this.combinationNo].squaresByGrid[gridNo]++;
this.totalSquaresByGrid[gridNo]++;
}
Aggregator.prototype.getSquaresForGridAndCombo = function(gridNo, comboNo) {
if (comboNo == undefined) {
comboNo = this.combinationNo
}
return this.calculations[comboNo].squaresByGrid[gridNo];
}
Aggregator.prototype.getTotalSquaresForCombo = function(comboNo) {
if (comboNo == undefined) {
comboNo = this.combinationNo
}
return this.calculations[comboNo].sumOfSquares();
}
Aggregator.prototype.getTotalSquaresForGrid = function(gridNo) {
return this.totalSquaresByGrid[gridNo];
}
Aggregator.prototype.getGrandTotal = function(gridNo, comboNo) {
return this.totalSquaresByGrid[0] + this.totalSquaresByGrid[1] + this.totalSquaresByGrid[2];
}
// -----------
// Demo class:
const SLOW_DEMO_SPEED = 600;
const MEDIUM_DEMO_SPEED = 300;
const FAST_DEMO_SPEED = 100;
const VERY_FAST_DEMO_SPEED = 10;
function Demo() {
this.triGrid = new TriGrid(n, "ssq4-demo-grid-");
this.animationSpeed = MEDIUM_DEMO_SPEED;
this.isPristine = true; // i.e. never been animated
this.isAnimating = false;
this.isPaused = false;
this.agg = new Aggregator();
this.combinationStep = 0;
}
Demo.prototype.updateUIForAnimation = function () {
document.getElementById("ssq4-demo-button-start").disabled = this.isAnimating;
document.getElementById("ssq4-demo-button-pause").disabled = this.isPaused || !this.isAnimating;
document.getElementById("ssq4-demo-button-step").disabled = !(this.isPaused && this.isAnimating);
document.getElementById("ssq4-demo-button-resume").disabled = !this.isPaused;
var slider = document.getElementById("ssq4-combinationsSlider");
slider.disabled = this.isAnimating;
slider.readonly = this.isAnimating;
}
Demo.prototype.stopAnimating = function () {
window.clearInterval(this.timer);
this.isPaused = false;
this.isAnimating = false;
this.isPristine = false;
this.triGrid.grids[2].setSquareStartRow(-1);
this.updateUIForAnimation();
}
Demo.prototype.startAnimation = function () {
this.isAnimating = true;
this.updateUIForAnimation();
this.clearCountsInTable();
this.triGrid.setCombination(0, 1);
this.agg.clear();
this.combinationStep = 0;
this.timer = window.setInterval(this.showNext.bind(this), this.animationSpeed);
}
Demo.prototype.pauseAnimation = function () {
window.clearInterval(this.timer);
this.isPaused = true;
this.updateUIForAnimation();
}
Demo.prototype.stepThroughAnimation = function () {
this.showNext();
}
Demo.prototype.resumeAnimation = function () {
this.isPaused = false;
this.updateUIForAnimation();
this.timer = window.setInterval(this.showNext.bind(this), this.animationSpeed);
}
Demo.prototype.setAnimationSpeed = function (newSpeed) {
if (this.isAnimating && !this.isPaused) {
window.clearInterval(this.timer);
}
this.animationSpeed = newSpeed;
if (this.isAnimating && !this.isPaused) {
this.timer = window.setInterval(this.showNext.bind(this), newSpeed);
}
}
Demo.prototype.clearCountsInTable = function() {
$("table.ssq4-demo-table td.ssq4-demo-table--value").text("");
}
Demo.prototype.clearRow = function(rowNo) {
var tdSel = "table.ssq4-demo-table > tbody > tr:nth-child(" + rowNo + ") > td.ssq4-demo-table--value";
$(tdSel).text("");
}
Demo.prototype.clearSelection = function() {
var tdSel = "table.ssq4-demo-table > tbody > tr > td.ssq4-demo-table--value";
$(tdSel).removeClass("ssq4-demo-table--selected-row");
}
Demo.prototype.selectRow = function(rowNo) {
this.clearSelection();
var tdSel = "table.ssq4-demo-table > tbody > tr:nth-child(" + rowNo + ") > td.ssq4-demo-table--value";
$(tdSel).addClass("ssq4-demo-table--selected-row");
}
Demo.prototype.userSelectedARow = function(tdElement) {
var selectedRowElement = tdElement.parentElement;
var tableElement = selectedRowElement.parentElement;
for (var i = 0; i < tableElement.children.length; i++) {
var childElement = tableElement.children[i];
if (childElement === selectedRowElement) {
this.userSelectedRow(i + 1);
}
}
}
Demo.prototype.userSelectedRow = function(rowNo) {
if (this.isAnimating || this.isPristine) {
return;
}
var slider = document.getElementById("ssq4-combinationsSlider");
var sliderValue = parseInt(slider.value);
var selectedComboNo = 31 - parseInt(sliderValue) + rowNo;
this.agg.combinationNo = selectedComboNo;
var calc = this.agg.calculations[this.agg.combinationNo];
this.triGrid.setCombination(calc.i, calc.j);
this.clearSelection();
this.selectRow(rowNo);
}
Demo.prototype.showCountsForGridAndCurrentCombo = function(gridNo) {
var rowToUpdate = this.agg.combinationNo + 1;
if (rowToUpdate > 5) {
rowToUpdate = 5;
}
this.showCountsInRowForGridAndCombo(rowToUpdate, gridNo, this.agg.combinationNo);
}
Demo.prototype.showCountsInRowForGridAndCombo = function(rowNo, gridNo, combNo, updateRowTotal, updateGridTotal, updateGrandTotal) {
if (combNo == undefined) {
combNo = this.agg.combinationNo
}
if (updateRowTotal == undefined) { updateRowTotal = true; }
if (updateGridTotal == undefined) { updateGridTotal = true; }
if (updateGrandTotal == undefined) { updateGrandTotal = true; }
const rowPrefix = "tbody > tr:nth-child(" + rowNo + ")";
const footerPrefix = "tfoot > tr:first-child";
const gridName = GRID_NAMES_BY_GRID_NO[gridNo];
const allGrids = "ABC";
function updateCell(prefix, suffix, value) {
var cssSelector = "table.ssq4-demo-table > " + prefix + " > td.ssq4-demo-table-" + suffix;
$(cssSelector).text(value == 0 ? "" : value);
}
var squareCount = this.agg.getSquaresForGridAndCombo(gridNo, combNo);
updateCell(rowPrefix, gridName, squareCount);
if (updateRowTotal) {
var comboTotal = this.agg.getTotalSquaresForCombo(combNo);
updateCell(rowPrefix, allGrids, comboTotal);
}
if (updateGridTotal) {
var gridTotal = this.agg.getTotalSquaresForGrid(gridNo);
updateCell(footerPrefix, gridName + "-total", gridTotal);
}
if (updateGrandTotal) {
var grandTotal = this.agg.getGrandTotal();
updateCell(footerPrefix, allGrids + "-total", grandTotal);
}
}
Demo.prototype.showComboInRow = function(rowNo, combNo, updateFooterTotals) {
if (combNo == undefined) {
combNo = this.agg.combinationNo;
}
var calc = this.agg.calculations[combNo];
const rowPrefix = "table.ssq4-demo-table > tbody > tr:nth-child(" + rowNo + ") > td.ssq4-demo-table-";
$(rowPrefix + "combination").text(calc.combinationNo);
$(rowPrefix + "i").text(calc.i)
$(rowPrefix + "j").text(calc.j)
for (var g = 0; g < 3; g++) {
var shouldUpdateFooter = updateFooterTotals && (g == 0);
this.showCountsInRowForGridAndCombo(rowNo, g, combNo, g == 0, shouldUpdateFooter, shouldUpdateFooter);
}
}
Demo.prototype.showRowsToCombination = function(combNo, leaveLastRowBlank) {
if (combNo == undefined) {
combNo = this.agg.combinationNo;
}
var combCount = leaveLastRowBlank ? 4 : 5;
if (this.isAnimating) {
if (this.agg.combinationNo < 4) {
combCount = this.agg.combinationNo + 1;
leaveLastRowBlank = false;
}
this.selectRow(combCount);
} else {
var rowNoOfSelectedCombNo = this.agg.combinationNo - combNo + 5;
if ((rowNoOfSelectedCombNo > 0) && (rowNoOfSelectedCombNo <= 5)) {
this.selectRow(rowNoOfSelectedCombNo);
} else {
this.clearSelection();
}
}
for (var r = combCount; r > 0; r--) {
this.showComboInRow(r, combNo, r == 1);
combNo--;
}
if (leaveLastRowBlank) {
this.clearRow(5);
}
}
Demo.prototype.showNext = function() {
const MAX_STEPS = 18;
const STEPS_AT_START = 1;
const STEPS_BETWEEN_GRIDS = 0;
const STEPS_WITH_SQUARES = 2 * this.triGrid.n + 1;
var last_square_step_no = STEPS_AT_START + 2 * STEPS_BETWEEN_GRIDS + STEPS_WITH_SQUARES;
var last_combination_no = this.triGrid.n * (this.triGrid.n + 1) / 2 - 1;
if (this.combinationStep == MAX_STEPS) {
if (this.agg.combinationNo == last_combination_no) {
this.stopAnimating();
return;
}
// Scroll rows up by 1:
this.showRowsToCombination(undefined, true);
// Move to next combination:
this.agg.combinationNo++;
this.combinationStep = 0;
var demoSlider = document.getElementById("ssq4-combinationsSlider");
var lastVisibleCombNo = this.agg.combinationNo;
if (lastVisibleCombNo < 4) {
lastVisibleCombNo = 4;
}
var newValue = COMBINATION_COUNT - lastVisibleCombNo;
demoSlider.value = newValue;
return;
}
if (this.combinationStep == 0) {
// Start next combination:
this.triGrid.grids.forEach(function(g) {
g.areValidTopLeftSquaresVisible = false;
g.setSquareStartRow(-1);
});
if (this.agg.combinationNo > 0) {
var i = this.triGrid.i;
var j = this.triGrid.j + 1;
if (j > n) {
i++;
j = i + 1;
}
this.triGrid.setCombination(i, j);
}
this.showRowsToCombination();
}
if (this.combinationStep == last_square_step_no) {
this.triGrid.grids[2].setSquareStartRow(-1);
}
else if ((this.combinationStep >= STEPS_AT_START)
&& (this.combinationStep < last_square_step_no))
{
// Find the grid to advance the pink square on:
var newY = this.combinationStep - STEPS_AT_START;
var gridNo = 0;
var grid = this.triGrid.grids[gridNo];
while (newY >= grid.squaresInSlice() + STEPS_BETWEEN_GRIDS) {
newY -= grid.squaresInSlice() + STEPS_BETWEEN_GRIDS;
gridNo++;
grid = this.triGrid.grids[gridNo];
}
newY = newY - STEPS_BETWEEN_GRIDS;
if (newY == 0) {
grid.areValidTopLeftSquaresVisible = true;
}
if (newY == - STEPS_BETWEEN_GRIDS) {
if (gridNo > 0) {
// Hide the pink square from the previous grid:
this.triGrid.grids[gridNo - 1].setSquareStartRow(-1);
}
}
if (newY >= 0) {
grid.setSquareStartRow(newY);
this.agg.incrementSquare(gridNo);
this.showCountsForGridAndCurrentCombo(gridNo);
}
};
this.combinationStep++;
}
Demo.prototype.scrollTable = function(sliderValue) {
if (!this.isAnimating) {
var lastComboNo = 36 - parseInt(sliderValue);
this.showRowsToCombination(lastComboNo);
}
}
// ------------------------------------------
// Interactive animation functions and setup:
// ------------------
// Interaction class:
function Interaction() {
var tg = new TriGrid(n, "ssq4-interactive-grid-");
this.triGrid = tg;
var self = this;
tg.grids.forEach(function(g) {
g.areValidTopLeftSquaresVisible = true;
g.onSliceChanged = self.updateTableOnSliceChanged;
});
tg.onCombinationChanged = this.updateSelectorsOnCombinationChanged;
}
Interaction.prototype.updateUIForAnimation = function (isAnimationEnding) {
document.getElementById("ssq4-choose-i-and-j").disabled = !isAnimationEnding;
document.getElementById("ssq4-animate-i-and-j").disabled = !isAnimationEnding;
document.getElementById("ssq4-stop-animating-i-and-j").disabled = isAnimationEnding;
document.getElementById("ssq4-i-select").disabled = !isAnimationEnding;
document.getElementById("ssq4-j-select").disabled = !isAnimationEnding;
}
Interaction.prototype.animateIAndJ = function() {
this.triGrid.setCombination(0, 1);
this.updateUIForAnimation(false);
this.timer = window.setInterval(this.showNextIAndJ.bind(this), 1000);
}
Interaction.prototype.showNextIAndJ = function() {
var i = this.triGrid.i;
var j = this.triGrid.j + 1;
if (j > n) {
i++;
j = i + 1;
if (i == n) {
this.stopAnimatingIAndJ();
return;
}
}
this.triGrid.setCombination(i, j);
}
Interaction.prototype.stopAnimatingIAndJ = function() {
window.clearInterval(this.timer);
this.updateUIForAnimation(true);
}
Interaction.prototype.updateTableOnSliceChanged = function() {
// this will be the grid
$("td#ssq4-table" + this.gridNo + "-p").text(this.p);
$("td#ssq4-table" + this.gridNo + "-q").text(this.q);
var w = this.sliceWidth();
$("td#ssq4-table" + this.gridNo + "-w").text(w);
$("td#ssq4-table" + this.gridNo + "-s").text(this.squaresInSlice());
$("td#ssq4-totals-table" + this.gridNo + "-wxw").text(w + " x " + w);
$("td#ssq4-totals-table" + this.gridNo + "-s").text(this.squaresInSlice());
}
Interaction.prototype.updateSelectorsOnCombinationChanged = function () {
var iSelect = document.getElementById("ssq4-i-select");
iSelect.selectedIndex = this.i;
var jSelect = document.getElementById("ssq4-j-select");
jSelect.selectedIndex = this.j - 1;
for (var k = 1; k < n; k++) {
jSelect.options[k-1].disabled = (k <= this.i);
}
}
// Functions for selecting i and j manually in the interactive grids:
Interaction.prototype.iChanged = function() {
var iSelect = document.getElementById("ssq4-i-select");
var i = iSelect.selectedIndex;
var j = this.triGrid.j;
if (j <= i) {
j = i + 1;
}
this.triGrid.setCombination(i, j);
}
Interaction.prototype.jChanged = function() {
var jSelect = document.getElementById("ssq4-j-select");
this.triGrid.setCombination(this.triGrid.i, jSelect.selectedIndex + 1);
}
// Setup for selecting i and j randomly for the interactive grids:
Interaction.prototype.chooseIAndJRandomly = function() {
// debug: alert("Calling chooseIAndJRandomly");
var i = getRandomInt(0, n); // i is the first element of the set {0, 1, ..., n}
var j = getRandomInt(0, n-1);
/* j is one of the remaining members of the set.
* But we'll fake choosing it from {0, 1, ..., i - 1, i + 1, ... n}.
* So choose j from {0, 1, ..., i-1, i, ..., n - 1}.
* But fix it by adding 1 to j if it's in the range {i, i+1, ..., n - 1}.
*/
if (j >= i) {
// Fix j, by shifting it on by one.
j++;
} else {
// Swap i and j
var swp = i;
i = j;
j = swp;
}
// NB: both branches lead to the required post-condition: 0 <= i < j <= n
// debug: alert("(i, j) = )" + ssq_params.i + ", " + ssq_params.j);
this.triGrid.setCombination(i, j);
}
// ==========
// Setup:
// ----------
// Constants:
const INIT_P = 3;
const INIT_Q = 7;
// -----------
// Demo setup:
function bindDemoToUI() {
document.getElementById("ssq4-demo-button-start").addEventListener("click", demo.startAnimation.bind(demo));
document.getElementById("ssq4-demo-button-pause").addEventListener("click", demo.pauseAnimation.bind(demo));
document.getElementById("ssq4-demo-button-step").addEventListener("click", demo.stepThroughAnimation.bind(demo));
document.getElementById("ssq4-demo-button-resume").addEventListener("click", demo.resumeAnimation.bind(demo));
document.getElementById("ssq4-demo__slow-radio-button").addEventListener("click",
(function() { demo.setAnimationSpeed(SLOW_DEMO_SPEED); }).bind(demo));
document.getElementById("ssq4-demo__medium-radio-button").addEventListener("click",
(function() { demo.setAnimationSpeed(MEDIUM_DEMO_SPEED); }).bind(demo));
document.getElementById("ssq4-demo__fast-radio-button").addEventListener("click",
(function() { demo.setAnimationSpeed(FAST_DEMO_SPEED); }).bind(demo));
document.getElementById("ssq4-demo__very-fast-radio-button").addEventListener("click",
(function() { demo.setAnimationSpeed(VERY_FAST_DEMO_SPEED); }).bind(demo));
var slider = document.getElementById("ssq4-combinationsSlider");
slider.addEventListener("change", function() { demo.scrollTable(this.value); });
slider.addEventListener("input", function() { demo.scrollTable(this.value); });
$(".ssq4-demo-table__cell").click( function() { demo.userSelectedARow(this); });
}
var demo = new Demo();
demo.triGrid.setCombination(INIT_P, INIT_Q);
demo.updateUIForAnimation();
bindDemoToUI();
var demoSlider = document.getElementById("ssq4-combinationsSlider");
demoSlider.disabled = true;
demoSlider.readonly = true;
// -----------------------------------
// Interactive grids and tables setup:
function bindInteractionToUI() {
document.getElementById("ssq4-i-select").addEventListener("change",
interaction.iChanged.bind(interaction));
document.getElementById("ssq4-j-select").addEventListener("change",
interaction.jChanged.bind(interaction));
document.getElementById("ssq4-choose-i-and-j").addEventListener("click",
interaction.chooseIAndJRandomly.bind(interaction));
document.getElementById("ssq4-animate-i-and-j").addEventListener("click",
interaction.animateIAndJ.bind(interaction));
document.getElementById("ssq4-stop-animating-i-and-j").addEventListener("click",
interaction.stopAnimatingIAndJ.bind(interaction));
}
var interaction = new Interaction();
interaction.triGrid.setCombination(INIT_P, INIT_Q);
bindInteractionToUI();
})();
</script>
Andrew Tweddlehttp://www.blogger.com/profile/02458479910895055369noreply@blogger.com0tag:blogger.com,1999:blog-10106192.post-60900842922707626152015-12-03T21:07:00.001-05:002015-12-03T21:11:11.626-05:00A combinatorial solution to the sum of squares formula<!-- From http://irrep.blogspot.com/2011/07/mathjax-in-blogger-ii.html: -->
<script src="//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML" type="text/javascript">
MathJax.Hub.Config({
HTML: ["input/TeX","output/HTML-CSS"],
TeX: { extensions: ["AMSmath.js","AMSsymbols.js"],
equationNumbers: { autoNumber: "AMS" } },
extensions: ["tex2jax.js"],
jax: ["input/TeX","output/HTML-CSS"],
tex2jax: { inlineMath: [ ['$','$'], ["\\(","\\)"] ],
displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
processEscapes: true },
"HTML-CSS": { availableFonts: ["TeX"],
linebreaks: { automatic: true } }
});
</script>
<h3>Overview</h3>
<p>
In my previous two blog posts, I showed that the number of squares of various sizes in an nxn grid is $\sum_{{i}={1}}^{n}i^2 = \frac{n(n + 1)(2n + 1)}{6}$. In this post I'm going to use combinatorial Mathematics to derive a different closed form solution.
</p>
<h3>All posts in this series</h3>
<ul>
<li><a href="http://andrewtweddle.blogspot.com/2015/06/counting-squares-in-grid-using-sum-of.html">Counting squares in a grid</a></li>
<li><a href="http://andrewtweddle.blogspot.com/2015/12/guessing-closed-form-solution-for-sum.html">Guessing a closed form solution for the sum of squares</a></li>
<li><a href="http://andrewtweddle.blogspot.ca/2015/12/a-combinatorial-solution-to-sum-of.html">A combinatorial solution for counting squares in a grid</a></li>
</ul>
<br />
<h3>A quick review of combinations</h3>
<p>
In combinatorics, the combination function $\binom{n}{k}$ is used to counts the number of different ways that k items can be chosen from a set of n unique items, with the order of the k items being irrelevant. $\binom{n}{k}$ is usually read as "n combination k" or "n choose k". It is also known as the binomial coefficient. You can read more about combinations <a href="https://en.wikipedia.org/wiki/Combination">on Wikipedia</a>.
</p>
<p>
You also need to know that:
$$
\begin{align*}
n! &= 1 . 2 . 3 ... n & \text{n! is the product of the first n natural numbers} \\
\binom{n}{k} &= \frac{n!}{(n-k)! k!} \\
&= \frac{n (n - 1) ... (n - k + 1)}{1 . 2 . 3 ... k}
\end{align*}
$$
</p>
<h3>The approach</h3>
<p>
We are going to use combinatorial Mathematics to count the number of possible squares in the grid. To do this we will need to define a coordinate system for the grid and a convenient data representation for a valid square in that grid. By a data representation, I mean a set of numeric values that can uniquely identify a particular square in the grid.
</p>
<p>
We will then use combinations to count all possible ways of generating valid data representations. But we will need to take into account that combinations generate unique numbers. That will require us to consider separate cases based on the number of identical variables in our representation.
</p>
<h3>A useful data representation</h3>
<hr />
<h4>A recap of the problem</h4>
<p>
The problem is to count the total number of squares of any size in a grid. So if you think of a Chessboard, how many 1x1, 2x2, ..., 8x8 squares are there?
</p>
<p>
Let's consider whether there is a useful notation for describing any valid square in the grid.
</p>
<hr />
<h4>Defining a coordinate system</h4>
<p>
First let's define a coordinate system for the grid.
</p>
<p>
We'll say that (x, y) is the coordinate of a space on the checkerboard in column x and row y. And we'll choose (1,1) to be the coordinate of the top-left corner. That way columns go from left to right and rows go from top to bottom - the same direction as reading pages in a book (for most cultures).
</p>
<p>
<i>[This is just one possible convention... I could have started at the bottom left instead, like a chart, or made the corner square (0, 0) instead of (1,1).]</i>
</p>
<hr />
<h4>Comparing different representations for valid squares in the grid</h4>
<p>
We could use the co-ordinates $(x_1, y_1)$ and $(x_2, y_2)$ of two opposite corners of a square block. But that representation encodes any rectangle in the grid, not just the squares. We'd prefer to choose a data representation which enforces constraints naturally.
</p>
<p>
So what if we used $(x_1, y_1, s)$ as our data representation, where $(x_1, y_1)$ is the top left corner of the square and $s$ is the size of the sides of the square? This is better, because we have 3 variables representing a valid square instead of 4, and we have expressed the constraint that a square's sides have the same size.
</p>
<p>
However this representation is still a little cumbersome. To express the constraint that the square fits within the grid, we need to ensure that the bottom right corner is somewhere inside the grid. That requires calculating the x and y coordinates of the bottom-right corner. Our data representation would be more convenient if we could avoid all unnecessary calculations.
</p>
<p>
So let's encode the bottom-right corner into the data representation instead. This will give us our desired data representation...
</p>
<hr />
<h4>The chosen data representation</h4>
<p>
We will represent a square in the grid by the triple $(x, y, s)$ where (x, y) is the bottom-right corner of the square and s is the size.
</p>
<p>
Our data representation (x, y, s) is summarized in the following diagram:
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7yw6ou6Qh350YWGaBHmHHAxjSy2f650uDwUQxLa6z8fcmkn32AGnTIXR7PA8H3S7knQ8ZDIOkyugdiINEk-2-fLOLbe87_4VmaqXHj5q8P9gWo-1HdfITiaIGGOzPTl_mjr4/s1600/SquareParameters.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7yw6ou6Qh350YWGaBHmHHAxjSy2f650uDwUQxLa6z8fcmkn32AGnTIXR7PA8H3S7knQ8ZDIOkyugdiINEk-2-fLOLbe87_4VmaqXHj5q8P9gWo-1HdfITiaIGGOzPTl_mjr4/s1600/SquareParameters.PNG" /></a></div>
</p>
<p>
With this data representation, our constraints on a valid square are:
$$
\begin{align*}
1 \leq x \leq n & \tag{1} \\
1 \leq y \leq n & \tag{2} \\
1 \leq s \leq n & \tag{3} \\
s \leq x & \tag{4} \\
s \leq y & \tag{5} \\
\end{align*}
$$
</p>
<p>
There is some redundancy in constraint 3, since the other 4 constraints ensure that $s \leq n$.
I've chosen to do it this way so that constraints 1, 2 and 3 all have the same form.
They will be satisfied naturally when we use the combination function.
Only constraints 4 and 5 will need to be considered explicitly.
</p>
<br />
<h3>Generating all possible data representations</h3>
<hr />
<h4>Choosing values of x, y and s from the set {1, 2, ... n}</h4>
<p>
We can address constraints 1, 2 and 3 by choosing our variables x, y and s from the set {1, 2, 3, ..., n}. That allows us to use the combination function.
</p>
<p>
Unfortunately there's a catch! Combinations count unique numbers, and some of the variables x, y and s could be the same. So we'll need to consider separate cases depending on how many of the variables have unique values.
</p>
<p>
Let's start by defining variables for each case. Let $c_i$ be the number of ways of generating representations $(x, y, s)$ when $i$ unique numbers are taken from the set {1, 2, 3, ..., n}. The total number of squares is then $c_1 + c_2 + c_3$.
</p>
<hr />
<h4>Counting $c_3$: the number of squares when x, y and s are all different</h4>
<p>
There are $\binom{n}{3}$ ways of choosing three unique numbers from the set {1, 2, ..., n}. Of these, the smallest must always be assigned to $s$ so that constraints 4 and 5 are addressed. However the largest could be assigned to x or y. This leads to two sub-cases:
$$
\begin{align*}
\text{ i) } & s < x < y \\
\text{ii) } & s < y < x
\end{align*}
$$
</p>
<p>
So we have two different representations generated from each set of three distinct numbers. Thus:
$$
c_3 = 2 \binom{n}{3}
$$
</p>
<hr />
<h4>Counting $c_2$: the number of squares when only two of x, y and s are different</h4>
<p>
There are $\binom{n}{2}$ ways of choosing two unique numbers from the set {1, 2, ..., n}.
But there are three different ways that these two numbers can be assigned to the values (x, y, s):
$$
\begin{align*}
\text{ i) } & s < x = y \\
\text{ ii) } & s = x < y \\
\text{ iii) } & s = y < x \\
\end{align*}
$$
</p>
<p>
Thus:
$$
c_2 = 3 \binom{n}{2}
$$
</p>
<hr />
<h4>Counting $c_1$: the number of squares when x, y and s are all the same</h4>
<p>
There are $\binom{n}{1} = n$ ways of choosing a single number from the set {1, 2, ..., n}.
</p>
<p>
Each such number will define a single solution with:
$$
\begin{align*}
s = x = y \\
\end{align*}
$$
Clearly all the constraints will be satisfied. Thus:
$$
c_1 = n
$$
</p>
<hr />
<h4>Putting it all together</h4>
<p>
So the total number of squares of various sizes in an n x n grid is:
$$
c_1 + c_2 + c_3 = n + 3 \binom{n}{2} + 2 \binom{n}{3}
$$
</p>
<br />
<h3>Checking the answer</h3>
<p>
In the second post of the series we showed that the number of squares is $ \frac{n(n + 1)(2n + 1)}{6} $.
Let's check our answer by seeing if the two formulas are equivalent.
</p>
<p>
$$
\begin{align*}
n + 3 \binom{n}{2} + 2 \binom{n}{3} &= n + 3 \frac{n (n - 1)}{2} + 2 \frac{ n(n - 1)(n - 2)}{6} \\
&= \frac{n}{6} [ 6 + 9 (n - 1) + 2(n - 1)(n - 2) ] & \text{ Extract } \frac{n}{6} \text { from all terms } \\
&= \frac{n}{6} [ 6 + 9n - 9 + 2n^2 - 6n + 4 ] \\
&= \frac{n}{6} [ 2n^2 + 3n + 1 ] \\
&= \frac{n}{6} [ (n + 1)(2n + 1) ] \\
&= \frac{n(n + 1)(2n + 1)}{6} \\
\end{align*}
$$
So our calculations seem correct, since this is the formula we generated in the previous blog post.
</p>
<h3>The value of a good data representation</h3>
<p>
Rob Pike's <a href="http://users.ece.utexas.edu/~adnan/pike.html">5th rule of programming</a> supposedly states that:
<blockquote cite="http://users.ece.utexas.edu/~adnan/pike.html">
Data dominates. If you've chosen the right data structures and organized things well, the algorithms will almost always be self-evident. Data structures, not algorithms, are central to programming.
</blockquote>
</p>
<p>
This echoes Fred Brooks' statement in "The Mythical Man-Month" that: "Representation is the essence of programming".
</p>
<p>
This blog post is about Mathematics, not programming. But I'd argue that the elegance of this solution also comes from choosing the right data representation.
</p>
<h3>Conclusion</h3>
<p>
This is the third post in the series. The first three posts have shown that the number of squares in an nxn grid is:
$$
\begin{align*}
& \sum_{{i}={1}}^{n}i^2 & & \text{ from blog post 1} \\
= & \frac{n(n + 1)(2n + 1)}{6} & & \text{ from blog post 2} \\
= & n + 3 \binom{n}{2} + 2 \binom{n}{3} & & \text{ from blog post 3} \\
\end{align*}
$$
</p>
<br />
<h3>Next time</h3>
<p>
This post had far less algebra than the previous blog post. But there was still some algebraic manipulation required to arrive at the formula $\frac{n(n + 1)(2n + 1)}{6}$. It's certainly not obvious why the formula has that specific form. My aim in the next two blog posts will be to address this. I want to arrive at that formula via a much more direct route.
</p>
Andrew Tweddlehttp://www.blogger.com/profile/02458479910895055369noreply@blogger.com0tag:blogger.com,1999:blog-10106192.post-78310311591711216562015-12-02T23:03:00.000-05:002016-01-16T09:27:39.024-05:00Guessing a closed form solution for the sum of squares<!-- From http://irrep.blogspot.com/2011/07/mathjax-in-blogger-ii.html: -->
<script src="//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML" type="text/javascript">
MathJax.Hub.Config({
HTML: ["input/TeX","output/HTML-CSS"],
TeX: { extensions: ["AMSmath.js","AMSsymbols.js"],
equationNumbers: { autoNumber: "AMS" } },
extensions: ["tex2jax.js"],
jax: ["input/TeX","output/HTML-CSS"],
tex2jax: { inlineMath: [ ['$','$'], ["\\(","\\)"] ],
displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
processEscapes: true },
"HTML-CSS": { availableFonts: ["TeX"],
linebreaks: { automatic: true } }
});
</script>
<h3>Overview</h3>
<p>
In <a href="http://andrewtweddle.blogspot.ca/2015/06/counting-squares-in-grid-using-sum-of.html">a previous blog post</a>, I showed that the number of squares of various sizes in an nxn grid is $\sum_{{i}={1}}^{n}i^2 $. As n gets large, it's going to take a long time to sum up all those squares. So a closed form solution would be much better.
</p>
<p>
In this blog post I'm going to demonstrate a very mechanical way of finding that formula. It doesn't require any flashes of genius, and it generalizes to many similar problems. If you were the manager of a team of Mathematicians selling commercial Mathematics projects, this is probably how you'd want them to solve the problem. It's boring, predictable, general and doesn't require (nor provide) any special insight. And you can even solve the algebra programmatically, which will reduce the risk of a calculation error. Kaching!
</p>
<p>
What? You don't work for a company that sells Mathematics for money? How surprising. Well, stay tuned to this blog series. Because in future blog posts I'm going to present a variety of more inspired solutions. These will give much better insight into why the formula comes out the way it does. But first let's derive the formula...
</p>
<h3>All posts in this series</h3>
<ul>
<li><a href="http://andrewtweddle.blogspot.com/2015/06/counting-squares-in-grid-using-sum-of.html">Counting squares in a grid</a></li>
<li><a href="http://andrewtweddle.blogspot.com/2015/12/guessing-closed-form-solution-for-sum.html">Guessing a closed form solution for the sum of squares</a></li>
<li><a href="http://andrewtweddle.blogspot.ca/2015/12/a-combinatorial-solution-to-sum-of.html">A combinatorial solution for counting squares in a grid</a></li>
</ul>
<br />
<h3>The mechanical approach</h3>
<hr />
<h4>Step 1: guess the pattern of the formula</h4>
<p>
The degree of a polynomial is the highest power that the variable is raised to. If the summation is over a polynomial of degree $d$, then make an educated "guess" that the closed form expression is a polynomial of degree $d+1$. This seems plausible, because it holds for the following well known formulae:
$$
\begin{align*}
\sum_{{i} = {1}}^{n}1 &= n \\
\sum_{{i} = {1}}^{n}i &= \frac{n(n+1)}{2} &= \frac{1}{2} n^2 + \frac{1}{2} n \\
\end{align*}
$$
</p>
<p>
Firstly, let $f(n) = \sum_{{i}={1}}^{n}i^2 $ be the actual sum of squares function.
</p>
<p>
Note that $f(0) = 0$ since there are no terms to add when $n = 0$ (there are zero squares in a zero by zero grid).
</p>
<p>
Now let $g(n)$ be our guess for the closed form equivalent of function f:
$$
\begin{align*}
g(n) &= \sum_{{j}=0}^{d+1}a_j.n^j \\
&= a_{0} + a_{1} n + a_{2} n^{2} + a_{3} n^{3} & \text{since d = 2} & \tag{1}
\end{align*}
$$
</p>
<p>
Next we need to calculate values for the $a_j$ values. We will start with $a_0$.
</p>
<hr />
<h4>Step 2: Ensure that $g(0) = f(0) = 0$ </h4>
<p>
$ g(0) = a_3 . 0^3 + a_2 . 0^2 + a_1 . 0 + a_0 = a_0 $
</p>
<p>
Hence set: $a_0 = 0 \tag{2}$
</p>
<hr />
<h4>Step 3: Choose a method for determining the other coefficients</h4>
<p>
At this point it's tempting to set $g(1) = f(1)$, $g(2) = f(2)$ and $g(3) = f(3)$ to calculate the coefficients $a_j$.
This is quick and simple and it will generate the correct values of the $a_j$ if $f(n)$ is indeed a polynomial.
</p>
<p>
But it won't provide any justification that $f(n)$ <i>really</i> is a polynomial of degree $d + 1$ i.e. that $f(n) = g(n)$ for all non-negative integers $n$.
To be more rigorous, we are going to calculate the coefficients a different way...
</p>
<p>
Let $h(i)$ be the expression in the summation i.e. $h(i) = i^2 \tag{3} $
</p>
<p>
<i>[Note that you can generalize this method using other polynomial functions for $h$.]</i>
</p>
<p>
We are going to choose values for the $a_j$ coefficients so that:
$$
\begin{align*}
g(i) - g(i-1) &= h(i) & \forall i \in \mathbb{N} & \tag{4} \\
\end{align*}
$$
</p>
<p>
Providing we can find such values, this will enable us to prove that
$$
\begin{align*}
g(n) &= f(n) & \forall n \in \mathbb{N}
\end{align*}
$$
This is because:
</p>
<p>
$$
\begin{align*}
f(n) &= \sum_{{i}={1}}^{n}h(i) \\
&= \sum_{{i}={1}}^{n}[g(i) - g(i-1)] \\
&= [g(n) - g(n-1)] + [g(n-1) - g(n-2)] + ... + [g(2) - g(1)] + [g(1) - g(0)] \\
&= g(n) - g(0) & \text{because all other terms cancel out} \\
&= g(n) & \text{since } g(0) = 0 \text{ by equation 2}
\end{align*}
$$
</p>
<p>
There's some tedious and error-prone algebraic manipulation involved in calculating $g(i) - g(i-1)$.
Instead of doing it by hand, let's use symbolic mathematics software to do it for us...
</p>
<hr />
<h4>Step 4: Determine the other coefficients programmatically</h4>
<p>
First let's make some technology choices:
<ul>
<li>Use the Python programming language, since it is popular in the Scientific community.</li>
<li>I already have Python 2.7.10 installed via the <a href="https://python-xy.github.io/">PythonXY</a> scientific Python distribution for Windows.</li>
<li>Use the <a href="http://www.sympy.org/">SymPy</a> Python library as our symbolic Mathematics package.</li>
<li>Use the <a href="http://ipython.org/notebook.html">iPython notebook</a> as our REPL, as it allows easy sharing of the code.</li>
<li>Store the notebook in GitHub, which has <a href="http://blog.jupyter.org/2015/05/07/rendering-notebooks-on-github/">built-in support</a> for hosting and displaying iPython notebooks.</li>
<li>Extract the code into a separate Python script, and copy this script into a <a href="https://gist.github.com/AndrewTweddle/eaa8e3e3e09a8bf58c47">GitHub gist</a> for easy sharing in the blog.</li>
</ul>
</p>
<p>
The first step was to develop the SymPy code in an iPython web notebook. You can download or view <a href="https://github.com/AndrewTweddle/BlogArtefacts/blob/master/AndrewTweddle.blogspot.com/SumOfSquares/Guess/ClosedFormGuess.ipynb">the notebook on GitHub</a>.
</p>
<p>
If you download it, you can then open it from the command line by navigating to a suitable folder and running <pre>ipython notebook --ip=localhost</pre>
</p>
<p>
This will open a local web page which you can use to navigate to the notebook and edit it:
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7jKyyn-dgCd4gO4K-NDnMubU0WoBER9ecE8JHJu-o129rtXPIHGKA-IyS62KRzNHKFsMqRYWX95dXnGRL1tTezOHoOQR4OCCp1SSa8dMMph8pXC-gpdIFUJHGrsaA9pC81FA/s1600/iPythonNotebook.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7jKyyn-dgCd4gO4K-NDnMubU0WoBER9ecE8JHJu-o129rtXPIHGKA-IyS62KRzNHKFsMqRYWX95dXnGRL1tTezOHoOQR4OCCp1SSa8dMMph8pXC-gpdIFUJHGrsaA9pC81FA/s400/iPythonNotebook.PNG" /></a></div>
</p>
<p>
After calculating the coefficients and factorizing the polynomial, the notebook spits out the following formula:
$$ \frac{n(n + 1)(2n + 1)}{6} $$
</p>
<hr />
<h4>Step 5 (optional): Generalize the code to work for other polynomial summations</h4>
<p>
After getting the code working using the iPython Notebook, I ported it to a Python script. I then modified the script so that, instead of only supporting the case $h(i) = i^2$, it would accept any polynomial expression in the variable $i$ as a command line parameter.
</p>
<p>
To demonstrate that this method works more generally, I've included a short Powershell snippet to calculate the closed form of the sums of k<sup>th</sup> powers for k from 0 to 10:
<script src="https://gist.github.com/AndrewTweddle/eaa8e3e3e09a8bf58c47.js"></script>
</p>
<p>
Running this Powershell snippet, produces the following output:
<div class="separator" style="clear: both; text-align: left;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZxrvJ_ks0UTBC2bHUys3_3jDB3wljmRDWQj7Ury6VVoYfAu1d2ONt7p44QCjdrTaRLWjPHSp_Jw1KpNoS5RIHzjk_NJNhW8H8EntkDid_N5liS5XdZG7ZbXmtn_OquYgDq3I/s1600/GuessClosedFormFromPowerShell.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="611" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZxrvJ_ks0UTBC2bHUys3_3jDB3wljmRDWQj7Ury6VVoYfAu1d2ONt7p44QCjdrTaRLWjPHSp_Jw1KpNoS5RIHzjk_NJNhW8H8EntkDid_N5liS5XdZG7ZbXmtn_OquYgDq3I/s640/GuessClosedFormFromPowerShell.PNG" width="800" /></a></div>
</p>
<p>
I'm fairly happy with how the Python script turned out in the end. But I found it quite frustrating getting to that point. While SymPy is reasonably well documented at the level of modules and functions, it's harder to work out what the allowed values are for many of the function parameters and what they mean. Although the code is working, there are still quite a few hacks and TODO's left in the code.
</p>
<p>
As well as using the <a href="http://docs.sympy.org/dev/index.html">official SymPy documentation</a>, I also found this <a href="http://mattpap.github.io/scipy-2011-tutorial/html/basics.html">SciPy 2011 Tutorial</a> quite useful.
</p>
<p>
It would have been quicker to do the algebra by hand, although admittedly not for the more general case. Arguably that's also due to my lack of regular practice with SymPy, iPython and Python.
</p>
<hr />
<h3>Conclusion</h3>
<p>
In this blog post I've used SymPy to derive the following result:
$$ \sum_{{i}={1}}^{n}i^2 = \frac{n(n + 1)(2n + 1)}{6} $$
</p>
<p>
In my next few blog posts on this problem, I'm going to demonstrate other derivations of the closed form solution. In particular, I'm looking for derivations which provide greater insight into the problem.
</p>Andrew Tweddlehttp://www.blogger.com/profile/02458479910895055369noreply@blogger.com0tag:blogger.com,1999:blog-10106192.post-45521107505632211422015-06-10T14:20:00.001-04:002015-12-03T21:11:58.072-05:00Counting squares in a grid using the sum of squares method<!-- From http://irrep.blogspot.com/2011/07/mathjax-in-blogger-ii.html: -->
<script type="text/javascript" src="//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
MathJax.Hub.Config({
HTML: ["input/TeX","output/HTML-CSS"],
TeX: { extensions: ["AMSmath.js","AMSsymbols.js"],
equationNumbers: { autoNumber: "AMS" } },
extensions: ["tex2jax.js"],
jax: ["input/TeX","output/HTML-CSS"],
tex2jax: { inlineMath: [ ['$','$'], ["\\(","\\)"] ],
displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
processEscapes: true },
"HTML-CSS": { availableFonts: ["TeX"],
linebreaks: { automatic: true } }
});
</script>
<h3>Introduction</h3>
<p>
A while back a colleague asked some of us how we would go about solving a particular Mathematical problem. The problem was to count the total number of squares in a Chess board. This included all squares of size 1x1, 2x2, up to 8x8.
</p>
<p>
I found a number of different ways of solving the problem which I'm going to present in the next few blog posts.
</p>
<p>
As has been the theme with my previous blog posts on Mathematical puzzles, my goal is to progress from solutions which are algebraic to solutions which provide insight.
</p>
<p>
This blog post is about the first way of solving the problem, which I've dubbed the sum of squares method.
</p>
<p>
<i>Note that I'll be solving the problem for any n x n grid (or lattice), not just for the 8 x 8 chessboard.</i>
</p>
<h3>All posts in this series</h3>
<ul>
<li><a href="http://andrewtweddle.blogspot.com/2015/06/counting-squares-in-grid-using-sum-of.html">Counting squares in a grid</a></li>
<li><a href="http://andrewtweddle.blogspot.com/2015/12/guessing-closed-form-solution-for-sum.html">Guessing a closed form solution for the sum of squares</a></li>
<li><a href="http://andrewtweddle.blogspot.ca/2015/12/a-combinatorial-solution-to-sum-of.html">A combinatorial solution for counting squares in a grid</a></li>
</ul>
<br />
<hr />
<h3>Step 1: How many k x k squares fit into an n x n grid?</h3>
<p>
Supposing we have an n x n grid. In how many different ways can we fit a square of size k x k into the grid (and of course its edges must align with edges inside or along the edge of the grid)?
</p>
<p>
Consider the k x k pink square shown in the diagram below...
</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgG5cLY4BOAjNM54OLbgTTF81MofZOfQJdrC6QnBwChQKaWPmprR4kb9GoQh-DfUU5AvCRQsuP4Btvjl9EHYU-sQo2cctrmZQpxzI8FBW93P1iRV8Gi_WyhxKM5Fu1f0Klhp2k/s1600/SumOfSquares.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgG5cLY4BOAjNM54OLbgTTF81MofZOfQJdrC6QnBwChQKaWPmprR4kb9GoQh-DfUU5AvCRQsuP4Btvjl9EHYU-sQo2cctrmZQpxzI8FBW93P1iRV8Gi_WyhxKM5Fu1f0Klhp2k/s1600/SumOfSquares.PNG" /></a></div>
<p>
I've shown the top-left cell of the pink square in a darker pink. We can't move the dark pink square any further right or down. But we could move it up or to the left. As we move the square around, the dark pink cell can end up anywhere within the yellow area.
</p>
<p>
So the number of possible positions for the pink k x k square is precisely the number of positions for the dark pink cell, which is the size of the yellow area.
</p>
<p>
Now the yellow area has sides of length: $n - (k - 1) = n - k + 1$.
So there are $(n - k + 1)^2$ possible k x k squares that can fit into the n x n grid.
</p>
<hr />
<h3>Step 2: Add up the number of squares for each value of k</h3>
<p>
The smallest value of k is 1 and there will be $n^2$ one-by-one squares fitting in the grid.
The largest value of k is n and there will be only one n x n square.
</p>
<p>
If we add up the number of k x k squares for each value of k we get a formula of:
</p>
$$ \sum_{{k}={1}}^{n}(n - k + 1)^2 $$
<br />
That's quite a neat formula. But we can make it even neater by adding a new variable $i = n - k + 1$ and summing over all possible values of $i$ instead. The following table will make this clearer:
<br />
<br />
<div>
<style>
table,th,td
{
border:1px solid black;
border-collapse:collapse;
text-align:center;
}
th
{
background-color:WhiteSmoke;
}
td
{
background-color:White;
}
</style>
<table>
<tr>
<th width="50">$k$</th>
<th width="150">$i = n - k + 1$</th>
<th width="250">Number of squares of size k x k <br/>$ = (n - k + 1)^2 = i^2$</th>
</tr>
<tr>
<td>$1$</td>
<td>$n$</td>
<td>$n^2$</td>
</tr>
<tr>
<td>$2$</td>
<td>$n - 1$</td>
<td>$(n-1)^2$</td>
</tr>
<tr>
<td>...</td>
<td>...</td>
<td>...</td>
</tr>
<tr>
<td>$n - 1$</td>
<td>$2$</td>
<td>$2^2$</td>
</tr>
<tr>
<td>$n$</td>
<td>$1$</td>
<td>$1^2$</td>
</tr>
</table>
</div>
<br />
<br />
Now instead of summing over the values of k (from top to bottom), we can instead sum over the values of i (i.e. starting from the bottom row to the top row). And our formula becomes:
$$ \sum_{{i}={1}}^{n}i^2 $$
<hr />
<h3>Step 3: Derive a closed form solution</h3>
<p>
The formula above is very succinct and beautiful. But it has a drawback... as $n$ gets large, it's going to a take a long time to do all those summations. To address this, what we really want to find is a <a href="http://en.wikipedia.org/wiki/Closed-form_expression">closed form solution</a> without any summation symbols.
</p>
<p>
In future posts I'm going to show you a few different ways of getting a closed formula.
</p>
<hr />
<h3>Next steps...</h3>
<p>
In the next blog post in this series, I'm going to derive the closed form solution through algebraic manipulation of the summation formula.
</p>
<p>
In later posts I will derive the number of squares in the grid in other ways. These will lead directly to the closed form solution. I'm hoping they will also give more insight into why the formula comes out the way it does.
</p>
Andrew Tweddlehttp://www.blogger.com/profile/02458479910895055369noreply@blogger.com0tag:blogger.com,1999:blog-10106192.post-1754567230495232392014-03-02T12:44:00.001-05:002015-09-12T16:32:05.620-04:00Andrew Loves Math(s)<h3>Introduction </h3>
The other day I was discussing Mathematical puzzles with my colleagues and one of them mentioned the "Johnny Hates Math" problem. The problem statement can be found on <a href="http://www.spoj.com/problems/ANARC07J/">the SPOJ web site</a>.
<br />
<br />
Essentially the problem is to insert plus signs at appropriate places in a string of digits such that the resulting sum equals a target value. The plus signs will divide the digits into a set of numbers. None of these numbers should be more than 5 digits long and none should start with a zero, except possibly the number zero itself.
<br />
<br />
The instructions on the SPOJ web site are to print out the solution with the least number of plus signs. But I think it's more interesting to know the total number of solutions. So I'm just going to provide code to solve that problem, not satisfy the SPOJ specification.
<br />
<br />
I'm going to solve the problem in a number of different ways and using different programming language features (mainly using Scala). The purpose is to get a feel for which solution methods produce the most concise code and which produce the fastest code. It will also be an opportunity to experiment with Scala and develop more of a feel for the language.
<br />
<br />
<h3>A PowerShell solution</h3>
My first attempt was in PowerShell and took 46 minutes to code. A lot of that time was spent fixing PowerShell syntax errors.
<br />
<br />
Interestingly, I automatically turned to recursion to solve the problem. I guess that's the effect of experimenting with functional programming in the last little while!
<br />
<br />
My approach was to:
<ul>
<li> Branch on the number of digits in the next number (line 6 in the code snippet below) </li>
<li> Prune each branch based on having enough digits left (line 8), not starting with a zero (line 11), not exceeding the target value (line 16) and not running out of digits before reaching the target value (line 25)</li>
<li> Recursively call the function with the remaining digits and remaining target value (line 28)</li>
<li> End the recursion when there are no digits remaining and the target value has been reached exactly (line 21)</li>
</ul>
<br />
<script src="https://gist.github.com/AndrewTweddle/9122060.js"></script>
A quick comment on line 28: @(...) wraps its contents in an array unless the contents are already an array. This solves a problem in PowerShell where, if a function or script emits multiple values, these values are automatically wrapped in an array - but if there is only a single value, then it is returned as is.
<br />
<br />
This inconsistency can lead to some very tricky bugs. So it's important to coerce the return value to an array.
<br />
<br />
My laptop has an i5-2430M CPU. The PowerShell script solved the problem in under 5 seconds for the problem of inserting plus signs into the left hand side of: 15442147612367219875=472
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6CbtlzqWyAY-1rH6y2YrOTby2-qgB2P1dTT9qudSWfs1mb6DCdfLp6YFf09tY7KvX19JaTFAFhlYKqsWxbYZ19zHIn_p08rj6yeTe5dynReoIHmvp6cAjmttRL-r_4Fv5gek/s1600/Test_JHM_Powershell.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6CbtlzqWyAY-1rH6y2YrOTby2-qgB2P1dTT9qudSWfs1mb6DCdfLp6YFf09tY7KvX19JaTFAFhlYKqsWxbYZ19zHIn_p08rj6yeTe5dynReoIHmvp6cAjmttRL-r_4Fv5gek/s1600/Test_JHM_Powershell.PNG" /></a></div>
<br />
The break statements provided a very convenient way of excluding infeasible branches. This didn't feel as clean in my first Scala attempt, since Scala treats if statements as expressions (similar to the ?: ternary conditional operator in languages like C# and Java).
<br />
<br />
<hr />
<h3>A few Scala solutions</h3>
<br />
<h4>A Scala base class for the solver algorithm</h4>
I refactored the algorithm-independent features into a base class. The code for this is shown below:
<br />
<br />
<script src="https://gist.github.com/AndrewTweddle/9168772.js"></script>
<br />
I made the somewhat strange decision of defining the abstract solve method to take the same parameters as the class constructor. This was purely to give the derived classes the option of recursively calling the solve method. But it's not a great API design choice. It feels like it is trying to be both a static method (although Scala doesn't have such a concept as it would violate pure OO) and an abstract method.
<br />
<br />
I'm uncomfortable with this API, which usually means something's wrong. But I'm going to leave it for now, because my focus is on solving the problem, not designing an API.
<br />
<br />
In line 19, the companion object uses the "SolverUsingFlatMap" derived class as its default solver. I'll provide that sub-class next...
<br />
<br />
<h4>Scala solution 1: SolverUsingFlatMap</h4>
The code for my first Scala solution is shown below:
<br />
<br />
<script src="https://gist.github.com/AndrewTweddle/9176739.js"></script>
<br />
This solver works in a very similar fashion to the PowerShell algorithm. However, instead of returning arrays of strings, it represents a single solution as a List[Int] (i.e. a Scala linked list of integers). A list of solutions is then represented as a List[List[Int]].
<br />
<br />
I returned an empty list of solutions to indicate that a branch was infeasible. But the break statements in PowerShell felt a bit more intuitive to me.
<br />
<br />
<h4>Scala solution 2: SolverUsingFor</h4>
I wanted to get away from returning empty lists as a way of indicating a failed branch. My first thought was to use an Option[List[List[Int]]] type to represent a pruned branch using an option of None. But this felt like overkill.
<br />
<br />
Instead my second attempt used a for statement to get away from the need to return empty lists. This code turned out to be very concise and pleasing:
<br />
<br />
<script src="https://gist.github.com/AndrewTweddle/9177389.js"></script>
<br />
<h4>Scala solution 3: SolverUsingFoldLeft</h4>
I then started wondering whether it was possible to use a foldLeft operation to calculate a solution.
<br />
<br />
There were a few conceptual issues with making this work.
Firstly, a foldLeft is taking a fixed list of items and an initial accumulator, and updating the accumulator for each successive item. In this case, each item was an individual digit.
<br />
<br />
Secondly, all the previous solutions branched in up to 5 directions at each step. To get branching to work with foldLeft, I needed to store all the branches in the accumulator. To do this, I created a PartialSolution class to keep track of the numbers generated so far, as well as the current number which was being built up. At each step, I would then split each PartialSolution into at most 2 new PartialSolutions, by either terminating the current number or adding another digit to it.
<br />
<br />
The code is shown below:
<br />
<br />
<script src="https://gist.github.com/AndrewTweddle/9177615.js"></script>
<br />
This code is considerably more verbose. I also didn't expect it to perform very well. Firstly, it is effectively doing a breadth-first search. Secondly I would expect it to be more memory-intensive and memory access is often much slower than computation. This is partially mitigated by storing the numbers in reverse order (so that large parts of the previous level's data structures are reused, since prepending a number to a list does not create a new list). Also, the foldLeft is tail recursive.
<br />
<br />
I also expected the foldLeft solution to degrade very quickly for large target totals, as the number of partial solutions is potentially doubling in size at every step. So with a large target value, the pruning effect of comparing the running total to the target is lost.
<br />
<br />
It turns out that this solver performed a lot better than I expected. And although it did degrade more with larger totals, the effect was not as pronounced as I had been expecting.
<br />
<br />
<h4>Scala worksheet to compare performance of the solvers</h4>
I used a Scala worksheet to test the code and compare performance of the various algorithms. The worksheet code and results are shown below:
<br />
<br />
<script src="https://gist.github.com/AndrewTweddle/9177884.js"></script>
<p>
<i>
[UPDATE: This way of measuring performance is not ideal, as explained in <a href="http://shipilev.net/blog/2014/nanotrusting-nanotime/">this blog post by Aleksey Shipilёv</a>.
Rather use a micro-benchmarking framework, such as <a href="http://openjdk.java.net/projects/code-tools/jmh/">JMH</a> (possibly with <a href="https://github.com/ktoso/sbt-jmh">sbt-jmh</a>) or <a href="http://scalameter.github.io/">ScalaMeter</a>.]
</i>
</p>
I tested the 3 solvers twice - first using a low target value, and then again using a high target value (to get an idea of the scalability of the algorithms).
<br />
<br />
There are some interesting things to note here.
<br />
<br />
Firstly, the flatMap method was fastest in all cases.
<br />
<br />
Despite being so short, the method using for comprehensions was much slower. In fact, for the smaller target value, I was quite surprised to see that the breadth-first foldLeft solver was sometimes faster!
<br />
<br />
With a much larger value, the foldLeft solver showed its poor scalability. However it wasn't that much worse than the method using the for comprehension.
<br />
<br />
All the Scala solvers were much faster than the PowerShell algorithm. This isn't too surprising, since PowerShell is an interpreted scripting language.
<br />
<br />
<hr />
<h3>Impressions of Scala</h3>
Last year I completed the two Scala courses given on <a href="http://www.coursera.com">Coursera</a>. This was my first foray into Scala since completing those courses. So what are my impressions of the language?
<br />
<br />
<h4>Programming in the small</h4>
Firstly, I've really enjoyed solving this problem with Scala. As with F#, I love being able to have concise, readable code - not unlike a dynamically typed language - and yet still gain the benefits of type safety and good performance.
<br />
<br />
I can see how functional programming in general provides a great solution for "<a href="http://en.wikipedia.org/wiki/Programming_in_the_large_and_programming_in_the_small">programming in the small</a>".
<br />
<br />
<h4>Scripting</h4>
Scripting is another aspect of programming in the small. So at some point I'd like to experiment with using the Scala REPL for writing scripts.
<br />
<br />
There is definitely promise in this area. I've had a lot of experience using PowerShell for scripting at work and at home. But I've had colleagues demonstrate that they can generally match the features of PowerShell using the F# REPL. It's obvious that functional programming languages can mount a credible challenge in this space - at least from a language perspective. The major advantage PowerShell continues to have in a work setting is its ubiquity on Windows servers.
<br />
<br />
I also recently finished working on <a href="https://github.com/AndrewTweddle/CodingChallenge">a "record linkage" challenge</a> in my spare time using Python and Pandas for the data munging. If and when time permits, I would like to re-develop the project in Scala to see which code base is easier to work with.
<br />
<br />
<h4>Programming in the large</h4>
Something I really like about Scala is that it is clearly designed for "programming in the large" as well. It's not just about functional programming. The baby has not been thrown out with the bathwater: object orientation is treated as an equal citizen in the language.
<br />
<br />
Actually I'd argue that Scala's true potential in the enterprise has very little to do with being a functional programming language. There are many Scala features which allow very concise, readable Object-Oriented code to be written. This succinctness is part of the <i>style</i> of functional programming. However the overtly functional features in Scala are often too easily abused and could be a distraction from the very real value that Scala offers.
<br />
<br />
I've seen a number of recent indicators that Scala may be ready to break into the enterprise development space:
<ul>
<li>
The January 2014 issue of the <a href="http://www.thoughtworks.com/radar/#/languages-and-frameworks">Thoughtworks technology radar</a> places "Scala - The good parts" in the adopt category at the centre of the radar.
</li>
<li><a href="http://en.wikipedia.org/wiki/Rod_Johnson_(programmer)">Rod Johnson</a> is the creator of the Spring framework. In <a href="https://www.youtube.com/watch?v=DBu6zmrZ_50">his keynote address at ScalaDays 2013</a>, he stated his opinion that by 2018 Scala would be the leading "new" language and that it would find its niche as the leading enterprise language.
</li>
<li><a href="https://vaughnvernon.co/">Vaughn Vernon</a>, author of "Implementing Domain-Driven Design", has recently been blogging about using Scala and Akka for Domain-Driven Design. Tomorrow I will be attending a 3 day workshop given by Vaughn. So I'm looking forward to hearing his opinions on Scala.
</li>
<li>The frequently down (and out?) HammerPrinciple.com web site provides survey-based comparisons of various programming languages. Scala ranked first (above C# and Java) for <a href="http://web.archive.org/web/20120510205707/http://hammerprinciple.com/therighttool/statements/this-language-is-best-for-very-large-projects">"this language is best for very large projects"</a>, second for <a href="http://web.archive.org/web/20120510211121/http://hammerprinciple.com/therighttool/statements/i-would-use-this-language-for-writing-server-progr">"I would use this language for writing server programs"</a> and third for <a href="http://web.archive.org/web/20120510210702/http://hammerprinciple.com/therighttool/statements/this-language-is-good-for-distributed-computing">"this language is good for distributed computing"</a>.
</li>
</ul>
<br />
<h4>On the other hand...</h4>
Scala has momentum. But that doesn't mean it will achieve breakthrough. Numerous great languages have fallen by the wayside, and there's no guarantee that Scala won't join their ranks. It's a big leap from a language being ready for the enterprise, to the enterprise being ready for a language!
<br />
<br />
On the negative side, Scala has a lot of very advanced language features which can easily trip up newcomers. This complexity is a substantial barrier to enterprise adoption.
<br />
<br />
This explains why Thoughtworks qualifies its recommendation of Scala as being just "the good parts".
<br />
<br /> Rod Johnson also addresses this issue in the ScalaDays keynote mentioned earlier, emphasizing the need to favour readability over poetry.
<br />
<br />
Martin Odersky, the designer of the Scala language, is well aware of the challenge. In 2011 he provided <a href="http://www.scala-lang.org/old/node/8610">a list of language features</a> with recommendations for which features were suitable for different levels of application developers and which for library designers.
<br />
<br />
<h4>Library design</h4>
This raises another area where Scala appears to be very elegant... library design.
<br />
<br />
The hammerprinciple.com site has Scala at number 3 for <a href="http://web.archive.org/web/20120510210755/http://hammerprinciple.com/therighttool/statements/i-rarely-have-difficulty-abstracting-patterns-i-fi">"I rarely have difficulty abstracting patterns I find in my code"</a> and <a href="http://web.archive.org/web/20120510110830/http://hammerprinciple.com/therighttool/statements/this-language-is-expressive">"this language is expressive"</a>. It comes in at number 2 for <a href="http://web.archive.org/web/20120510110142/http://hammerprinciple.com/therighttool/statements/i-find-code-written-in-this-language-very-elegant">"I find code written in this language is very elegant"</a>. These suggest that Scala could be a great language for designing libraries for Scala and the JVM.
<br />
<br />
Of course you have to take this with a pinch of salt, since early adopters are likely to bias the HammerPrinciple survey outcomes. However there seems to be enough promise here to justify further experimentation.
<br />
<br />
On that note, there were some things in my JHM code that I really didn't like. I want to refactor the solve() method. And even though it's only in a worksheet, I'd also like to get rid of the implicit parameter to the time() function. It's one of those features which can easily make a code base more opaque. And even though the way I've used it is fairly transparent, just the act of using it legitimizes its use by less experienced programmers (who either haven't developed the intuition to know when not to use the feature, or don't yet have the ability to hold their peers to account by being able to explain the reasoning behind that intuition).
<br />
<br />
In a later post I'd like to refactor the code for reusability, and in so doing understand more of Scala's library design features.
<br />
<br />
<hr />
<h3>Conclusion</h3>
This has been an interesting algorithmic challenge and I have really enjoyed solving it with Scala.
<br />
<br />
I've seen enough promise with Scala to justify using it as my preferred hobby language (along with Python for experimenting with Data Analytics and data munging).
<br />
<br />
It's been a lot of fun programming with Scala. And that's pretty important, because there's no guarantee that it will ever become a marketable skill.Andrew Tweddlehttp://www.blogger.com/profile/02458479910895055369noreply@blogger.com0tag:blogger.com,1999:blog-10106192.post-90729596787672465752014-02-27T16:43:00.001-05:002014-03-09T03:37:49.073-04:00RIP AmberToday we took our beautiful bull mastiff to be put down. Heart-breaking, but necessary... she was over 10 years old and had been diagnosed with kidney failure. She was skipping meals and her weight had dropped to just 34 kilograms (from 51 kg a few years before).
<br />
<br />
She had a wonderful sniff around the vet's waiting room, and it was lovely to see some of her old joy and bounciness back. But it also brought back memories of her younger self. It made it all the sadder to say good-bye and stroke her head as she fell asleep for the last time...
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1BJoDqFiW3MAjRBLWAPC_dnN8nNXfPNhBA7PuAJmD_0Dffi4o1f6taH676FgueDlV4zuiMdu6McHfZtfsnuPMCZs5Q7bWNjL5HFkF2q04W81lIPFGrUU_Jw3m6B8f38g3KdQ/s1600/IMG_1585.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1BJoDqFiW3MAjRBLWAPC_dnN8nNXfPNhBA7PuAJmD_0Dffi4o1f6taH676FgueDlV4zuiMdu6McHfZtfsnuPMCZs5Q7bWNjL5HFkF2q04W81lIPFGrUU_Jw3m6B8f38g3KdQ/s320/IMG_1585.JPG" /></a></div>
<br />
RIP, Amber!
Andrew Tweddlehttp://www.blogger.com/profile/02458479910895055369noreply@blogger.com0tag:blogger.com,1999:blog-10106192.post-61971804373021405082014-02-23T17:54:00.001-05:002014-02-23T17:54:34.002-05:00Birthday Board Games Bash<h3>Thank you!</h3>
Firstly, a big thank you to my wonderful wife for arranging a board games party on Saturday for my birthday.
<br />
<br />
Around 40 friends attended, with the numbers being fairly evenly split between adults and children. We started at 10:00 am, and things went very smoothly, despite a power outage to replace some stolen electrical cable in the area.
<br />
<br />
There were 3 separate groups of friends present. I was a little apprehensive as it's always difficult to mix different social groups together. But it went really well.
<br />
<br />
<hr />
<h3>Ricochet Robots</h3>
We started out playing <a href="http://www.boardgamegeek.com/boardgame/51/ricochet-robots">Ricochet Robots</a> as it can handle an arbitrary number of people and it's easy to join or leave at any time. It's a very clever competitive puzzle and it can be a real brain-burner. It's not everyone's cup of tea, but I love it!
<br />
<br />
There are online versions at <a href="http://www.ricochetrobot.com/">http://www.ricochetrobot.com</a> and <a href="http://www.ricochetrobots.com">http://www.ricochetrobots.com</a>.
<br />
<br />
<hr />
<h3>The Resistance: Avalon</h3>
Two of my friends had brought along copies of <a href="http://www.boardgamegeek.com/boardgame/128882/the-resistance-avalon">Avalon (the Resistance)</a> so we then split into two groups - one for novices and the other for the experts. I played in the expert group, but felt a bit out of my depth at first.
<br />
<br />
There are a group of us who play games once a week after work. The rest of the guys have been playing Avalon a lot over the past few months and even have their own terminology now. But over that time I was temporarily seconded to another project to produce an architecture document for a client. So I have a lot of catching up to do.
<br />
<br />
<hr />
<h3>A present!</h3>
Although everyone had been instructed not to bring presents, my colleagues clubbed together to buy me a present anyway. They had colluded with my wife, who had sneakily found out which games were on my most desired list. So I received a copy of <a href="http://www.boardgamegeek.com/boardgame/125618/libertalia">Libertalia</a>.
<br />
<br />
This game has been a big hit in our weekly after-work games sessions. It's a lot of fun to play. So I was delighted!
<br />
<br />
<hr />
<h3>Ultimate Werewolf</h3>
After one of the Avalon owners left the party, we ended up with around 15 people still wanting to play. Although the Resistance improves on Werewolf in just about every way, it only caters for up to 10 players. Werewolf can handle much more than that.
<br />
<br />
So we switched over to a few games of <a href="http://www.boardgamegeek.com/boardgame/38159/ultimate-werewolf-ultimate-edition">Ultimate Werewolf</a> instead. Great fun as always!
<br />
<br />
<hr />
<h3>The birthday buffoon!</h3>
As numbers dwindled, we switched back to Avalon again. We ended with an absolutely excellent game, where the minions of Mordred won all 3 quests and also knew who Merlin was.
<br />
<br />
I ended up being a perfect patsy for two of the minions of Mordred, who inveigled their way into the trusted group. It was so impressive to see how cleverly they pulled the wool over our eyes, that I felt honoured to have been part of the game - even if I was one of the ones who had been so comprehensively fooled by them.
<br />
<br />
Usually our monthly board games events last until late into a Saturday night, but this time everyone was gone before supper.
<br />
<br />
<hr />
<h3>And thanks again!</h3>
All in all, it was a really fun way to spend a birthday. Thanks to everyone who attended, and made this an ideal birthday party for me!
Andrew Tweddlehttp://www.blogger.com/profile/02458479910895055369noreply@blogger.com0tag:blogger.com,1999:blog-10106192.post-80562868632810941542014-02-08T10:05:00.000-05:002015-01-02T15:15:02.397-05:00Researching the Josephus problem<!-- From http://irrep.blogspot.com/2011/07/mathjax-in-blogger-ii.html: -->
<script src="http://cdn.mathjax.org/mathjax/latest/MathJax.js" type="text/javascript">
MathJax.Hub.Config({
HTML: ["input/TeX","output/HTML-CSS"],
TeX: { extensions: ["AMSmath.js","AMSsymbols.js"],
equationNumbers: { autoNumber: "AMS" } },
extensions: ["tex2jax.js"],
jax: ["input/TeX","output/HTML-CSS"],
tex2jax: { inlineMath: [ ['$','$'], ["\\(","\\)"] ],
displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
processEscapes: true },
"HTML-CSS": { availableFonts: ["TeX"],
linebreaks: { automatic: true } }
});
</script>
<!-- Alex Gorbatchev syntax highlighter hosted on AWS (donation given) -->
<link href="http://alexgorbatchev.com/pub/sh/current/styles/shCore.css" rel="stylesheet" type="text/css"></link>
<link href="http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css" rel="stylesheet" type="text/css"></link>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js" type="text/javascript"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shAutoloader.js" type="text/javascript"></script>
<!-- F# syntax highlighting from http://tinesware.blogspot.com/2008/12/syntax-highlighting.html -->
<!-- Wrapping in a CDATA is a trick obtained from http://tinesware.blogspot.com/2008/12/syntax-highlighting.html -->
<script type="text/javascript">//<![CDATA[
/**
* SyntaxHighlighter
* http://alexgorbatchev.com/
*
* SyntaxHighlighter is donationware. If you are using it, please donate.
* http://alexgorbatchev.com/wiki/SyntaxHighlighter:Donate
*
* @version
* 2.0.320 (May 03 2009)
*
* @copyright
* Copyright (C) 2004-2009 Alex Gorbatchev.
*
* @license
* This file is part of SyntaxHighlighter.
*
* SyntaxHighlighter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SyntaxHighlighter is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with SyntaxHighlighter. If not, see <http://www.gnu.org/copyleft/lesser.html>.
*/
/* Syntax contributed by Julien O., 2009-10-05 */
SyntaxHighlighter.brushes.FSharp = function() {
/* main F# keywords */
var keywords1 =
/* section 3.4 */
'abstract and as assert base begin class default delegate do done ' +
'downcast downto elif else end exception extern false finally for ' +
'fun function if in inherit inline interface internal lazy let ' +
'match member module mutable namespace new null of open or ' +
'override private public rec return sig static struct then to ' +
'true try type upcast use val void when while with yield ' +
'asr land lor lsl lsr lxor mod ' +
/* identifiers are reserved for future use by F# */
'atomic break checked component const constraint constructor ' +
'continue eager fixed fori functor global include method mixin ' +
'object parallel params process protected pure sealed tailcall ' +
'trait virtual volatile ' ;
/* do, return, let, yield keywords with ! at the end are added later */
/* define names of main libraries in F# Core so we can link to it
http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/manual/namespaces.html
*/
var modules =
'Array Array2D Array3D Array4D ComparisonIdentity HashIdentity List ' +
'Map Seq SequenceExpressionHelpers Set CommonExtensions Event ' +
'ExtraTopLevelOperators LanguagePrimitives NumericLiterals Operators ' +
'OptimizedClosures Option String NativePtr Printf' ;
/* 17.2 & 17.3 */
var functions =
'abs acos asin atan atan2 ceil cos cosh exp ' +
'floor log log10 pown round sign sin sinh sqrt tan tanh ' +
'fst snd KeyValue not min max ' +
'ignore stdin stdout stderr ' ;
/* 17.2 Object Transformation Operators */
var objectTransformations =
'box hash sizeof typeof typedefof unbox'
/* 17.2 Exceptions */
var exceptions =
'failwith invalidArg raise rethrow' ;
/* 3.11 Pre-processor Declarations / Identifier Replacements*/
var constants =
'__SOURCE_DIRECTORY__ __SOURCE_FILE__ __LINE__';
/* Pervasives Types & Overloaded Conversion Functions */
var datatypes =
'bool byref byte char decimal double exn float float32 ' +
'FuncConvert ilsigptr int int16 int32 int64 int8 ' +
'nativeint nativeptr obj option ref sbyte single string uint16 ' +
'uint32 uint64 uint8 unativeint unit enum async seq dict ' ;
function fixComments(match, regexInfo) {
var css = (match[0].indexOf("///") == 0) ? 'color1' : 'comments';
return [new SyntaxHighlighter.Match(match[0], match.index, css)];
}
this.regexList = [
/* 3.3 Conditional compilation & 13.3 Compiler Directives + light /light off*/
{ regex: /\s*#\b(light|if|else|endif|indent|nowarn|r(eference)?|I|include|load|time|help|q(uit)?)/gm, css: 'preprocessor' },
{ regex: SyntaxHighlighter.regexLib.singleLineCComments, css:'comments' },
{ regex: SyntaxHighlighter.regexLib.multiLineCComments, css: 'comments' },
{ regex: /\s*\(\*[\s\S]*?\*\)/gm, css: 'comments' },
{ regex: SyntaxHighlighter.regexLib.doubleQuotedString, css: 'string' },
{ regex: /'[^']?'/gm, css: 'string' },
{ regex: new RegExp(this.getKeywords(keywords1), 'gm'), css: 'keyword' },
{ regex: /\s*(do|let|yield|return)*\!/gm, css: 'keyword' },
{ regex: new RegExp(this.getKeywords(modules), 'gm'), css: 'keyword' },
{ regex: new RegExp(this.getKeywords(functions), 'gm'), css: 'functions' },
{ regex: new RegExp(this.getKeywords(objectTransformations), 'gm'), css: 'keyword' },
{ regex: new RegExp(this.getKeywords(exceptions), 'gm'), css: 'keyword' },
{ regex: new RegExp(this.getKeywords(constants), 'gm'), css: 'constants' },
{ regex: new RegExp(this.getKeywords(datatypes), 'gm'), css: 'keyword' }
];
this.forHtmlScript(SyntaxHighlighter.regexLib.aspScriptTags);
};
SyntaxHighlighter.brushes.FSharp.prototype = new SyntaxHighlighter.Highlighter();
SyntaxHighlighter.brushes.FSharp.aliases = ['f#', 'f-sharp', 'fsharp'];
SyntaxHighlighter.config.bloggerMode = true;
SyntaxHighlighter.defaults['toolbar'] = false;
SyntaxHighlighter.all();
//]]>
</script>
<hr />
<h3>All posts in this series:</h3>
<ul>
<li>
<a href="http://andrewtweddle.blogspot.com/2012/12/one-potato-two-potato.html">One Potato</a>: A description of the problem
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2012/12/two-potato-basic-equations.html">Two potato</a>: The basic equations
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/01/three-potato-algebraic-solution.html">Three potato</a>: An algebraic formula for the last person left
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/03/four-binary-calculation-method.html">Four</a>: A neat calculation method using binary numbers
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/03/five-potato-verifying-formulae-with-f.html">Five potato</a>: F# functions to check the various formulae
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/05/six-potato-building-calculation-graph.html">Six potato</a>: Using a calculation graph to see the pattern
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/06/seven-potato-generalizing-to-other.html">Seven potato</a>: Generalizing to other intervals
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/06/more-efficient-circle-elimination.html">More</a>: An efficient algorithm for arbitrary intervals
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2014/02/researching-josephus-problem.html">Online research</a>: In which I discover that it is called the Josephus problem
</li>
</ul>
<hr />
<h3>Introduction</h3>
This is the ninth post in a series about an interesting Mathematical problem known as the Josephus problem.
<br />
<br />
Imagine a number of people sitting in a circle. Every second person is asked to leave the circle. This continues until only one person is left. If you label the people 1 to n, what is the label of this last person?
<br />
<br />
In the first 6 posts in the series:
<ul>
<li>I derived a formula for the label of the last person left,</li>
<li>I provided various proofs of the formula, and </li>
<li>I wrote code in the F# programming language to calculate the answer in various ways.</li>
</ul>
<br />
In the seventh and eighth posts I created an efficient algorithm for calculating the answer when the number of people skipped is not two but some arbitrary interval k. But I wasn't able to find a closed formula. And I felt I had gone as far as I could on my own. It was time to see how other people had solved the problem.
<br />
<br />
This post is about what I discovered.
<br />
<br />
<hr />
<h3>The problem has a name</h3>
The first thing I discovered is that the problem has a name. It is known as the Josephus problem and it is widely used in Mathematical and Programming challenges. So I will be updating the previous posts to reflect the accepted name.
<br />
<br />
You can read more about the history of the Josephus problem on <a href="http://en.wikipedia.org/wiki/Josephus_problem">Wikipedia</a> or <a href="http://mathworld.wolfram.com/JosephusProblem.html">Wolfram Mathworld</a>.
<br />
<br />
<hr />
<h3>In search of an elegant solution</h3>
<br />
<h4>My own attempt</h4>
When there are n people in the circle labelled 1 to n, the formula for the last person left in the circle is:
$$
f(n) = 2n + 1 - 2^{{\lfloor log_2 n \rfloor} + 1} \tag{1}
$$
<br />
In the third post in the series I used algebra to prove this result. But although algebra is a very powerful tool for proving the formula, it doesn't give any insight into why the formula is true.
<br />
<br />
One of the challenges I set myself was to provide that insight. The closest I came was in <a href="http://andrewtweddle.blogspot.com/2013/05/six-potato-building-calculation-graph.html">the sixth post in the series</a>, where I used a calculation graph to show the pattern of values for $f(n)$.
<br />
<br />
Chamberlain's solution is much more elegant...
<br />
<br />
<h4>Chamberlain's solution</h4>
Chamberlain's Solution can be found on page 403 of <a href="http://math.fau.edu/Yiu/PSRM2012/00PSRM2012Chapters1to15.pdf">Problem Solving and Recreational Mathematics 2012</a> by Paul Yiu of Florida Atlantic University.
<br />
<br />
His solution is based on a few key insights. I've paraphrased these as follows:
<ol>
<li>When the number of people in the circle is a power of two, the last person left is the first person skipped. This is because:
<br />
<ol type="a">
<li>All the even numbered people are skipped each time around the circle</li>
<li>Exactly half the people are eliminated, so the remaining size of the circle is again a power of two</li>
<li>Person one will still be the first person skipped on the next traversal of the circle</li>
<li>So the process repeats itself with the circle halving each time, until person one is the only person left</li>
</ol>
</li>
<br />
<li>Suppose the size of the circle is $2^m + r$ (with $0 \le r < 2^m$). Carry out the first $r$ removals. Then:
<br />
<ol type="a">
<li>The first $r$ people removed are persons $2, 4, 6, ..., 2r$</li>
<li>The next person to be skipped will be person $2r + 1$</li>
<li>This leaves a new, smaller circle with $2^m$ people</li>
<li>So we can apply the rule from point 1 above (for a circle of size $2^m$)</li>
<li>So person $2r + 1$ will be the last person left in the circle</li>
</ol>
</li>
</ol>
<br />
<h4>Making it rigorous</h4>
There are a few points above which may not be obvious. Let's try to make them more rigorous:
<br />
<br />
In point 2.a., the assumption is made that the labels of the first $r$ people removed would not "clock over" (i.e. one revolution of the circle hasn't been completed). Is this assumption reasonable?
<br />
<br />
$$
\begin{align*}
\text {By definition: } 0 & \le r < 2^m \tag{2} \\
\text {and: } n & = 2^m + r \tag{3} \\
\\
\therefore 2r & < 2^m + r &\text{ by adding r to both sides of the inequality on the right of (2)}\\
\text{i.e. } 2r & < n \tag{4} \\
\\
\text{Also: } 2r + 1 & \le n \tag{5}
\end{align*}
$$
<br />
Equation 4 shows that this is a reasonable assumption.
<br />
<br />
In point 2.b. we also assumed that the next person to be skipped will be person $2r + 1$. Equation 5 proves that this label doesn't "clock over" either.
<br />
<br />
So this proves that $f(2^m + r) = 2r + 1 \text{ where } 0 \le r < 2^m$.
<br />
<br />
And this can be re-expressed in terms of logarithm and floor functions by noting that:
<br/>
$$
\begin{align*}
0 & \le r < 2^m & \text{ from equation 2} \\
\therefore 2^m & \le 2^m + r < 2^m + 2^m \\
\therefore 2^m & \le n < 2^{m+1} & \text{ from equation 3} \\
\therefore m & \le \log_2{n} < m+1 & \text{ since logarithms are monotonic (order-preserving) } \\
\therefore m & = {\lfloor log_2 n \rfloor} \tag{6} \\
\\
\text{So:} \\
f(n) & = 2r + 1 \\
& = 2( n - 2^m ) + 1 & \text{ from equation 3}\\
& = 2n + 1 - 2^{m+1} \\
& = 2n + 1 - 2^{{\lfloor log_2 n \rfloor} + 1} & \text{ from equation 6}
\end{align*}
$$
<br />
And this is the formula given in equation 1 earlier.
<br />
<br />
<h4>A beautiful visualization of Chamberlain's solution</h4>
You can find a beautiful visual explanation of this solution on the <a href="http://www.exploringbinary.com/powers-of-two-in-the-josephus-problem/">Exploring Binary</a> web site.
<br />
<br />
<h4>Another very succinct solution</h4>
I was also very impressed by <a href="http://home.comcast.net/~mrtwhs/vita/MM1031.pdf">this solution</a>. But although it was very succinct, I didn't feel that it provided the same "aha" moment of Chamberlain's solution.
<br />
<br />
<hr />
<h3>Generalizing the Josephus problem to arbitrary intervals</h3>
I found two algorithms for generalizing the Josephus problem to arbitrary intervals.
<br />
<br />
<h4>Jakobczyk's algorithm</h4>
One of these was in a paper entitled <a href="http://journals.cambridge.org/article_S0017089500001919">On the Generalized Josephus Problem</a> by F Jakobczyk. Although interesting in its own right, I'm rather going to concentrate on the other algorithm I found, as it was far more elegant...
<br />
<br />
<h4>The Ahrens-Schubert algorithm</h4>
I found <a href="http://ms.appliedprobability.org/data/files/Abstracts%2031/31-2-3.pdf">the first page of an article by I. M. Davids</a> published by the Applied Probability Trust. This page gives a very elegant algorithm for the Josephus problem generalized to arbitrary intervals. But since only the first page (with the abstract) is provided, I don't have a proof for the algorithm.
<br />
<br />
The Ahrens-Schubert solution uses a number series known as an Ahrens array. These are also referred to in the paper as "rounded up" arrays. They are similar to geometric sequences, except that the answer is rounded up after every step. So each term is the previous term multiplied by a factor $f$ and then rounded up to the nearest integer.
<br />
<br />
Suppose $a_0$ is the initial integer term. Then:
$$
\begin{align*}
a_1 & = \lceil{a_0 . f}\rceil & \\
a_2 & = \lceil{a_1 . f}\rceil & = \lceil{\lceil{a_0 . f} \rceil . f} \rceil \\
& ... \\
a_m & = \lceil{a_{m-1} . f}\rceil &= \overbrace{\lceil{ \text{ ... } \lceil{\lceil{a_0 . f} \rceil . f} \rceil} \text{ ... } .f \rceil}^\text{m times} \\
\end{align*}
$$
<br />
<br />
The Ahrens-Schubert solution works by finding the largest number (say $a_{m}$) in this series which is less than $kn+1$, where $k$ is the interval to skip and $n$ is the number of people in the circle. The answer is then $ kn + 1 - a_m$.
<br />
<br />
But what are the values for $a_0$ and $f$ in the rounded up series?
<br />
<br />
Well, one neat thing about the Ahrens-Schubert solution, is that by choosing different values for $a_0$ it can determine not just the last person left in the circle, but any arbitrary $e^\text{th}$ person removed from the circle. Here are the parameters:
$$
\begin{align*}
f & = \frac{k}{k-1} \\
a_0 & = k(n - e) + 1 \\
\therefore a_0 & = 1 & \text{when determining the last person left in the circle (set } e = n \text{)}\\
\end{align*}
$$
<br />
<h4>The Ahrens-Schubert solution for $k = 2$</h4>
<br />
Something else I like about the Ahrens-Schubert solution is how cleanly the closed formula for $k = 2$ emerges from the algorithm. When $k = 2$:
$$
\begin{align*}
f & = \frac{2}{2 - 1} = 2 \\
a_0 & = k(n - e) + 1 = 2(n-e) + 1 \\
\text{ or: } a_0 & = 1 & \text{for the last person left in the circle}\\
\therefore a_m & = \overbrace{\lceil{ \text{ ... } \lceil{\lceil{a_0 . f} \rceil . f} \rceil} \text{ ... } .f \rceil}^\text{m times} \\
& = \overbrace{\lceil{ \text{ ... } \lceil{\lceil{1 . 2} \rceil . 2} \rceil} \text{ ... } .2 \rceil}^\text{m times} \\
& = 2^m &\text{since the ceiling falls away as all terms are integers}
\end{align*}
$$
So the answer is $2n + 1 - 2^m$ where $2^m$ is the largest value of $a_i = 2^i$ less than $2n + 1$. And this reduces to equation 1!
<br />
<br />
<h4>My implementation of the Ahrens-Schubert algorithm</h4>
Below is my implementation of the algorithm in F#:
<pre class="brush: fsharp">
let rec getLargestTermInRoundedUpGeometricSeriesBelow
(startValue:int) (factor:double) boundary =
if startValue >= boundary then
raise (
System.ArgumentException(
"The starting element in the series is not below the bound!"))
else
let nextValue = int (System.Math.Ceiling( (double startValue) * factor ))
if nextValue >= boundary then
startValue
else
getLargestTermInRoundedUpGeometricSeriesBelow nextValue factor boundary
let calcEliminationNumberInCircleWithIntervalByRoundedUpSeries
eliminationIndex interval sizeOfCircle =
let dInterval = double interval
let factor = dInterval / (dInterval - 1.0)
let bound = interval * sizeOfCircle + 1
let initialValue = interval * (sizeOfCircle - eliminationIndex) + 1
bound - getLargestTermInRoundedUpGeometricSeriesBelow initialValue factor bound
let calcLastLeftInCircleWithIntervalByRoundedUpSeries interval sizeOfCircle =
calcEliminationNumberInCircleWithIntervalByRoundedUpSeries sizeOfCircle interval sizeOfCircle
</pre>
<br />
<br />
<h4>Performance of the Ahrens-Schubert algorithm</h4>
<br />
Below are timings for the algorithm:
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5X6kmqCIrqrJo2UVXyQPcAxk7a-BOgIIBVbKHM4Llwrg15NeK0glwwOWXx02cTeZ0vrh9hPNI5WT-kuVibm8lXAOu4-ymHG8oRZzEtuCW8xxGjv-P_PpEZXdxDT8OiMxlkRs/s1600/calcLastLeftInCircleWithIntervalByRoundedUpSeries.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5X6kmqCIrqrJo2UVXyQPcAxk7a-BOgIIBVbKHM4Llwrg15NeK0glwwOWXx02cTeZ0vrh9hPNI5WT-kuVibm8lXAOu4-ymHG8oRZzEtuCW8xxGjv-P_PpEZXdxDT8OiMxlkRs/s1600/calcLastLeftInCircleWithIntervalByRoundedUpSeries.PNG" /></a></div>
<br />
These are the same inputs as I used in my own algorithm from <a href="http://andrewtweddle.blogspot.com/2013/06/more-efficient-circle-elimination.html">post 8 of the series</a>, shown below:
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAQbdD1ESMyKPXB3PkZBvgeE4JU2570h4JdHtfVLNDnWB_9qdzaBcO5AyYCA9zIHnsj-Aqhz2hV12KohHN5WwxhJ00hcrnvaV0R9hQz3YJRetBz6MfD1alO4iz0DkkOOe6Wmc/s1600/calcLastLeftInCircleWithIntervalByRow.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAQbdD1ESMyKPXB3PkZBvgeE4JU2570h4JdHtfVLNDnWB_9qdzaBcO5AyYCA9zIHnsj-Aqhz2hV12KohHN5WwxhJ00hcrnvaV0R9hQz3YJRetBz6MfD1alO4iz0DkkOOe6Wmc/s1600/calcLastLeftInCircleWithIntervalByRow.PNG" /></a></div>
<br />
Comparing the timings, you can see that the Ahrens-Schubert algorithm is noticeably faster. It took 0.9 seconds to my algorithm's 1.2 seconds when k = 2, and 2.4 seconds versus 2.6 seconds when k = 100.
<br />
<br />
<hr />
<h3>Other references</h3>
<br />
<h4>Code to solve the Josephus problem</h4>
Implementations in a variety of languages can be found <a href="http://rosettacode.org/wiki/Josephus_problem">on the Rosetta Code web site</a>.
<br />
<br />
<h4>More research on the Josephus problem</h4>
I found an extensive list of references to the Josephus problem on <a href="http://www.puzzlemuseum.com">the puzzlemuseum.com web site</a>. Professor David Singmaster has provided <a href="http://www.puzzlemuseum.com/singma/singma-index.htm">a list of articles on recreational Mathematics</a>. Page 4 of source document 3 lists a large number of articles on the Josephus problem.
<br />
<br />
<hr />
<h3>Conclusion</h3>
In this series of articles, I have tackled and successfully solved the Josephus problem. This includes finding an efficient algorithm when the problem is generalized so that an arbitrary number of people is skipped.
<br />
<br />
I have discovered that this is a well-known problem with an extensive history and a number of explanations and algorithms, including two that were extremely elegant. This makes it an ideal problem to tackle yourself, as there are extensive online resources to compare your approach against.
<br />
<br />
Using F# to implement various algorithms has been particularly rewarding. This has piqued my interested in functional programming. As a result, I have started dabbling in Haskell. I also recently completed Martin Odersky's <a href="https://class.coursera.org/progfun-003/class/index">Functional Programming Principles in Scala</a> course on Coursera and the follow-up course on the <a href="https://www.coursera.org/course/reactive">Principles of Reactive Programming</a>. These are thoroughly enjoyable courses, which I recommend highly.
<br />
<br />
But I'm glad to be finally wrapping up this series on the Josephus problem. It's definitely time to move onto something new!
<br />
<br />
<hr />
<br />Andrew Tweddlehttp://www.blogger.com/profile/02458479910895055369noreply@blogger.com0tag:blogger.com,1999:blog-10106192.post-78191721390311558432013-09-22T15:13:00.000-04:002015-11-29T09:28:19.106-05:00The 2013 Entelect AI Challenge Play-offs<h3>
Introduction</h3>
Last Saturday evening (the 14th of September), I attended the play-offs of the <a href="http://challenge.entelect.co.za/">2013 Entelect Artificial Intelligence Programming Challenge</a>.
<br />
<br />
Last year the theme was based on the Tron light-cycles game, but on a sphere instead of a grid. This year the theme was inspired by the 1980's tank warfare game, <a href="http://en.wikipedia.org/wiki/Battle_City_(video_game)">Battle City</a>.
<br />
<br />
Here's a whirlwind summary of <a href="http://challenge.entelect.co.za/DisplayLink.aspx?group=Rules&name=Rules">the game rules</a>...
<br />
<br />
There are 8 fixed boards ranging in size from about 61x61 to 81x81. Each player has 2 tanks. A play wins by either shooting or driving over the enemy base. Bullets move at twice the speed of tanks, and tanks can shoot each other and shoot through walls. Tanks occupy a 5x5 area. Bases and bullets occupy a single cell. When a wall is shot, the two walls on either side are also destroyed (thus allowing a tank to shoot a path through the walls). If tanks try to move into a wall or another tank, the tank will turn but not move. A tank can only have one bullet in play at a time. So the tank is effectively disarmed until its bullet hits a wall, another tank, a base or the edge of the board. Both players move simultaneously and there are 3 seconds between turns. The players communicate with the game engine via SOAP web service calls.
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMUU3T0e2LMxassUxlf9TE1WV2xVr3nR1G_-hlyM1CRR2Eu4GWUubgeJw5zYJxnhJi5Bmeda0wOcwvKRNQQtrwzEOczMgybNiGQOqZ9htpYaleTtR715S2kw0AumvLS7km9rY/s1600/GameState_Maze_Warfare.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMUU3T0e2LMxassUxlf9TE1WV2xVr3nR1G_-hlyM1CRR2Eu4GWUubgeJw5zYJxnhJi5Bmeda0wOcwvKRNQQtrwzEOczMgybNiGQOqZ9htpYaleTtR715S2kw0AumvLS7km9rY/s400/GameState_Maze_Warfare.bmp" /></a></div>
<br />
<br />
Last year there were over 100 competitors, but this year the challenge was definitely a few notches up in complexity. In the end there were only 22 contestants. So I was expecting the play-offs to be much quieter than last year. But fortunately it wasn't like that at all.
<br />
<br />
As with last year's competition the camaraderie was superb. There was a lot of lively chatter. And it was great to put faces to some of the people I had been interacting with on <a href="https://groups.google.com/forum/#!forum/entelect-r100k-challenge">the unofficial google groups forum for the competition</a>.
<br />
<br />
<hr />
<h3>
My path to the play-offs</h3>
<h4>
My first bot</h4>
Although I worked hard on my bot, I had also been working crazy hours at work and I was still quite tired. But I was given a couple of days off work to compensate for the long hours I had been working. So I don't think the hours at work affected my productivity too much (I ended up writing over 15 000 lines of code in the 7 weeks of the competition). But I think I made more careless mistakes than I usually would.
<br />
<br />
Between my own bugs and quite a few bugs and race conditions in the official test harness, I lost most of the final week of the competition to bug fixing. I found myself with a number of really nice algorithms, but with no bot to make use of them!
<br />
<br />
The result is that I very nearly didn't submit a bot at all. My initial entry used a very basic heuristic of having one tank take the shortest path to attack the enemy base and the other tank attack the closest enemy tank. It had very basic bullet avoidance code which I added at the death (so to speak), and which hadn't been adequately tested.
<br />
<br />
At that stage I had given up on doing well. My main reason for entering was to attend the play-offs, meet up with some of the other contestants and enjoy the superb spirit of the event. I had very low expectations for my bot and I named it "Vrot Bot".
<br />
<br />
<hr />
<h4>
Vrot Bot</h4>
"Vrot" is the Afrikaans word for rotten. Like many Afrikaans words it has become a standard part of English slang in South Africa.
<br />
<br />
It is pronounced like the English word "fraught", but with a short vowel sound and a rolled "r". But if you're an English-speaking South African, you will mis-pronounce it slightly so that "vrot" rhymes with "bot". So it will sound like "frot bot".
<br />
<br />
<hr />
<h4>
The entry date gets extended</h4>
Due to the small number of competitors and some technical issues with the test harness, the competition ended up being extended twice.
<br />
<br />
This gave me an extra week to put a much more sophisticated bot together. However it was only in the last 24 hours of the competition that my new bot ended up beating the original vrot bot convincingly. And a lot of the new code had not gone through nearly enough testing, so it was bound to be buggy.
<br />
<br />
On that basis I decided to keep the name Vrot Bot.
<br />
<br />
<hr />
<h4>
Testing, testing, testing...</h4>
Last year I came fourth in the competition. My secret sauce was building a WPF user interface that allowed me to play against my bot, save and load games, rewind games to arbitrary points in their history, visualize a variety of parameters at each cell of the board and drill down into the search tree of moves (I used <a href="http://en.wikipedia.org/wiki/Negamax">the NegaMax algorithm</a> to choose moves). I ended up with a very robust, well-tested bot.
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-gdZNUZfKUPU/UHHHaihjuJI/AAAAAAAAABM/mRK6sphPy5I/s1600/WallPositioning.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-gdZNUZfKUPU/UHHHaihjuJI/AAAAAAAAABM/mRK6sphPy5I/s320/WallPositioning.JPG" /></a></div>
<br />
Although I had planned to do something similar this year, the competition had a much shorter duration than last year and I never quite got there. At one point I had a choice between writing my own test harness and using the official test harness provided by the organisers.
<br />
<br />
I chose to use the official test harness, because I was concerned about the risk of integrating a C# WCF web service client with the SOAP web service exposed by the Java test harness. So I wanted to test that integration as thoroughly as possible, and my own test harness wouldn't have provided that capability.
<br />
<br />
I ended up using a variety of simpler methods for testing my bot's logic. However this made the test cycle much slower, and I paid the price in having a much buggier bot. There were many parts of my code which were very poorly tested because I simply didn't have a quick and easy way of forcing the game into the scenario I wanted to test.
<br />
<br />
If there was only one thing I could change about my approach to this year's competition, it would be to write my own test harness (as well as using the official test harness for testing integrations). That would have given me the ability to load a previously played game at any point in its history, instead of having to wait minutes for the harness to reach the turn where I knew the bug to be.<br />
<br />
<hr />
<h4>
Performance tuning...</h4>
Last year my bot's main weakness was its slow performance. The first and third-placed players both used C++ for their bots, so having good performance gave a distinct competitive advantage. So this year I put a lot more effort into tweaking the performance of my bot.
<br />
<br />
There's a very true saying that premature optimization is the root of all evil. I was well aware of this. However I also felt that to get really good performance the data structures would need to be designed with performance in mind. And that's something that's not easy to change later if you get it wrong.
<br />
<br />
With hindsight I shouldn't have spent as much time worrying about performance. The search space for this year's challenge was massive. That meant that a brute force approach was far less valuable than last year. My time would have been better spent improving my testing capability.
<br />
<br />
<hr />
<h3>The play-offs</h3>
The 22 contestants were grouped into 4 round robin pools with 5 to 6 players per pool.
<br />
<br />
Many of the matches were decided on silly mistakes, so I think many of the other contestants had also been struggling with the same issues: the shorter competition timeframe, a much more complex problem space and difficulties with adequately testing their bots.
<br />
<br />
My bot performed much better than I was expecting. I topped my pool quite easily, winning 4 of my 5 pool matches.
<br />
<br />
Strangely enough, my bot seemed to be a lot more decisive than I was expecting. But I didn't think too much of it at the time.
<br />
<br />
My one loss was on a smaller, fairly open board. Bullet-dodging was something I had tested quite thoroughly. Yet my tanks ran straight into the enemy tanks' bullets instead of dodging them. That was also quite surprising.
<br />
<br />
My bot's algorithm is based on running through a number of scenarios and, for each applicable scenario, calculating a value for each of the 6 possible tank actions for each tank. At the time I suspected that a number of scenarios had combined to create a cumulative value for moving forward that was greater than the value of either dodging or shooting approaching bullets.
<br />
<br />
It was only a few days later that I discovered the real reason for the lack of bullet-dodging behaviour... but I'll get to that later.
<br />
<br />
<hr />
<h3>
The elimination round</h3>
I was expecting the top 2 players in each pool to go through to the finals at the <a href="http://www.rageexpo.co.za/">rAge computer gaming expo</a> on 5 October. However there was a twist in proceedings...
<br />
<br />
The players were seeded based on how many games they had won in the pool stage. The top 16 seeds went into an elimination round. The elimination round worked as follows: seed 1 played seed 16, seed 2 played seed 15, and so on. The winners of those 8 matches would go through to the finals at rAge.
<br />
<br />
Although I was one of the top 5 seeds, I had a sinking feeling at this point as a single loss could knock one out the tournament. Imagine my feeling when I saw the board for my knock-out game... It was the same board as my one loss in the pool rounds! And sadly the result was the same... my tanks raced towards the enemy tanks, ate a bullet each and I was knocked out the competition!
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhObSOIx5p5YmilNDGAP4nDZwx4oknCOLd7PdJpUbdkb8sBjs7w9y3bGKI221GYYLTv8zEUqUAv9OGxnmYZAq4LzGdeo5GmJc1e0wzNpltdMsgFdAVsTKjoqbjsIJOZhq_OgAQ/s1600/LatticeGame.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhObSOIx5p5YmilNDGAP4nDZwx4oknCOLd7PdJpUbdkb8sBjs7w9y3bGKI221GYYLTv8zEUqUAv9OGxnmYZAq4LzGdeo5GmJc1e0wzNpltdMsgFdAVsTKjoqbjsIJOZhq_OgAQ/s320/LatticeGame.bmp" width="320" /></a></div>
<br />
<br />
I was disappointed and a little irritated that everything had hinged on a single game. At least last year the format was a <a href="http://en.wikipedia.org/wiki/Double-elimination_tournament">double elimination</a>, so a single bad game couldn't knock a good bot out of the competition.
<br />
<br />
On the other hand, my expectations had been low to begin with. So the end result wasn't different from what I had been expecting. And I had the added satisfaction of knowing that my bot had generally been much stronger than expected. So all I could do was be philosophical about the loss...
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_ZO28EwnZdiXWhFqpEQlO4XcOpVQfQT2dtgOFABa2heZiNtOfqS_PorPrAY9XArBx4UZXgQPiBHRxeYXM7zzr9XWtPUiNNExC2tr4lu-O-5HKXh8K4F0YmuwANjh7OXSBd5k/s1600/ThoughtfulOrangutan.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_ZO28EwnZdiXWhFqpEQlO4XcOpVQfQT2dtgOFABa2heZiNtOfqS_PorPrAY9XArBx4UZXgQPiBHRxeYXM7zzr9XWtPUiNNExC2tr4lu-O-5HKXh8K4F0YmuwANjh7OXSBd5k/s1600/ThoughtfulOrangutan.jpg" /></a></div>
<br />
<br />
<hr />
<h3>The debacle of the official test harness</h3>
<h4>Bots that froze</h4>
Something surprising and quite sad happened to some contestants. On start-up their bots froze and did nothing for the rest of the game.
<br />
<br />
Last year's winner, Jaco Cronje, had this problem on a number of boards. But he won easily when his bot played. Fortunately he won enough games to be one of the lower seeds of the top 16, and during the knockout round he got the board that his bot did not freeze on. He won that game easily and made it through to the finals.
<br />
<br />
Another competitor, Bernhard Häussermann, had a similar issue which prevented his tank from moving in any of his games. This is very sad when you consider how much effort people put into their entries for the competition.
<br />
<br />
But was it really his own error that led to the frozen tanks?
<br />
<br />
<hr />
<h4>The mysterious case of the frozen tanks</h4>
In the week after the play-offs Jaco and Bernhard went on a mission to identify the reason for their tanks freezing.
<br />
<br />
During the final weekend before submission, Jaco had set his bot running through the night to check for weird issues. It had run fine. The competition organisers had said they would use the same test harness to run the competition as they had provided for the players to test against. So how was it possible for Jaco to get a different result during the play-offs?
<br />
<br />
The details of their digging are on <a href="https://groups.google.com/forum/#!topic/entelect-r100k-challenge/QEkot8SBMR0">this page of the google group</a> for the competition. Here's the short version...
<br />
<br />
A number of players had noticed that the id's of the 4 tanks were always 0, 1, 2 and 3. One player would get tanks 0 and 3. The other player would get tank id's 1 and 2. Since tank movement was resolved in the order of tank id's, this ordering made it a little fairer to choose who got precedence when two tanks tried to move into the same space on the same turn.
<br />
<br />
But in the play-offs the test harness started assigning very large tank id's. This caused out of range exceptions for some of the bots when they tried to save the tank information into a much smaller array.
<br />
<br />
Perhaps the test harness was modified to run multiple games without requiring a restart, instead of the correct approach of building a "tournament runner application" which would re-start the unmodified test harness between games.
<br />
<br />
<hr />
<h4>So why didn't my tanks dodge bullets?</h4>
On Wednesday I had a hunch about why my bullets didn't dodge bullets. And why they seemed to behave differently to what I'd been expecting.
<br />
<br />
So on Wednesday evening, after getting home from a <a href="http://www.meetup.com/Data-Science-Johannesburg/events/136784372/">Data Science meetup group</a>, I put my hunch to the test.
<br />
<br />
I simulated a tank id outside the range of 0 to 3. As expected, this caused my bot to also throw an out-of-range exception in the part of my code which configures tank movement sequences.
<br />
<br />
The reason my bot didn't freeze is that I had put in a fail-safe mechanism to catch any errors made by my bot.
<br />
<br />
If an error occurred, I would see if my bot had made a move yet. If it hadn't, I would run a very simple algorithm so that it would at least make a move rather than just freezing. My closest tank would attack the enemy base, and my other tank would attack the closest enemy tank. There was no bullet dodging logic, as I didn't want to risk running any extra code that could have been the cause of the original error.
<br />
<br />
So this explains why my bot had failed to dodge bullets. And also why it had acted so single-mindedly.
<br />
<br />
I had worked so hard on my scenario-driven bot in the final (extended) week of the competition. And it probably didn't even get used due to this bug. How sad. Since I still won my pool with this very basic bot, imagine what I could have done with my much more sophisticated bot!
<br />
<br />
<i>[Bernhard was in the same pool as me. So without the tank ID bug I might not have won the pool. But at least my best bot would have been on display.]</i>
<br />
<br />
<hr />
<h4>To be or not to be bitter</h4>
So at this point I have two choices...
<br />
<br />
Jaco and Bernhard have decompiled the jar file for the official test harness, and there should be no way that the test harness can assign a tank id out of the range 0 to 3. So the organizers must have changed the test harness after the final submission date, in which case it nullifies its purpose as a test harness. I could choose to be bitter about this.
<br />
<br />
My second choice is to suck it up and learn from the mistakes that I made. And I can only do that if I don't take the easy way out by blaming someone else for what went wrong (tempting though that is).
<br />
<br />
I used a variety of defensive coding practices, including my fail-safe algorithm. But I failed to code defensively against the possibility of the tank id's being outside the range of 0 to 3. I have a vague memory of feeling very uncomfortable at making this assumption. But I made the poor judgement call of ignoring my intuition and going with a cheap solution rather than the right solution (or even just a cheap solution with more defensive coding). That mistake is my mistake, and it's something I can learn from.
<br />
<br />
<hr />
<h4>Keeping the goal in mind</h4>
The big picture with entering competitions like this, is that you are not doing it for the prize money or the fame (though those are useful motivators to help give you a focus for your efforts). Doing so would be a form of moonlighting and is arguably unethical.
<br />
<br />
To some extent you are doing it for the fun and the camaraderie - the opportunity to interact with smart developers who enjoy challenging themselves with very tricky problems. But I get similar enjoyment from playing <a href="http://martinfowler.com/bliki/Eurogames.html">German-style board games</a> with my colleagues and friends - and with far less investment of time.
<br />
<br />
However the most important reason is for the learning experience. You are exposing yourself to a challenging situation which will stretch you to your limits, and in which you are bound to make some mistakes (as well as learn new coding techniques). The mistakes you make and the lessons you learn are part of your growth as a programmer.
<br />
<br />
With this perspective I would rather learn from my mistakes than look for someone to blame.
<br />
<br />
The alternative is to become bitter at the injustice of the situation. But life is full of injustice, and in many cases (such as this one) it is not caused by malevolence, but simply by human fallibility. As software developers we know all too well how seemingly innocuous changes can have unexpectedly large side-effects.
<br />
<br />
I'm not advocating that we should lower the standards we set for ourselves and others. But I think we should balance that against having the maturity to forgive ourselves and others when we make unfortunate mistakes despite our best intentions.
<br />
<br />
<hr />
<h3>The most valuable lesson I learnt</h3>
The most valuable lesson learnt was not about coding more defensively. Or about making testability a primary consideration. Or to avoid premature optimization. Or even to listen to my gut feeling when I'm feeling uncomfortable about code that I'm writing (valuable though that is).
<br />
<br />
Those are all things I already knew. As the saying goes, experience is that thing that allows you to recognise a mistake when you make it again!
<br />
<br />
The most valuable lesson was a new insight: <b>beware of the second system effect!</b>
<br />
<br />
Last year I placed fourth in the challenge with a fairly standard NegaMax search tree approach. I was conservative in my approach, did the basics well, and got a great result! But I had some innovative ideas which I never got as far as implementing...
<br />
<br />
This year I wanted to do better than last year. And I wanted to push the envelope more. And as a result I fell foul of the second system effect, and did worse than last year.
<br />
<br />
And this is not the only recent instance of this happening...
<br />
<br />
In 2011 I did really well in the <a href="http://fantasyrugby.udt.co.za/superrugby/page/game_info.html">UDT Super 15 Rugby Fantasy League</a> with a simple statistical model (built in Excel) and a linear programming model for selecting my Fantasy League team each round. And I placed in the 99.4th percentile (183rd out of around 30 000 entrants)!
<br />
<br />
In 2012 I wanted to do even better, so I used <a href="http://www.r-project.org/">the R programming language</a> to build a much more complex statistical model. I put a lot more effort into it. Yet I only placed in the 90th percentile. The second systems effect again!
<br />
<br />
In both cases I learnt far more from my second system than from the more conservative (but more successful) first system. So it's not all bad, since learning and self-development is the primary goal. But in future I'd like to find a better balance between getting a good result and "expressing my creativity".
<br />
<br />
<hr />
<h3>Wrap-up</h3>
As one of the eight finalists in the 2012 competition, I ended up being interviewed after the play-offs last year. That interview can be found <a href="http://www.youtube.com/watch?v=5EhrFwLtQZk">on youtube</a>.
<br />
<br />
Although I didn't make it to the final eight this year, I still found myself being roped into an interview. I'll update this page with the link once that interview has been uploaded to youtube.
<br />
<br />
For anyone who's interested, I've also uploaded the source code for my bot entry <a href="https://github.com/AndrewTweddle/EntelectChallenge2013">on GitHub</a>.
<br />
<br />
There's more detail on the approach that I and the other contestants took at <a href="https://groups.google.com/forum/#!topic/entelect-r100k-challenge/qXjGNaqJQ9g">this page on the google groups forum</a>.
<br />
<br />
<hr />
<h3>What's next?</h3>
At some point I'd like to post a blog entry on my shortest path algorithm, as I came up with a neat trick to side-step the various complexities of doing a Dijkstra algorithm with binary heaps, pairing heaps, r-divisions on planar graphs, and so forth.
<br />
<br />
But first I'd like to wrap up <a href="http://andrewtweddle.blogspot.com/2012/12/one-potato-two-potato.html">my series of posts</a> on an interesting problem in recreational Mathematics known as the Josephus problem.
<br />
<br />
<hr />
Andrew Tweddlehttp://www.blogger.com/profile/02458479910895055369noreply@blogger.com0tag:blogger.com,1999:blog-10106192.post-10958467737436783552013-09-13T18:18:00.000-04:002013-09-16T17:29:30.186-04:00The past two months...<h2>
A break from blogging</h2>
Over the past 2 months I've taken a break from blogging.<br />
<br />
During that time I've spent a week in the Kruger park, worked crazy hours on a software architecture investigation for a client, and worked equally crazy hours in my spare time on the <a href="http://challenge.entelect.co.za/">2013 Entelect Artificial Intelligence competition</a>.<br />
<br />
This blog post is a recap of those 3 activities.
<br />
<br />
<hr />
<h2>
The Kruger Park</h2>
I took my family camping at Punda Maria campsite in the Northern section of the Kruger Park from 14 to 19 July.
<br />
<br />
Back in 2008 we had one or our best Kruger trips ever in that part of the park, seeing no less than 4 leopard, including an excellent sighting where the leopard was metres from our car. So we had extolled the virtues of Northern Kruger to our friends John and Sarah, who were accompanying us with their 3 children.
<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKLFHrSW9-1xtAiyFtOXebYfb2cBqkgfXyIXo2gX7TWAO3xZg_PUPYr3f-0oh8z2rc4JeytUpjpjgrnCa3GLbf16w8EgTKAbtH5o05W_XPbLnKhkic4tj2E145WVU4mrEqEGc/s1600/Leopard_Compressed.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKLFHrSW9-1xtAiyFtOXebYfb2cBqkgfXyIXo2gX7TWAO3xZg_PUPYr3f-0oh8z2rc4JeytUpjpjgrnCa3GLbf16w8EgTKAbtH5o05W_XPbLnKhkic4tj2E145WVU4mrEqEGc/s1600/Leopard_Compressed.jpg" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A leopard sighting from our trip to Northern Kruger in October 2008</td></tr>
</tbody></table>
This time was very quiet, however, as the floods earlier in the year had allowed the animals to disperse much further than they normally would in the dry month of July. We narrowly missed seeing a leopard near the campsite and we only heard the lions at night.
<br />
<br />
In my opinion, Northern Kruger is the most beautiful part of the Kruger park, particularly the area near the Pafuri picnic site and along the Nyala drive. So although we were disappointed at not seeing any of the big cats, we still enjoyed our visit immensely.
<br />
<br />
Northern Kruger has a reputation for being a birders' paradise, and we certainly saw our fair share of beautiful birds. I even managed to capture a Lilac-Breasted Roller in flight, something I had tried many times before without success.
<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvnEXXES7bI3duXvTh0bx_hXQsICb0W5Q6Kik_hJfqQVY8raKvrVJtcCte_U73A2MtLYJgmxkYEvsdLtB3yK3hJpAmZ6qLfPGCj01J19RW6sTXTMydgdoY0vDAre2SD2AdR1w/s1600/LilacBreastedRollerInFlight.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="191" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvnEXXES7bI3duXvTh0bx_hXQsICb0W5Q6Kik_hJfqQVY8raKvrVJtcCte_U73A2MtLYJgmxkYEvsdLtB3yK3hJpAmZ6qLfPGCj01J19RW6sTXTMydgdoY0vDAre2SD2AdR1w/s320/LilacBreastedRollerInFlight.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A lilac-breasted roller in flight</td></tr>
</tbody></table>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjLWHvvKszDny5CW5sBnpv7xOGwouN37t-ESg_3EGOGWlpEqA9N5cXyvcll5PE5tpiWaOhp2V3OnivIeEfXxs69UgUq7ZLqWPmO6bvNhaKAO-QDup_pW85hesYc0CBL9FfS8w/s1600/Korhaan_Compressed.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjLWHvvKszDny5CW5sBnpv7xOGwouN37t-ESg_3EGOGWlpEqA9N5cXyvcll5PE5tpiWaOhp2V3OnivIeEfXxs69UgUq7ZLqWPmO6bvNhaKAO-QDup_pW85hesYc0CBL9FfS8w/s320/Korhaan_Compressed.jpg" width="213" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A korhaan sighting on the way back from Pafuri</td></tr>
</tbody></table>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_Q7pt_q41LKrtkBFDI8SMElPIybPkrNBVObkIrYfop2LWE-9NmJfvuv_K2-LbKju-GsaDHsSnTd6nGqhrkR1N-28fzsWtAtjm2L3wpH2uzFbCX3PFjGktjLXjJ51HGoN7tIU/s1600/Hornbill_on_pole_Resized.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_Q7pt_q41LKrtkBFDI8SMElPIybPkrNBVObkIrYfop2LWE-9NmJfvuv_K2-LbKju-GsaDHsSnTd6nGqhrkR1N-28fzsWtAtjm2L3wpH2uzFbCX3PFjGktjLXjJ51HGoN7tIU/s1600/Hornbill_on_pole_Resized.jpg" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A hornbill that scavenges left-overs at the Babalala picnic area</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: center;">
</div>
<hr />
<h2>
An architectural investigation</h2>
On returning from Kruger, I jumped straight into a 4 week architectural investigation for a client. The client was concerned about their dependence on a 3rd party integration hub whose host was experiencing financial uncertainty. My role was to assist the client in understanding their IT ecosystem, assess their and their business partners' dependencies on the 3rd party vendor, and provide a roadmap (with estimated costs) for mitigating the risk.
<br />
<br />
This is very different from the work that I do on a day to day basis. Most of the time I am one of the software designers/architects on a team of 25 to 30 developers, testers, business analysts, architects and project managers. I spend my day doing UML diagrams for new features, assisting developers when requested, estimating task sizes, performing code reviews, troubleshooting performance issues, and so forth. In other words, I am very much a cog in a bigger machine.<br />
<br />
With the architectural investigation I was on my own. I had complete autonomy to carry out the investigation as I saw fit. I love doing investigative work. I love work that has strategic impact. I love feeling my way towards a solution. So I revelled in this independence.
<br />
<br />
Although exciting, it was very intense work too. On Friday 16 August I gave a presentation of my findings to the technical stakeholders. I worked right through the night on both the Tuesday and Thursday night beforehand, clocking over 25 hours of work on Tuesday and Wednesday, and over 27 hours on Thursday and Friday.
<br />
<br />
I've done all-nighters before in my career. But never two in one week. It's definitely not something I would recommend...
<br />
<br />
I was very happy with the outcome of the work though, and the presentation seemed to go very well, despite getting off to a slow start due to my lack of sleep and the adrenalin of putting the finishing touches to the presentation only minutes before the meeting started!
<br />
<br />
The final presentation to the executive committee took place the following Tuesday. The previous presentation had included a lot more technical detail, and the focus had been on presenting a wide variety of options. The final presentation was much more condensed, and focused on 2 primary recommendations. The presentation went extremely well, and the feedback was very positive - both from the customer's CFO and from a number of my superiors at Dariel.<br />
<br />
A few days later I was asked whether I would like to do similar investigations in future. I replied that this was similar to asking someone who has just completed <a href="http://www.comrades.com/">the Comrades Ultra-marathon</a> whether they would like to do it again next year!<br />
<br />
<hr />
<h2>
The 2013 Entelect AI Challenge</h2>
Last year I participated in the inaugural <a href="http://archive-za.com/page/2030571/2013-05-06/http://challenge.entelect.co.za/Home/finalists">Entelect R 100,000 Artificial Intelligence Challenge</a>. I was delighted to place 4th out of 101 contestants.
<br />
<br />
I was really hoping to better that this year. However the 2013 challenge took place over the same period as the trip to Kruger and the architectural investigation. Additionally, last year's contestants had from mid-July until 24 September to submit their entries. This year the closing date was 2nd September. So there was simply less time to recover from the long hours on the architectural investigation.
<br />
<br />
This year's competition was to program two tanks to take on two other tanks on one of 8 boards, up to 81x81 squares in size, in a simultaneous movement tank battle based loosely on the 1980's arcade game <a href="http://en.wikipedia.org/wiki/Battle_City_(video_game)">Battle City</a>, and using SOAP web services to communicate to the server. This was a massive jump in complexity from last year's competition, which was a Tron-like turn-based game with one unit per player, played on a 30x30 sphere with communication via reading and writing a text file.
<br />
<br />
If I was a wiser man, I would have thrown in the towel before even starting. Fortunately I'm not!
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjX9l-c8_uSp2Gh33d_lQJfVlYnxhDKSnwRRDrEqQCEoO5pC7NceXVkEtw3fp1PrNIInwJLE_b89QMvjkXlsFOH46cRiGBac9mHPYM_22W0DeBzEwFB1EJ-Of3cBi17C7mbQxU/s1600/GameState.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjX9l-c8_uSp2Gh33d_lQJfVlYnxhDKSnwRRDrEqQCEoO5pC7NceXVkEtw3fp1PrNIInwJLE_b89QMvjkXlsFOH46cRiGBac9mHPYM_22W0DeBzEwFB1EJ-Of3cBi17C7mbQxU/s1600/GameState.bmp" /></a></div>
<br />
<br />
I started my career as an <a href="http://en.wikipedia.org/wiki/Operations_research">Operations Research</a> consultant at <a href="http://www.csir.co.za/">the CSIR</a> and I still retain a strong passion for decision optimization and decision automation. My outlet for that passion is entering competitions like the Entelect AI Challenge.
<br />
<br />
It's also a great way to keep my coding skills current, as my role as a software designer means that I don't get to write as much production code as I would like.<br />
<br />
So, against my better judgement, and despite all obstacles, I have participated in the Entelect programming challenge again this year! It's been very tiring and the stress levels have been immense. I have barely lurched over the finish line, assisted in no small measure by the final entry date being extended by a week from 2nd September to Monday 9th September.
<br />
<br />
But at least I have managed to complete an entry. And one that I'm reasonably proud of, even though I don't think it will be good enough to get me to the final 8 like last year.
<br />
<br />
The camaraderie last year was amazing, and the way the contestants have assisted and encouraged each other in this year's competition has been no less amazing. You would never guess we were competing with each other for a prize of R 100,000 (roughly $10,000). That's a lot of money and the prize for second place is tiny by comparison. But you'd never guess it by the way contestants have generously shared advice with each other, and encouraged each other to keep going when it gets tough. And believe me, this has been a very tough challenge this year (as shown by the far smaller number of people who got as far as submitting an entry).<br />
<br />
The play-offs for the competition take place tomorrow evening at the <a href="http://www.proteahotels.com/hotels/Pages/protea-hotel-fire-ice-melrose-arch.aspx" target="_blank">Fire and Ice Protea Hotel in Melrose Arch</a>, Johannesburg. I will be there to share in that spirit of camaraderie again.<br />
<br />
Although my expectations for my bot are low, my hopes are high. I'm hoping that my bot gives it horns...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglyd4syK8A4lWQVgACx4NbsoA7Mv0G8_Qwpk3i3Csl2oXgEi45eYZ7JZ6JZiVDM3VU2odWYW5H1rvePbQdigLZNswlyP7ZhegjvH4WtkVtdZOIs9LWCHQJ_zLcWOZfNfEQpCY/s1600/Waterbuck_Resized.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="213" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglyd4syK8A4lWQVgACx4NbsoA7Mv0G8_Qwpk3i3Csl2oXgEi45eYZ7JZ6JZiVDM3VU2odWYW5H1rvePbQdigLZNswlyP7ZhegjvH4WtkVtdZOIs9LWCHQJ_zLcWOZfNfEQpCY/s320/Waterbuck_Resized.jpg" width="320" /></a></div>
<br />
<br />
... and that some remaining bug doesn't sabotage my efforts and leave me meekly hiding in the shadows!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXJu3k7LdMB4iW-xIshesp2SMXPUKQLQt9j3q0Y95FTvDAj54VdYtZ8b0GEN7N4BU-ouH2s_1fpr0wHSqXohRCNZcUx7VZl1MmGhaFsaNrCH2fzfUL0DS19Ikli49PLftQVjU/s1600/Grysbok_Cropped_Small.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="264" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXJu3k7LdMB4iW-xIshesp2SMXPUKQLQt9j3q0Y95FTvDAj54VdYtZ8b0GEN7N4BU-ouH2s_1fpr0wHSqXohRCNZcUx7VZl1MmGhaFsaNrCH2fzfUL0DS19Ikli49PLftQVjU/s320/Grysbok_Cropped_Small.jpg" width="320" /></a></div>
<br />
<hr />
<h2>
What comes next?</h2>
I'm still busy with a couple of blog postings for <a href="http://andrewtweddle.blogspot.com/2012/12/one-potato-two-potato.html">the problem of calculating the last person left in a circle if every second person is asked to leave</a>. I hope to post those shortly and close off the series.
<br />
<br />
I worked out a few very nice algorithms for the Battle City AI challenge. So I'd like to write a series of articles on those as well. I'd also like to analyse some of the mistakes I made in the competition, and what I would do differently if I could start over.
<br />
<br />
And once my bot is knocked out the competition I'm planning on posting the code for my entry to GitHub.
<br />
<br />
I suspect this will be of interest to the other contestants in the competition (we have already been doing post-mortems of our strategies <a href="https://groups.google.com/forum/#!topic/entelect-r100k-challenge/qXjGNaqJQ9g">on the unofficial google groups forum for the competition</a>). But it may also have a few nice ideas that will be useful to people competing in other AI competitions in future.
<br />
<br />
So please keep a lookout for those!
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5rzBMjHUws4oUn4i4NW-TE5Ss0zXnTbARrjBaZEbeFcEFT36vhUqGOAG4QzfKQGCN0eyfuKom-DNInRlaZSPZHJHioSRW-17UHmDluvLOH7UdNlPxfVwRa1ZHkRQbME6k4lU/s1600/HornbillInCamp_Compressed.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5rzBMjHUws4oUn4i4NW-TE5Ss0zXnTbARrjBaZEbeFcEFT36vhUqGOAG4QzfKQGCN0eyfuKom-DNInRlaZSPZHJHioSRW-17UHmDluvLOH7UdNlPxfVwRa1ZHkRQbME6k4lU/s400/HornbillInCamp_Compressed.jpg" /></a></div>
<br />
<hr />
Andrew Tweddlehttp://www.blogger.com/profile/02458479910895055369noreply@blogger.com0tag:blogger.com,1999:blog-10106192.post-50780852771782176532013-06-09T16:14:00.001-04:002014-02-08T10:22:30.134-05:00More (an efficient circle elimination algorithm for arbitrary intervals)<!-- From http://irrep.blogspot.com/2011/07/mathjax-in-blogger-ii.html: -->
<script src="http://cdn.mathjax.org/mathjax/latest/MathJax.js" type="text/javascript">
MathJax.Hub.Config({
HTML: ["input/TeX","output/HTML-CSS"],
TeX: { extensions: ["AMSmath.js","AMSsymbols.js"],
equationNumbers: { autoNumber: "AMS" } },
extensions: ["tex2jax.js"],
jax: ["input/TeX","output/HTML-CSS"],
tex2jax: { inlineMath: [ ['$','$'], ["\\(","\\)"] ],
displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
processEscapes: true },
"HTML-CSS": { availableFonts: ["TeX"],
linebreaks: { automatic: true } }
});
</script>
<!-- Alex Gorbatchev syntax highlighter hosted on AWS (donation given) -->
<link href="http://alexgorbatchev.com/pub/sh/current/styles/shCore.css" rel="stylesheet" type="text/css"></link>
<link href="http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css" rel="stylesheet" type="text/css"></link>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js" type="text/javascript"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shAutoloader.js" type="text/javascript"></script>
<!-- F# syntax highlighting from http://tinesware.blogspot.com/2008/12/syntax-highlighting.html -->
<!-- Wrapping in a CDATA is a trick obtained from http://tinesware.blogspot.com/2008/12/syntax-highlighting.html -->
<script type="text/javascript">//<![CDATA[
/**
* SyntaxHighlighter
* http://alexgorbatchev.com/
*
* SyntaxHighlighter is donationware. If you are using it, please donate.
* http://alexgorbatchev.com/wiki/SyntaxHighlighter:Donate
*
* @version
* 2.0.320 (May 03 2009)
*
* @copyright
* Copyright (C) 2004-2009 Alex Gorbatchev.
*
* @license
* This file is part of SyntaxHighlighter.
*
* SyntaxHighlighter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SyntaxHighlighter is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with SyntaxHighlighter. If not, see <http://www.gnu.org/copyleft/lesser.html>.
*/
/* Syntax contributed by Julien O., 2009-10-05 */
SyntaxHighlighter.brushes.FSharp = function() {
/* main F# keywords */
var keywords1 =
/* section 3.4 */
'abstract and as assert base begin class default delegate do done ' +
'downcast downto elif else end exception extern false finally for ' +
'fun function if in inherit inline interface internal lazy let ' +
'match member module mutable namespace new null of open or ' +
'override private public rec return sig static struct then to ' +
'true try type upcast use val void when while with yield ' +
'asr land lor lsl lsr lxor mod ' +
/* identifiers are reserved for future use by F# */
'atomic break checked component const constraint constructor ' +
'continue eager fixed fori functor global include method mixin ' +
'object parallel params process protected pure sealed tailcall ' +
'trait virtual volatile ' ;
/* do, return, let, yield keywords with ! at the end are added later */
/* define names of main libraries in F# Core so we can link to it
http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/manual/namespaces.html
*/
var modules =
'Array Array2D Array3D Array4D ComparisonIdentity HashIdentity List ' +
'Map Seq SequenceExpressionHelpers Set CommonExtensions Event ' +
'ExtraTopLevelOperators LanguagePrimitives NumericLiterals Operators ' +
'OptimizedClosures Option String NativePtr Printf' ;
/* 17.2 & 17.3 */
var functions =
'abs acos asin atan atan2 ceil cos cosh exp ' +
'floor log log10 pown round sign sin sinh sqrt tan tanh ' +
'fst snd KeyValue not min max ' +
'ignore stdin stdout stderr ' ;
/* 17.2 Object Transformation Operators */
var objectTransformations =
'box hash sizeof typeof typedefof unbox'
/* 17.2 Exceptions */
var exceptions =
'failwith invalidArg raise rethrow' ;
/* 3.11 Pre-processor Declarations / Identifier Replacements*/
var constants =
'__SOURCE_DIRECTORY__ __SOURCE_FILE__ __LINE__';
/* Pervasives Types & Overloaded Conversion Functions */
var datatypes =
'bool byref byte char decimal double exn float float32 ' +
'FuncConvert ilsigptr int int16 int32 int64 int8 ' +
'nativeint nativeptr obj option ref sbyte single string uint16 ' +
'uint32 uint64 uint8 unativeint unit enum async seq dict ' ;
function fixComments(match, regexInfo) {
var css = (match[0].indexOf("///") == 0) ? 'color1' : 'comments';
return [new SyntaxHighlighter.Match(match[0], match.index, css)];
}
this.regexList = [
/* 3.3 Conditional compilation & 13.3 Compiler Directives + light /light off*/
{ regex: /\s*#\b(light|if|else|endif|indent|nowarn|r(eference)?|I|include|load|time|help|q(uit)?)/gm, css: 'preprocessor' },
{ regex: SyntaxHighlighter.regexLib.singleLineCComments, css:'comments' },
{ regex: SyntaxHighlighter.regexLib.multiLineCComments, css: 'comments' },
{ regex: /\s*\(\*[\s\S]*?\*\)/gm, css: 'comments' },
{ regex: SyntaxHighlighter.regexLib.doubleQuotedString, css: 'string' },
{ regex: /'[^']?'/gm, css: 'string' },
{ regex: new RegExp(this.getKeywords(keywords1), 'gm'), css: 'keyword' },
{ regex: /\s*(do|let|yield|return)*\!/gm, css: 'keyword' },
{ regex: new RegExp(this.getKeywords(modules), 'gm'), css: 'keyword' },
{ regex: new RegExp(this.getKeywords(functions), 'gm'), css: 'functions' },
{ regex: new RegExp(this.getKeywords(objectTransformations), 'gm'), css: 'keyword' },
{ regex: new RegExp(this.getKeywords(exceptions), 'gm'), css: 'keyword' },
{ regex: new RegExp(this.getKeywords(constants), 'gm'), css: 'constants' },
{ regex: new RegExp(this.getKeywords(datatypes), 'gm'), css: 'keyword' }
];
this.forHtmlScript(SyntaxHighlighter.regexLib.aspScriptTags);
};
SyntaxHighlighter.brushes.FSharp.prototype = new SyntaxHighlighter.Highlighter();
SyntaxHighlighter.brushes.FSharp.aliases = ['f#', 'f-sharp', 'fsharp'];
SyntaxHighlighter.config.bloggerMode = true;
SyntaxHighlighter.defaults['toolbar'] = false;
SyntaxHighlighter.all();
//]]>
</script>
<hr />
<h2>
All posts in this series:</h2>
<ul>
<li>
<a href="http://andrewtweddle.blogspot.com/2012/12/one-potato-two-potato.html">One Potato</a>: A description of the problem
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2012/12/two-potato-basic-equations.html">Two potato</a>: The basic equations
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/01/three-potato-algebraic-solution.html">Three potato</a>: An algebraic formula for the last person left
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/03/four-binary-calculation-method.html">Four</a>: A neat calculation method using binary numbers
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/03/five-potato-verifying-formulae-with-f.html">Five potato</a>: F# functions to check the various formulae
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/05/six-potato-building-calculation-graph.html">Six potato</a>: Using a calculation graph to see the pattern
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/06/seven-potato-generalizing-to-other.html">Seven potato</a>: Generalizing to other intervals
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/06/more-efficient-circle-elimination.html">More</a>: An efficient algorithm for arbitrary intervals
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2014/02/researching-josephus-problem.html">Online research</a>: In which I discover that it is called the Josephus problem
</li>
</ul>
<hr />
<h2>
Introduction</h2>
In my previous post I generated a recursive equation for calculating the last person left in a circle if every $k^{\text{th}}$ person is removed from the circle.
<br />
<br />
I also wrote an F# program which highlighted a weakness of this algorithm. For circles with more than 60 000 people, the F# algorithm would experience a stack overflow.
<br />
<br />
In this post I am going to develop a more efficient algorithm to address this flaw.
<br />
<br />
<i>Warning: Lots of algebra ahead!</i>
<br />
<br />
<b>The initial formatting of the Mathematical equations can be slow. If so, please be patient.</b>
<br />
<br />
<hr />
<h2>
The recursive equation for arbitrary intervals</h2>
To recap, here is the main equation from the previous blog post:
$$
f_k(n) = [f_k(n-1) + k - 1 ] \bmod n + 1 \tag{4}
$$
<br />
<i>[This is very much a continuation of the previous post, so I have gone with the same equation numbering].</i>
<br />
<br />
<hr />
<h2>
Arranging the function outputs in rows</h2>
Recall the calculation graph from <a href="http://andrewtweddle.blogspot.com/2013/05/six-potato-building-calculation-graph.html">post 6 in the series</a>:
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3gETz3hmRJ9ElkjlirxEwn4CymhIqFx99DMCHSrmistciGOCHYtbSKrVFbF_Rfwd5N7dTjuIQveOTYEAKlT9sLfUTDkgCvfESJe4sVEZ2Q0nVdEZv_lU5nQI_TkTUtj4n3jE/s1600/CirclesGraph_3Levels.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="304" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3gETz3hmRJ9ElkjlirxEwn4CymhIqFx99DMCHSrmistciGOCHYtbSKrVFbF_Rfwd5N7dTjuIQveOTYEAKlT9sLfUTDkgCvfESJe4sVEZ2Q0nVdEZv_lU5nQI_TkTUtj4n3jE/s509/CirclesGraph_3Levels.png" width="509" /></a></div>
<br />
<br />
Notice how $f(n)$ "clocks over" for the first value in each row.
<br />
<br />
Let's arrange the values of $f_k(n)$ into rows in a similar way. To do this I am going to consider the values of $n$ at which $f_k(n)$ clocks over. Every time this happens, a new row will be started.
<br />
<br />
Let $r$ be the index of a row in the graph.
<br />
Let $n_r$ be the first value of $n$ in row $r$.
<br />
<br />
Now consider the previous value of $n$: $n_r - 1$. Since the modulo operator clocks over for $n_r$, we can take the argument to this operator in equation 4, and we must have:
<br />
$$
\begin{align*}
f_k(n_r - 1) + k - 1 &\ge n_r \\
\therefore f_k(n_r - 1) &\ge n_r - (k - 1)\\
\end{align*}
$$
<br />
However, we also know - by the definition of $f_k(n)$ - that:
<br />
$$
f_k(n_r - 1) <= n_r - 1
$$
<br />
So it must be that:
<br />
$$
\begin{align*}
f_k(n_r - 1) &= n_r - j & \text{for some } j \in \mathbb{N} \text{ such that } 1 \le j \le k-1 \tag{5}
\end{align*}
$$
<br />
From our definition of $n_r$ we know that $f_k(n)$ doesn't clock over for any $n \in \left\{n_{r-1}+1, n_{r-1} + 2, ..., n_r - 1\right\}$.
<br />
<br />
And because it doesn't clock over, we can remove the mod operator in equation 4, which simplifies to:
$$
\begin{align*}
f_k(n) &= [f_k(n-1) + k - 1 ] \bmod n + 1 & \text{from equation 4}\\
&= [f_k(n-1) + k - 1 ] + 1 \\
&= f_k(n-1) + k & \forall n \in \left\{n_{r-1}+1, n_{r-1} + 2, ..., n_r - 1\right\} \\
\\
\therefore f_k(n_{r-1} + i) &= f_k(n_{r-1}) + ik &\forall i \in \left\{0, 1, \ldots, n_r - n_{r-1}-1 \right\} \tag{6} \\
\\
\text{In particular:} \\
f_k(n_r - 1) &= f_k(n_{r-1}) + k.(n_r - n_{r-1} - 1) \tag{7} \\
\end{align*}
$$
<br />
Equations 5 and 7 give us two different formulae for $f_k(n_r-1)$, so we can combine them:
$$
\begin{align*}
n_r - j &= f_k(n_{r-1}) + k.(n_r - n_{r-1} - 1)\\
\Leftrightarrow (k-1).n_r &= k.n_{r-1} + k - j - f_k(n_{r-1}) \tag{8}
\end{align*}
$$
<br />
First we are going to use equation 8 to determine the value of j.
<br />
<br />
We can re-arrange equation 8 as follows:
$$
\begin{align*}
j - 1 & = k.n_{r - 1} + k - 1 - f_k(n_{r-1}) - (k-1).n_r \\
& = [(k-1)+1].n_{r - 1} + (k-1) - f_k(n_{r-1}) - (k-1).n_r \\
& = n_{r-1} - f_k(n_{r-1}) + (k-1).[n_{r - 1} + 1 - n_r] \tag{9}
\end{align*}
$$
<br />
Why $j-1$ on the left side of the equation?
<br />
<br />
Recall from equation 5 that $1 \leq j \leq k-1 $. So $0 \leq j - 1 \leq k-2 < k-1$.
<br />
<br />
And hence $j - 1 = (j - 1) \bmod (k - 1)$. This allows us to simplify the right hand side of equation 9 by taking the "$\bmod (k-1)$" of both sides.
<br />
<br />
So:
$$
\begin{align*}
j - 1 & = [j - 1] \bmod (k - 1) \\
& = [n_{r-1} - f_k(n_{r-1}) + (k-1).[n_{r - 1} + 1 - n_r]] \bmod (k - 1) & \text{from equation 9} \\
& = [n_{r-1} - f_k(n_{r-1})] \bmod (k - 1) & \text{since multiples of }k - 1\text{ can be removed} \\
\\
\Rightarrow j & = [n_{r-1} - f_k(n_{r-1})] \bmod (k - 1) + 1 \tag{10}
\end{align*}
$$
<br />
<br />
Now that we have a formula for j, we can determine both $n_r$ (using equation 8) and $f_k(n_r)$ (using equations 4 and 5).
<br />
<br />
<hr />
Let's determine $n_r$ first. Substituting equation 10 into equation 8 gives:
<br />
<br />
$$
\begin{align*}
(k-1).n_r &= k.n_{r-1} + k - j - f_k(n_{r-1}) &\text{from equation 8} \\
&= k.n_{r-1} + k - ([n_{r-1} - f_k(n_{r-1})] \bmod (k - 1) + 1) - f_k(n_{r-1}) &\text{from equation 10} \\
&= [(k-1) + 1].n_{r-1} + (k-1) - [n_{r-1} - f_k(n_{r-1})] \bmod (k - 1) - f_k(n_{r-1}) \\
&= (k-1).n_{r-1} + n_{r-1} + (k-1) - [n_{r-1} - f_k(n_{r-1})] \bmod (k - 1) - f_k(n_{r-1}) \\
&= (k-1)[n_{r-1} + 1] + n_{r-1} - f_k(n_{r-1}) - [n_{r-1} - f_k(n_{r-1})] \bmod (k - 1) \\
\Rightarrow n_r &= n_{r-1} + 1 + \frac{n_{r-1} - f_k(n_{r-1}) - [n_{r-1} - f_k(n_{r-1})] \bmod (k - 1) }{k-1} \\
\end{align*}
$$
This rather complicated quotient can be simplified quite significantly by observing that:
$$
\frac{m - m \bmod d}{d} = \lfloor \frac{m}{d} \rfloor
$$
<br />
This follows by expressing $m$ as:
$$
\begin{align*}
m &= qd+r & \text{where }0 \leq r < d &\text{i.e. }r = m \bmod d \\
\Rightarrow q & = \frac{m - r}{d} \\
& = \frac{m - m \bmod d}{d} \\
\text{But: } q = \lfloor \frac{m}{d} \rfloor &\text{and the result follows}
\end{align*}
$$
<br />
<br />
So substituting $n_{r-1} - f_k(n_{r-1})$ for $m$ and $k-1$ for $d$ we obtain the following simplification:
<br />
<br />
$$
n_r = n_{r-1} + 1 + \lfloor \frac{n_{r-1} - f_k(n_{r-1}) }{ k - 1 } \rfloor \tag{11}
$$
<br />
<br />
<hr />
Now let's determine the value of $f_k(n_r)$:
<br />
<br />
$$
\begin{align*}
f_k(n_r) &= [f_k(n_r - 1) + k - 1 ] \bmod n_r + 1 & \text{from equation 4} \\
&= [(n_r - j) + k - 1 ] \bmod n_r + 1 & \text{from equation 5} \\
&= [k - 1 - j] \bmod n_r + 1 \\
&= [k - 1 - ([n_{r-1} - f_k(n_{r-1})] \bmod (k - 1) + 1)] \bmod n_r + 1 & \text{from equation 10} \\
&= [k - 2 - [n_{r-1} - f_k(n_{r-1})] \bmod (k - 1)] \bmod n_r + 1
\end{align*}
$$
<br />
This is already sufficient. However we can simplify things further. When $n_r \ge k-1$, the $ \text{mod }n_r $ can be dropped. This gives the following set of formulae:
<br />
<br />
$$
\begin{align*}
f_k(n_{1}) & = f(1) = 1 \tag{12} \\
f_k(n_r) & = k - 1 - [n_{r-1} - f_k(n_{r-1})] \bmod (k - 1) & \text{ where } n_r \geq k - 1 \tag{13} \\
f_k(n_r) & = [k - 2 - [n_{r-1} - f_k(n_{r-1})] \bmod (k - 1)] \bmod n_r + 1 & \forall n_r \text{ where } r > 1 \tag{14} \\
\end{align*}
$$
<br />
<br />
<hr />
So equations 11 through to 14 provide formulae for:
<br />
<ul>
<li>
$n_r$ in terms of $n_{r-1}$ and $f_k(n_{r-1})$
</li>
<li>
$f_k(n_r)$ in terms of $n_r$, $n_{r-1}$ and $f_k(n_{r-1})$
</li>
<li>
$n_1$ and $f_k(n_1)$
</li>
</ul>
<br />
With these pieces in place we are ready to create a more efficient algorithm for calculating $f_k(n)$ for arbitrary n.
<br />
<br />
<hr />
<h2>
The row-based algorithm</h2>
Given a value $n$ for which we wish to calculate $f_k(n)$, we can't work backwards down to $f_k(1)$ as we have done in the past. The problem is that we don't know which row $r$ it falls within, or what $n_r$ and $f(n_r)$ are.
<br />
<br />
Instead we are going to have to work our way forwards to find the row that $n$ belongs in. We will start at $n_{1} = 1, f_k(1) = 1$. From this we will derive $n_{2}$ and $f(n_{2})$. We will continue generating this sequence of values for $n_r$ and $n_{r+1}$ until we find a value of $r$ for which $n_r \le n < n_{r+1}$. When we do, we can use equation 6 to determine $f_k(n)$:
$$
f_k(n) = f_k(n_r) + k.(n-n_r) \tag{15}
$$
<br />
While working our way through the rows we will keep track of the values for the next row: $n_{r+1}$ and $f_k(n_{r+1})$. This will allow us to easily calculate $n_{r+2}$ and $f_k(n_{r + 2})$.
<br />
<br />
Below is an F# function to calculate these values for the next row:
<br />
<br />
<pre class="brush: fsharp">let getNextRowStartSizeAndLabel thisRowStartSize thisRowStartLabel interval =
let floor
= int( System.Math.Floor( double(thisRowStartSize - thisRowStartLabel)
/ double(interval - 1) )
)
let nextRowStartSize = thisRowStartSize + 1 + floor
let nextRowStartLabel =
if nextRowStartSize < interval - 1 then
( interval - 2
- (thisRowStartSize - thisRowStartLabel) % (interval - 1)
) % nextRowStartSize + 1
else
interval - 1 - (thisRowStartSize - thisRowStartLabel) % (interval - 1)
(nextRowStartSize, nextRowStartLabel)
</pre>
<br />
The variables map to the formula as follows:
<br />
<ul>
<li>
thisRowStartSize $\mapsto n_r$
</li>
<li>
thisRowStartLabel $\mapsto f_k(n_r)$
</li>
<li>
interval $\mapsto k$
</li>
<li>
nextRowStartSize $\mapsto n_{r+1}$
</li>
<li>
nextRowStartLabel $\mapsto f_k(n_{r+1})$
</li>
</ul>
<br />
The following F# functions use the function above to calculate $f_k(n)$:
<br />
<pre class="brush: fsharp">let rec getLabelOfLastLeftInCircleWithIntervalByRow
thisRowStartSize thisRowStartLabel
nextRowStartSize nextRowStartLabel
interval sizeOfCircle =
if (sizeOfCircle >= thisRowStartSize) && (sizeOfCircle < nextRowStartSize) then
thisRowStartLabel + interval * (sizeOfCircle - thisRowStartSize)
else
let (newNextRowStartSize, newNextRowStartLabel)
= getNextRowStartSizeAndLabel nextRowStartSize nextRowStartLabel interval
getLabelOfLastLeftInCircleWithIntervalByRow
nextRowStartSize nextRowStartLabel
newNextRowStartSize newNextRowStartLabel
interval sizeOfCircle
let calcLastLeftInCircleWithIntervalByRow interval sizeOfCircle =
if sizeOfCircle < 1 then
raise (System.ArgumentException("The size of the circle must be positive!"))
if interval < 2 then
raise (System.ArgumentException("The interval must be 2 or more!"))
let (nextRowStartSize, nextRowStartLabel) = getNextRowStartSizeAndLabel 1 1 interval
getLabelOfLastLeftInCircleWithIntervalByRow
1 1 nextRowStartSize nextRowStartLabel interval sizeOfCircle
</pre>
<br />
<hr />
<h2>
The efficiency of the row-based algorithm</h2>
Look back at equation 11:
<br />
$$
n_r = n_{r-1} + 1 + \lfloor \frac{n_{r-1} - f_k(n_{r-1})}{k-1} \rfloor
$$
<br />
<br />
Consider what happens as $n_{r-1}$ starts becoming large compared to $k$. $f_k(n_{r-1}) \le k$ (since a "clock-over" occurs at $n_{r-1}$), so this term will become very small. So as $n_{r-1}$ becomes large:
<br />
$$
n_r \approx (1 + \frac{1}{k-1}).n_{r-1}
$$
<br />
<br />
So the $\left\{n_r\right\}$ series is approximately a geometric progression. Since its values are going to grow exponentially, the algorithm will rapidly find the row containing n, even for very large values of n. The size of the stack is going to scale as O(log n), so we are far less likely to reach a value of n which will cause stack overflows.
<br />
<br />
If we run the latest F# algorithm it is indeed blindingly fast:
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAQbdD1ESMyKPXB3PkZBvgeE4JU2570h4JdHtfVLNDnWB_9qdzaBcO5AyYCA9zIHnsj-Aqhz2hV12KohHN5WwxhJ00hcrnvaV0R9hQz3YJRetBz6MfD1alO4iz0DkkOOe6Wmc/s1600/calcLastLeftInCircleWithIntervalByRow.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="417" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAQbdD1ESMyKPXB3PkZBvgeE4JU2570h4JdHtfVLNDnWB_9qdzaBcO5AyYCA9zIHnsj-Aqhz2hV12KohHN5WwxhJ00hcrnvaV0R9hQz3YJRetBz6MfD1alO4iz0DkkOOe6Wmc/s665/calcLastLeftInCircleWithIntervalByRow.PNG" width="665" /></a></div>
<br />
It was able to handle some very large 32 bit numbers almost instantaneously.
<br />
<br />
I would expect it to fail when the next higher $n_r$ value is larger than the maximum 32 bit integer. It might be possible to work around this by inserting a "try catch" block to detect and recover from this situation, since we can still calculate the answer using the previous value of $n_r$.
<br />
<br />
I thought that another failure condition could be with very large values of $k$, since the exponential growth rate would be very small when $k$ is large. So I tested the algorithm with $k$ and $n$ both equal to a billion (one thousand million). The calculation took slightly under 32 seconds on my i5 laptop.
<br />
<br />
<hr />
<h2>
Displaying the rows in the row-based algorithm</h2>
Ideally we would like a closed form solution for arbitrary k. I tried looking for patterns in the rows for various values of k, but I couldn't see a consistent pattern.
<br />
<br />
For anyone who would like to try, or just if you're curious to see the row structure in action, here is an F# script to allow you to see all $(n, f_k(n))$ pairs arranged in rows. It depends on the function getNextRowStartSizeAndLabel defined earlier, so make sure that function has been defined in your F# interactive session:
<br />
<pre class="brush: fsharp">let rec showLabelsOfLastLeftInCircleWithIntervalByRow
showFullRow thisRowStartSize thisRowStartLabel
nextRowStartSize nextRowStartLabel interval
sizeOfCircle maxSizeOfCircle =
if (sizeOfCircle <= maxSizeOfCircle) then
let result = thisRowStartLabel + interval * (sizeOfCircle - thisRowStartSize)
if sizeOfCircle = thisRowStartSize then
System.Console.WriteLine()
System.Console.Write( "f({0})={1}; ", sizeOfCircle, result)
elif showFullRow then
System.Console.Write( "f({0})={1}; ", sizeOfCircle, result)
if sizeOfCircle < nextRowStartSize - 1 then
showLabelsOfLastLeftInCircleWithIntervalByRow
showFullRow thisRowStartSize thisRowStartLabel
nextRowStartSize nextRowStartLabel
interval (sizeOfCircle+1) maxSizeOfCircle
else
let (newNextRowStartSize, newNextRowStartLabel) =
getNextRowStartSizeAndLabel nextRowStartSize nextRowStartLabel interval
showLabelsOfLastLeftInCircleWithIntervalByRow
showFullRow nextRowStartSize nextRowStartLabel
newNextRowStartSize newNextRowStartLabel
interval (sizeOfCircle+1) maxSizeOfCircle
let showLastLeftInCircleWithIntervalByRow showFullRow interval maxSizeOfCircle =
if maxSizeOfCircle < 1 then
raise
( System.ArgumentException(
"The maximum size of the circle must be 1 or more!")
)
if interval < 2 then
raise
( System.ArgumentException(
"The interval must be 2 or more!")
)
let (nextRowStartSize, nextRowStartLabel) =
getNextRowStartSizeAndLabel 1 1 interval
showLabelsOfLastLeftInCircleWithIntervalByRow
showFullRow 1 1
nextRowStartSize nextRowStartLabel interval
1 maxSizeOfCircle
System.Console.WriteLine()
</pre>
<br />
When the showFullRow parameter to the function is false, then only the first pair in each row will be shown. I find this is a lot more useful than seeing the intermediate values as well, which soon leads to each console line overflowing.
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7q2g3SPMH_9yEVEz-QnVb9F-eEJP_E2dfiGNocMYXvckfLpwmCU6haBXSKH1JSER5DoeYCynfS5wj-scze-tvfV2ZInM9V0kPFYZ2SYCuUEtlZybU8zxLzVP07_KeGZmkQ_A/s1600/showLastLeftInCircleWithIntervalByRow.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="625" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7q2g3SPMH_9yEVEz-QnVb9F-eEJP_E2dfiGNocMYXvckfLpwmCU6haBXSKH1JSER5DoeYCynfS5wj-scze-tvfV2ZInM9V0kPFYZ2SYCuUEtlZybU8zxLzVP07_KeGZmkQ_A/s625/showLastLeftInCircleWithIntervalByRow.PNG" width="547" /></a></div>
<br />
<hr />
<h2>
Validating the various algorithms against one other</h2>
The following F# script can be used to check that the 3 algorithms produce the same answers:
<br />
<pre class="brush: fsharp">
type circleCalculationWithInterval = {
SizeOfCircle: int;
Interval: int;
LastLeftInCircleByBruteForce: int;
LastLeftInCircleByRecursiveFormula: int;
LastLeftInCircleByRow: int
}
let getCircleCalculationWithInterval includeBruteForce interval sizeOfCircle =
// Brute force is slow to calculate. Provide the option to omit it...
let lastLeftInCircleByBruteForce =
match includeBruteForce with
| true -> calcLastLeftInCircleWithIntervalByBruteForce interval sizeOfCircle
| false -> 0
let lastLeftInCircleByRecursiveFormula =
calcLastLeftInCircleWithIntervalByRecursiveFormula interval sizeOfCircle
let lastLeftInCircleByRow =
calcLastLeftInCircleWithIntervalByRow interval sizeOfCircle
let circleCalc = {
SizeOfCircle = sizeOfCircle;
Interval = interval;
LastLeftInCircleByBruteForce = lastLeftInCircleByBruteForce;
LastLeftInCircleByRecursiveFormula = lastLeftInCircleByRecursiveFormula;
LastLeftInCircleByRow = lastLeftInCircleByRow
}
circleCalc
</pre>
<br />
You can then check the results using snippets similar to the following:
<br />
<br />
<pre class="brush: fsharp">
// With brute force:
[1..1000] |> List.map (getCircleCalculationWithInterval true 2) |> List.filter (
fun cc -> cc.LastLeftInCircleByRow <> cc.LastLeftInCircleByBruteForce
|| cc.LastLeftInCircleByRow <> cc.LastLeftInCircleByRecursiveFormula
);;
[1..100] |> List.map (getCircleCalculationWithInterval true 100) |> List.filter (
fun cc -> cc.LastLeftInCircleByRow <> cc.LastLeftInCircleByBruteForce
|| cc.LastLeftInCircleByRow <> cc.LastLeftInCircleByRecursiveFormula
);;
// Without brute force:
[1..10000] |> List.map (getCircleCalculationWithInterval false 2) |> List.filter (
fun cc -> cc.LastLeftInCircleByRow <> cc.LastLeftInCircleByRecursiveFormula
);;
[1..10000] |> List.map (getCircleCalculationWithInterval false 100) |> List.filter (
fun cc -> cc.LastLeftInCircleByRow <> cc.LastLeftInCircleByRecursiveFormula
);;
</pre>
<br />
<hr />
<h2>A new proof for the formula when removing every second person</h2>
We can use the general formulae to provide a new derivation of the closed form solution when every second person is removed from the circle.
<br />
<br />
The proof ends up being remarkably simple, because $k - 1 = 1$. Terms such as $(...) \bmod (k-1)$ simply fall away, since any integer mod 1 is zero. Also some terms involve a division by $k-1$, and these also simplify very nicely.
<br />
<br />
But these simplifications only work for $k = 2$. This reduces my confidence of finding a closed form solution for the general case.
<br />
<br />
Anyway, here's the proof:
<br />
$$
\begin{align*}
f_2(n_r) & = k - 1 - [n_{r-1} - f_k(n_{r-1})] \bmod (k - 1) & \text{ from equation 13, since } n_r \geq k - 1 = 1 \\
& = 2 - 1 - [n_{r-1} - f_2(n_{r-1})] \bmod 1 & \text{(but any number mod 1 is 0)} \\
& = 1 & \text{ for }n_r > 1 \\
\\
\text{but: } f_2(n_{1}) & = f(1) = 1 & \text{from equation 12} \\
\\
\therefore f_2(n_r) & = 1 & \forall n_r \tag{16} \\
\\
n_r & = n_{r-1} + 1 + \lfloor \frac{n_{r-1} - f_k(n_{r-1}) }{ k - 1 } \rfloor & \text{ from equation 11} \\
& = n_{r-1} + 1 + \lfloor n_{r-1} - f_k(n_{r-1}) \rfloor & \\
& = n_{r-1} + 1 + n_{r-1} - f_k(n_{r-1}) & \text{(since floor has an integer argument)} \\
& = n_{r-1} + 1 + n_{r-1} - 1 & \text{since }f_k(n_{r-1}) = 1 \text{ by equation 16 } \\
& = 2.n_{r-1} & \text{a geometric progression} \tag{17} \\
\\
\therefore n_r & = 2^{r-1} & \text{by equations 16 and 17} \tag{18} \\
\\
f_2(n) & = f_2(n_r) + 2.(n-n_r) & \text{from equation 15, where: } n_r \le n < n_{r+1} \\
& = 1 + 2(n - 2^{r-1}) & \text{ where: } 2^{r-1} \le n \le 2^r \text{(by equation 18)}\\
& = 1 + 2n - 2.2^{r-1} & \text{where: } 2^{r-1} \le 2^{log_2 n} < 2^r \\
& = 1 + 2n - 2.2^{\lfloor log_2 n \rfloor} & \text{since: } 2^{r-1} = 2^{\lfloor log_2 n \rfloor} < 2^{r}\\
& = 2n + 1 + 2^{{\lfloor log_2 n \rfloor} + 1} \tag{19} \\
\end{align*}
$$
<br/>
And this is the same equation derived in <a href="http://andrewtweddle.blogspot.com/2013/01/three-potato-algebraic-solution.html">blog post three</a> in the series, albeit through a completely different method.
<br />
<br />
<hr />
<h2>
Conclusion</h2>
In this blog post and the previous post I derived formulae which are applicable to intervals other than 2.
<br />
<br />
I was able to use these formulae to find a new proof of the closed form solution for the original interval of 2 (i.e. removing every second person from the circle).
<br />
<br />
I wasn't able to derive a closed form solution for arbitrary intervals. However I was able to devise an efficient algorithm for calculating the answer.
<br />
<br />
<hr />
<h2>
Next time</h2>
I feel that I have progressed as far as I can with the problem on my own. I don't know whether a closed form solution exists. But even if it does, I doubt I'm going to find it.
<br />
<br />
So now it's time to search the internet to see if this is a problem which other people have solved. In my next blog post, I hope to report back on what I found.
<br />
<br />
I'm also hoping to share some of the scripts and tools I used to generate the artefacts in this series of blog posts.
<br />
<br />
But most of all, I'm looking forward to wrapping up this series, so that I can move on to other topics that capture my interest!
<br />
<br />
<hr />
Andrew Tweddlehttp://www.blogger.com/profile/02458479910895055369noreply@blogger.com0tag:blogger.com,1999:blog-10106192.post-14624736586777069192013-06-06T16:01:00.001-04:002014-02-08T10:22:46.416-05:00Seven potato (generalizing to other intervals)<!-- From http://irrep.blogspot.com/2011/07/mathjax-in-blogger-ii.html: -->
<script src="http://cdn.mathjax.org/mathjax/latest/MathJax.js" type="text/javascript">
MathJax.Hub.Config({
HTML: ["input/TeX","output/HTML-CSS"],
TeX: { extensions: ["AMSmath.js","AMSsymbols.js"],
equationNumbers: { autoNumber: "AMS" } },
extensions: ["tex2jax.js"],
jax: ["input/TeX","output/HTML-CSS"],
tex2jax: { inlineMath: [ ['$','$'], ["\\(","\\)"] ],
displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
processEscapes: true },
"HTML-CSS": { availableFonts: ["TeX"],
linebreaks: { automatic: true } }
});
</script>
<!-- Alex Gorbatchev syntax highlighter hosted on AWS (donation given) -->
<link href="http://alexgorbatchev.com/pub/sh/current/styles/shCore.css" rel="stylesheet" type="text/css"></link>
<link href="http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css" rel="stylesheet" type="text/css"></link>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js" type="text/javascript"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shAutoloader.js" type="text/javascript"></script>
<!-- F# syntax highlighting from http://tinesware.blogspot.com/2008/12/syntax-highlighting.html -->
<!-- Wrapping in a CDATA is a trick obtained from http://tinesware.blogspot.com/2008/12/syntax-highlighting.html -->
<script type="text/javascript">//<![CDATA[
/**
* SyntaxHighlighter
* http://alexgorbatchev.com/
*
* SyntaxHighlighter is donationware. If you are using it, please donate.
* http://alexgorbatchev.com/wiki/SyntaxHighlighter:Donate
*
* @version
* 2.0.320 (May 03 2009)
*
* @copyright
* Copyright (C) 2004-2009 Alex Gorbatchev.
*
* @license
* This file is part of SyntaxHighlighter.
*
* SyntaxHighlighter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SyntaxHighlighter is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with SyntaxHighlighter. If not, see <http://www.gnu.org/copyleft/lesser.html>.
*/
/* Syntax contributed by Julien O., 2009-10-05 */
SyntaxHighlighter.brushes.FSharp = function() {
/* main F# keywords */
var keywords1 =
/* section 3.4 */
'abstract and as assert base begin class default delegate do done ' +
'downcast downto elif else end exception extern false finally for ' +
'fun function if in inherit inline interface internal lazy let ' +
'match member module mutable namespace new null of open or ' +
'override private public rec return sig static struct then to ' +
'true try type upcast use val void when while with yield ' +
'asr land lor lsl lsr lxor mod ' +
/* identifiers are reserved for future use by F# */
'atomic break checked component const constraint constructor ' +
'continue eager fixed fori functor global include method mixin ' +
'object parallel params process protected pure sealed tailcall ' +
'trait virtual volatile ' ;
/* do, return, let, yield keywords with ! at the end are added later */
/* define names of main libraries in F# Core so we can link to it
http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/manual/namespaces.html
*/
var modules =
'Array Array2D Array3D Array4D ComparisonIdentity HashIdentity List ' +
'Map Seq SequenceExpressionHelpers Set CommonExtensions Event ' +
'ExtraTopLevelOperators LanguagePrimitives NumericLiterals Operators ' +
'OptimizedClosures Option String NativePtr Printf' ;
/* 17.2 & 17.3 */
var functions =
'abs acos asin atan atan2 ceil cos cosh exp ' +
'floor log log10 pown round sign sin sinh sqrt tan tanh ' +
'fst snd KeyValue not min max ' +
'ignore stdin stdout stderr ' ;
/* 17.2 Object Transformation Operators */
var objectTransformations =
'box hash sizeof typeof typedefof unbox'
/* 17.2 Exceptions */
var exceptions =
'failwith invalidArg raise rethrow' ;
/* 3.11 Pre-processor Declarations / Identifier Replacements*/
var constants =
'__SOURCE_DIRECTORY__ __SOURCE_FILE__ __LINE__';
/* Pervasives Types & Overloaded Conversion Functions */
var datatypes =
'bool byref byte char decimal double exn float float32 ' +
'FuncConvert ilsigptr int int16 int32 int64 int8 ' +
'nativeint nativeptr obj option ref sbyte single string uint16 ' +
'uint32 uint64 uint8 unativeint unit enum async seq dict ' ;
function fixComments(match, regexInfo) {
var css = (match[0].indexOf("///") == 0) ? 'color1' : 'comments';
return [new SyntaxHighlighter.Match(match[0], match.index, css)];
}
this.regexList = [
/* 3.3 Conditional compilation & 13.3 Compiler Directives + light /light off*/
{ regex: /\s*#\b(light|if|else|endif|indent|nowarn|r(eference)?|I|include|load|time|help|q(uit)?)/gm, css: 'preprocessor' },
{ regex: SyntaxHighlighter.regexLib.singleLineCComments, css:'comments' },
{ regex: SyntaxHighlighter.regexLib.multiLineCComments, css: 'comments' },
{ regex: /\s*\(\*[\s\S]*?\*\)/gm, css: 'comments' },
{ regex: SyntaxHighlighter.regexLib.doubleQuotedString, css: 'string' },
{ regex: /'[^']?'/gm, css: 'string' },
{ regex: new RegExp(this.getKeywords(keywords1), 'gm'), css: 'keyword' },
{ regex: /\s*(do|let|yield|return)*\!/gm, css: 'keyword' },
{ regex: new RegExp(this.getKeywords(modules), 'gm'), css: 'keyword' },
{ regex: new RegExp(this.getKeywords(functions), 'gm'), css: 'functions' },
{ regex: new RegExp(this.getKeywords(objectTransformations), 'gm'), css: 'keyword' },
{ regex: new RegExp(this.getKeywords(exceptions), 'gm'), css: 'keyword' },
{ regex: new RegExp(this.getKeywords(constants), 'gm'), css: 'constants' },
{ regex: new RegExp(this.getKeywords(datatypes), 'gm'), css: 'keyword' }
];
this.forHtmlScript(SyntaxHighlighter.regexLib.aspScriptTags);
};
SyntaxHighlighter.brushes.FSharp.prototype = new SyntaxHighlighter.Highlighter();
SyntaxHighlighter.brushes.FSharp.aliases = ['f#', 'f-sharp', 'fsharp'];
SyntaxHighlighter.config.bloggerMode = true;
SyntaxHighlighter.defaults['toolbar'] = false;
SyntaxHighlighter.all();
//]]>
</script>
<hr />
<h2>
All posts in this series:</h2>
<ul>
<li>
<a href="http://andrewtweddle.blogspot.com/2012/12/one-potato-two-potato.html">One Potato</a>: A description of the problem
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2012/12/two-potato-basic-equations.html">Two potato</a>: The basic equations
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/01/three-potato-algebraic-solution.html">Three potato</a>: An algebraic formula for the last person left
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/03/four-binary-calculation-method.html">Four</a>: A neat calculation method using binary numbers
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/03/five-potato-verifying-formulae-with-f.html">Five potato</a>: F# functions to check the various formulae
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/05/six-potato-building-calculation-graph.html">Six potato</a>: Using a calculation graph to see the pattern
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/06/seven-potato-generalizing-to-other.html">Seven potato</a>: Generalizing to other intervals
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/06/more-efficient-circle-elimination.html">More</a>: An efficient algorithm for arbitrary intervals
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2014/02/researching-josephus-problem.html">Online research</a>: In which I discover that it is called the Josephus problem
</li>
</ul>
<hr />
<h2>
Introduction</h2>
In the previous posts in the series I derived Mathematical formulae for calculating the last person left in a circle if every second person is asked to leave (continuing until only one person remains).
<br />
<br />
But what if we want to remove every third person from the list, or every eighth person (as in the "One Potato, Two potato" children's game)?
<br />
<br />
In this post and the next I will attempt to generalize the results to arbitrary intervals.
<br />
<br />
<hr />
<h2>
The challenge</h2>
Let $f_k(n)$ be the label of the last person left in a circle of $n$ people when every $k^{\text{th}}$ person is asked to leave.
<br />
<br />
Clearly $f_k(1) = 1$.
<br />
<br />
When the interval is two, the <a href="http://andrewtweddle.blogspot.com/2012/12/two-potato-basic-equations.html">basic equations</a> allowed us to express $f_{2}(2n+b)$ in terms of $f_{2}(n)$. My gut feel is that this same method won't be useful for an interval of $k$, because it will only allow us to express $f_k(kn+d)$ in terms of $f_k((k-1)n)$, not in terms of $f_k(n)$.
<br />
<br />
Instead I'm going to find a way to express $f_k(n)$ in terms of $f_k(n-1)$.
<br />
<br />
Interestingly, the inspiration for doing this comes from the generalization of the F# brute force calculation method presented in the <a href="http://andrewtweddle.blogspot.com/2013/03/five-potato-verifying-formulae-with-f.html">fifth blog post</a> in the series. So not only have the functional algorithms proved useful for checking the Mathematics. They have also led to new Mathematical insights!
<br />
<br />
<hr />
<h2>
Brute force computation</h2>
So to recap, let's write an F# script which generalizes the brute force calculation method to arbitrary intervals:
<br />
<br />
<pre class="brush: fsharp">let rec getLastLabelInCircleWithIntervalByBruteForce numberToSkip interval circle =
match (numberToSkip, circle) with
| (_, current :: []) -> current
| (0, current :: restOfCircle)
-> getLastLabelInCircleWithIntervalByBruteForce
(interval-1) interval restOfCircle
| (negativeToSkip, _) when negativeToSkip < 0
-> raise (System.ArgumentException("The number to skip can't be negative!"))
| (positiveToSkip, current :: restOfCircle)
-> getLastLabelInCircleWithIntervalByBruteForce
(positiveToSkip-1) interval (restOfCircle @ [current])
| (_,_) -> raise (System.ArgumentException("The circle mustn't be empty!"))
let rec calcLastLeftInCircleWithIntervalByBruteForce interval sizeOfCircle =
getLastLabelInCircleWithIntervalByBruteForce
(interval-1) interval [1 .. sizeOfCircle]
</pre>
<br />
The method recursively skips a person and moves them to the end of the list until it reaches the next person to remove from the circle. It removes that person and starts over with the next person to skip being at the front of the list.
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaL2KzntBylRftWMrhlcsMF8W8lQLBy9GiuvCaMlRkC6g6C0TKtI-tsRgiRTcN0RzAlOETW1-EI2SR-5AUM9xLS03AF8hMvs-WtFKxnyRmK2nKVwnbBXe7raKpBI5gk3EeHrg/s1600/calcLastLeftInCircleWithIntervalByBruteForce.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="451" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaL2KzntBylRftWMrhlcsMF8W8lQLBy9GiuvCaMlRkC6g6C0TKtI-tsRgiRTcN0RzAlOETW1-EI2SR-5AUM9xLS03AF8hMvs-WtFKxnyRmK2nKVwnbBXe7raKpBI5gk3EeHrg/s722/calcLastLeftInCircleWithIntervalByBruteForce.PNG" width="722" /></a></div>
<br />
It runs in a similar time to the previous brute force algorithm.
<br />
<br />
<hr />
<h2>
A recursive formula</h2>
Let's consider what happens when we have $n$ people in a circle, and we remove the next person. Let $m$ be the label of the next person removed from the circle. As with the F# algorithm, after removing person $m$, we will shift the entire circle around so that person $m+1$ is the first person in the list (or person 1 if $m = n$).
<br />
<br />
So after removing person $m$ and shifting everyone around, we are left with $n-1$ people in the circle:
$$
\begin{array}{| l | c | c | c | c | c | c | c |}
\hline
\text{Index: }& 1 & 2 & \ldots & n-m & n-m+1 & \ldots & n-1\\
\text{Label: }& m+1 & m+2 & \ldots & n & 1 & \ldots & m-1\\
\hline
\end{array}
$$
<br />
We will be using the "mod" (modulo) operator to handle the clocking over from position n to position 1. To do this we need to modify the labels to be zero-based:
<br />
<br />
$$
\begin{array}{| l | c | c | c | c | c | c | c |}
\hline
\text{Index: }& 1 & 2 & \ldots & n-m & n-m+1 & \ldots & n-1\\
\text{Label - 1: } & m & m+1 & \ldots & n-1 & 0 & \ldots & m-2\\
\hline
\end{array}
$$
<br />
Note that $n-1 = (n-1) \bmod n$ and $0 = n \bmod n$. This suggests a way of expressing all the labels in a uniform manner:
<br />
<br />
$$
\begin{array}{| l | c | c | c | c | c | c | c |}
\hline
\text{Index: } & 1 & 2 & \ldots & n-m & n-m+1 & \ldots &n-1\\
\text{Label - 1: } & m\bmod n & (m+1)\bmod n & \ldots & (n-1)\bmod n & n\bmod n & \ldots & (n+m-2)\bmod n\\
\hline
\end{array}
$$
<br />
Now is a good time to make the labels one-based again. I'll also rearrange the terms slightly to make the mapping from indexes to labels more obvious:
<br />
<br />
$$
\begin{array}{| l | c | c | c | c |}
\hline
\text{Index: }& 1 & 2 & \ldots & n-1\\
\text{Label:} & [1+(m-1)]\bmod n + 1 & [2+(m-1)]\bmod n + 1 & \ldots & [(n-1) + (m-1)]\bmod n + 1\\
\hline
\end{array}
$$
<br />
This gives us a way of mapping from indexes to labels. The $i^{\text{th}}$ label in the list is $[i+(m-1)] \bmod n + 1$.
<br />
<br />
There are now $n-1$ people in the circle. So the <b>index</b> of the last person left will be $f_k(n-1)$. Hence the <b>label</b> of the last person left will be $[f_k(n-1)+m-1]\bmod n + 1$. Hence:
<br />
<br />
$$
f_k(n) = [f_k(n-1)+m-1]\bmod n + 1 \tag{1}
$$
<br />
$m$ is the label of the first person removed from the circle of n people. How do we determine $m$?
<br />
<br />
If $k \le n$ then $m = k$. But what if $k > n$? In this case the circle will be circumnavigated one or more times before the $k^{\text{th}}$ person is eliminated.
<br />
<br />
We can use the modulo operator to determine the value of m, remembering that we must first zero-base the label, then apply the modulo operator, then add back 1 to the label at the end. So:
<br />
<br />
$$
\begin{align*}
m = & (k-1) \bmod n + 1 & \text{for }k > n \\
m = & k & \text{for } k \le n
\end{align*}
$$
<br />
<br />
But these two equations can be collapsed into one, because when $k \le n$ then $k-1 < n$, so $k = (k-1) \bmod n + 1$. So this gives:
$$
m = (k-1) \bmod n + 1 \tag{2}
$$
<br />
Substituting into equation 1, we get the following:
<br />
<br />
$$
\begin{align*}
f_k(n) &= [f_k(n-1) + ((k - 1) \bmod n + 1) - 1] \bmod n + 1 \\
&= [f_k(n-1) + (k - 1) \bmod n ] \bmod n + 1 \tag{3} \\
\text{So: }\\
f_k(n) &= [f_k(n-1) + k - 1 ] \bmod n + 1 \tag{4}
\end{align*}
$$
<br />
As promised, we have a recursive formula for $f_k(n)$ in terms of $f_k(n-1)$.
<br />
<br />
<hr />
<h2>
F# code for the recursive formula</h2>
The following F# function implements the recursive formula:
<br />
<pre class="brush: fsharp">let rec calcLastLeftInCircleWithIntervalByRecursiveFormula interval sizeOfCircle =
match sizeOfCircle with
| 1 -> 1
| n when n > 1
-> ( calcLastLeftInCircleWithIntervalByRecursiveFormula
interval (sizeOfCircle-1)
+ interval - 1
) % sizeOfCircle
+ 1
| _ -> raise (System.ArgumentException("The size of the circle must be positive!"))
</pre>
<br />
This function runs much faster than the brute force approach. However it has a significant flaw. On my machine, a stack overflow occurs at some value of $n$ between 60 000 and 70 000. This is not really surprising, since the size of the stack is O(n) and the recursive call is not tail recursive.
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqJxA156j6I-CpyWmNbeyb-coNgl7mLHCCqSCLgzEnMyWVo3lAtivakgdSJP-xYTrWMu0twpdc-rzdHk_pD5xWrMW0BkwWjHUTvDP5HMf0Occ7N3VeVvZ01obDcAjY0AHfcg4/s1600/calcLastLeftInCircleWithIntervalByRecursiveFormula_WithStackOverflow_02.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="504" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqJxA156j6I-CpyWmNbeyb-coNgl7mLHCCqSCLgzEnMyWVo3lAtivakgdSJP-xYTrWMu0twpdc-rzdHk_pD5xWrMW0BkwWjHUTvDP5HMf0Occ7N3VeVvZ01obDcAjY0AHfcg4/s702/calcLastLeftInCircleWithIntervalByRecursiveFormula_WithStackOverflow_02.PNG" width="702" /></a></div>
<br />
By contrast, the recursive formula from <a href="http://andrewtweddle.blogspot.com/2013/03/five-potato-verifying-formulae-with-f.html">the fifth post in the series</a> was O(log n) in the stack size, since it expressed $f_{2}(2n+b)$ in terms of $f_{2}(n)$.
<br />
<br />
<hr />
<h2>Next time</h2>
My next blog post will be a continuation of this topic.
<br />
<br />
I will be deriving a more efficient algorithm which has O(log n) recursive calls rather than O(n). This will not only solve the stack overflow problem. It will also make the algorithm incredibly fast.
<br />
<br />
<hr />
Andrew Tweddlehttp://www.blogger.com/profile/02458479910895055369noreply@blogger.com0tag:blogger.com,1999:blog-10106192.post-43163361349664534202013-05-11T02:35:00.000-04:002014-02-08T10:23:17.981-05:00Six potato (building a calculation graph)<!-- From http://irrep.blogspot.com/2011/07/mathjax-in-blogger-ii.html: -->
<script src="http://cdn.mathjax.org/mathjax/latest/MathJax.js" type="text/javascript">
MathJax.Hub.Config({
HTML: ["input/TeX","output/HTML-CSS"],
TeX: { extensions: ["AMSmath.js","AMSsymbols.js"],
equationNumbers: { autoNumber: "AMS" } },
extensions: ["tex2jax.js"],
jax: ["input/TeX","output/HTML-CSS"],
tex2jax: { inlineMath: [ ['$','$'], ["\\(","\\)"] ],
displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
processEscapes: true },
"HTML-CSS": { availableFonts: ["TeX"],
linebreaks: { automatic: true } }
});
</script>
<hr />
<h2>
All posts in this series:</h2>
<ul>
<li>
<a href="http://andrewtweddle.blogspot.com/2012/12/one-potato-two-potato.html">One Potato</a>: A description of the problem
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2012/12/two-potato-basic-equations.html">Two potato</a>: The basic equations
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/01/three-potato-algebraic-solution.html">Three potato</a>: An algebraic formula for the last person left
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/03/four-binary-calculation-method.html">Four</a>: A neat calculation method using binary numbers
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/03/five-potato-verifying-formulae-with-f.html">Five potato</a>: F# functions to check the various formulae
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/05/six-potato-building-calculation-graph.html">Six potato</a>: Using a calculation graph to see the pattern
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/06/seven-potato-generalizing-to-other.html">Seven potato</a>: Generalizing to other intervals
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/06/more-efficient-circle-elimination.html">More</a>: An efficient algorithm for arbitrary intervals
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2014/02/researching-josephus-problem.html">Online research</a>: In which I discover that it is called the Josephus problem
</li>
</ul>
<hr />
<h2>Introduction</h2>
In previous posts in the series I derived Mathematical formulae (and provided F# scripts) for calculating the last person left in a circle if every second person is asked to leave (continuing until only one person remains).
<br />
<br />
In the <a href="http://andrewtweddle.blogspot.com/2013/01/three-potato-algebraic-solution.html">third post</a> in the series I developed an algebraic formula. However algebra often fails to provide insight into the solution. In this post I'd like to provide that insight by taking a very different approach to the problem.
<br />
<br />
<hr />
<h2>Finding a pattern</h2>
In <a href="http://andrewtweddle.blogspot.com/2012/12/two-potato-basic-equations.html">the second post</a> in the series I derived the basic equations needed to solve the problem:
<br />
<br />
$$
\begin{align*}
f(1) &= 1 \tag{1} \\
f(2n) &= 2.f(n) - 1 \tag{2} \\
f(2n+1) &= 2.f(n) + 1 \tag{3}
\end{align*}
$$
<br />
<br />
Notice how equations 2 and 3 both reference $f(n)$. So knowing $f(n)$ allows you to generate two other numbers: $f(2n)$ and $f(2n+1)$.
<br />
<br />
Let's generate a graph showing how the values are determined:
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3gETz3hmRJ9ElkjlirxEwn4CymhIqFx99DMCHSrmistciGOCHYtbSKrVFbF_Rfwd5N7dTjuIQveOTYEAKlT9sLfUTDkgCvfESJe4sVEZ2Q0nVdEZv_lU5nQI_TkTUtj4n3jE/s1600/CirclesGraph_3Levels.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="304" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3gETz3hmRJ9ElkjlirxEwn4CymhIqFx99DMCHSrmistciGOCHYtbSKrVFbF_Rfwd5N7dTjuIQveOTYEAKlT9sLfUTDkgCvfESJe4sVEZ2Q0nVdEZv_lU5nQI_TkTUtj4n3jE/s509/CirclesGraph_3Levels.png" width="509" /></a></div>
<br />
<br />
There seems to be a pattern here. The output in the first row is 1. The outputs in the second row are 1, 3. The outputs in the 3rd row are 1, 3, 5, 7. It gets a bit harder to see, but if we expand another level down we get 1, 3, 5, 7, 9, 11, 13, 15:
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZ_MuQbo5hV9DNTi81H3yVC_JP9zsvgll5XbKbe1ExcQEBZaMjnhMTiRcD6zLJ-usLmHnGA673wdp6Pjd_wgfuRQINKb2YNf2EUunnAXaHhaq4oLUk0NtfMy10q90NBqAQbfQ/s1600/CirclesGraph.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="212" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZ_MuQbo5hV9DNTi81H3yVC_JP9zsvgll5XbKbe1ExcQEBZaMjnhMTiRcD6zLJ-usLmHnGA673wdp6Pjd_wgfuRQINKb2YNf2EUunnAXaHhaq4oLUk0NtfMy10q90NBqAQbfQ/s516/CirclesGraph.png" width="516" /></a></div>
<br />
<br />
So the inputs in row r go from $2^{r-1}$ to $2^{r}-1$ in increments of 1.
<br />
<br />
And the outputs in row r seem to go from 1 to $2^{r} - 1$ in increments of 2.
<br />
<br />
<hr />
<h2>
Insight</h2>
A bit further on I will prove this pattern using the principle of induction. But first let's look for the insight into why this pattern is so.
<br />
<br />
There are two things to show:
<br />
<ul>
<li>The output of the first number in each row must always be 1</li>
<li>Consecutive outputs in a row always differ by 2</li>
</ul>
<hr />
Why is the output of the first number in a row always 1? In other words why does $f(2^{m}) = 1$?
<br />
<br />
If the size of the circle is a power of 2, then exactly half the people will be removed during one trip around the circle. And the last person removed will be the person just before person 1. So person 1 is not going to be eliminated in the next round of eliminations either.
<br />
<br />
And since half of a power of 2 is also a power of 2, this pattern is going to repeat itself. So person 1 is never going to be eliminated. So the answer has to be 1.
<br />
<br />
<hr />
Why does each pair of successive nodes in a row always differ by 2?
<br />
<br />
Subtract equation 2 from equation 3 in the basic equations. The $f(n)$ terms cancel out, showing that:
$$
f(2n+1) - f(2n) = [2.f(n) + 1] - [2.f(n) - 1] = 2
$$
<br />
<hr />
But we also need to consider the other way of pairing successive terms (i.e. an odd followed by an even argument).
<br />
<br />
In other words, why is $f(2n+2) - f(2n+1) = 2$ for all n, except when $2n+2$ is a power of 2?
<br />
<br />
We will prove this by induction. Suppose $f(n+1) - f(n) = 2$ in the previous row. Then:
<br />
<br />
$$
\begin{align*}
f(2n+2) &= f(2(n+1)) \\
&= 2.f(n+1) - 1 & \text{by (2)} \\
\\
\text{So: }f(2n+2) - f(2n + 1) & = [2.f(n+1) - 1] - [2.f(n) + 1] & \text{by (3)} \\
&= 2.[f(n+1) - f(n)] - 2 \\
&= 2.[2] - 2 \\
&= 2
\end{align*}
$$
<br />
Look at the second row in the graph: $f(3) - f(2) = 2$. Starting at this row, the property of successive outputs differing by 2 is going to ripple down from row to row in the graph.
<br />
<br />
Now let's prove this formally. We'll do it by induction again, but this time we'll use the row number in the graph as the induction step (or more correctly the row number minus 1).
<br />
<br />
<hr />
<h2>
Proof by induction</h2>
<h3>The inductive step</h3>
Suppose that for some integer k, the numbers from $2^k$ to $2^{k+1} - 1$ satisfy:
<br/>
<br />
$f(2^{k} + i) = 2i + 1 \tag{4}$.
<br/>
Then we wish to show that this holds true for k+1 as well. In other words, the numbers from $2^{k+1}$ to $2^{k+2} - 1$ should satisfy $f(2^{k+1} + j) = 2j + 1$.
<br/>
<br/>
To prove this we consider the two cases of odd and even values of j separately:
<br />
<br />
<u>Proof for even values:</u>
<br />
<br />
Let $j = 2i$ for any $i \in \left\{ 0, 1, ..., 2^{k}-1 \right\}$.
<br />
<br />
First note that j will have values in the following range:
<br />
<br />
$ j \in \left\{0, 2, ..., 2^{k+1} - 2 \right\} \tag{5}$
<br />
<br />
Then:
<br />
<br />
$$
\begin{align*}
f(2^{k+1} + j) &= f(2.2^{k} + 2i) \\
&= f(2[2^{k} + i]) \\
&= 2.f(2^{k} + i) - 1 & \text{by (2)} \\
&= 2.( 2i + 1 ) - 1 & \text{by (4)} \\
&= 2.( j + 1 ) - 1 \\
&= 2j + 1
\end{align*}
$$
<u>Derivation for odd values:</u>
<br />
<br />
Let $j = 2i + 1$ for any $i \in \left\{ 0, 1, ..., 2^{k}-1 \right\}$.
<br />
<br />
Note that j will have values in the following range:
<br />
<br />
$ j \in \left\{1, 3, ..., 2^{k+1} - 1 \right\} \tag{6}$
<br />
<br />
Then:
<br />
<br />
$$
\begin{align*}
f(2^{k+1} + j) &= f(2.2^{k} + 2i + 1) \\
&= f(2[2^{k} + i] + 1) \\
&= 2.f(2^{k} + i) + 1 & \text{by (3)} \\
&= 2.( 2i + 1 ) + 1 & \text{by (4)} \\
&= 2.( j ) + 1 \\
&= 2j + 1
\end{align*}
$$
<br />
<br />
So combining these two cases, we see that $f(2^{k+1} + j) = 2j + 1$ for all $j \in \left\{ 0, 1, ..., 2^{k+1}-1 \right\}$, from (5) and (6).
<br />
<br />
<h3>The base case</h3>
When k = 0:
<br />
<br />
$f(2^{0}) = f(1) = 1 = 2 . 0 + 1$
<br />
<br />
So when k = 0 and $i \in \left\{0, ..., 2^{k} - 1\right\} = \left\{0\right\}$, $f(2^{k} + i) = 2i + 1$
<br />
<br />
<h3>Conclusion</h3>
So this is our proof by induction of the following:
<br/>
<br/>
For all non-negative integers $k$ and $i$, with $i \in \left\{ 0, 1, ..., 2^{k}-1 \right\}$:
<br />
<br />
$f(2^{k} + i) = 2i + 1$
<br />
<br />
But for $n = 2^{k} + i$ this gives:
$$
\begin{align*}
f(n) &= 2i + 1 \\
&= 2.[i + 2^{k} - 2^{k}] + 1 \\
&= 2.[2^{k} + i] + 1 - 2^{k+1} \\
&= 2n + 1 - 2^{k+1} \\
&= 2n + 1 - 2^{{\lfloor log_2 n \rfloor} + 1}
\end{align*}
$$
And that is the same as the equation derived in the <a href="http://andrewtweddle.blogspot.com/2013/01/three-potato-algebraic-solution.html">third post of the series</a>.
<br />
<br />
<hr />
<h2>Next time</h2>
In the next blog post in the series I will try to generalize the result to intervals other than 2.
<br/>
<br/>
<hr/>
Andrew Tweddlehttp://www.blogger.com/profile/02458479910895055369noreply@blogger.com0tag:blogger.com,1999:blog-10106192.post-2068159122395980572013-03-24T17:34:00.000-04:002014-02-08T10:23:48.264-05:00Five potato (verifying the formulae with F#)<!-- From http://irrep.blogspot.com/2011/07/mathjax-in-blogger-ii.html: -->
<script src='http://cdn.mathjax.org/mathjax/latest/MathJax.js' type='text/javascript'>
MathJax.Hub.Config({
HTML: ["input/TeX","output/HTML-CSS"],
TeX: { extensions: ["AMSmath.js","AMSsymbols.js"],
equationNumbers: { autoNumber: "AMS" } },
extensions: ["tex2jax.js"],
jax: ["input/TeX","output/HTML-CSS"],
tex2jax: { inlineMath: [ ['$','$'], ["\\(","\\)"] ],
displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
processEscapes: true },
"HTML-CSS": { availableFonts: ["TeX"],
linebreaks: { automatic: true } }
});
</script>
<!-- Alex Gorbatchev syntax highlighter hosted on AWS (donation given) -->
<link href='http://alexgorbatchev.com/pub/sh/current/styles/shCore.css' rel='stylesheet' type='text/css'/>
<link href="http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css" rel="stylesheet" type="text/css"></link>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js" type="text/javascript"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shAutoloader.js" type="text/javascript"></script>
<!-- F# syntax highlighting from http://tinesware.blogspot.com/2008/12/syntax-highlighting.html -->
<!-- Wrapping in a CDATA is a trick obtained from http://tinesware.blogspot.com/2008/12/syntax-highlighting.html -->
<script type="text/javascript">//<![CDATA[
/**
* SyntaxHighlighter
* http://alexgorbatchev.com/
*
* SyntaxHighlighter is donationware. If you are using it, please donate.
* http://alexgorbatchev.com/wiki/SyntaxHighlighter:Donate
*
* @version
* 2.0.320 (May 03 2009)
*
* @copyright
* Copyright (C) 2004-2009 Alex Gorbatchev.
*
* @license
* This file is part of SyntaxHighlighter.
*
* SyntaxHighlighter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SyntaxHighlighter is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with SyntaxHighlighter. If not, see <http://www.gnu.org/copyleft/lesser.html>.
*/
/* Syntax contributed by Julien O., 2009-10-05 */
SyntaxHighlighter.brushes.FSharp = function() {
/* main F# keywords */
var keywords1 =
/* section 3.4 */
'abstract and as assert base begin class default delegate do done ' +
'downcast downto elif else end exception extern false finally for ' +
'fun function if in inherit inline interface internal lazy let ' +
'match member module mutable namespace new null of open or ' +
'override private public rec return sig static struct then to ' +
'true try type upcast use val void when while with yield ' +
'asr land lor lsl lsr lxor mod ' +
/* identifiers are reserved for future use by F# */
'atomic break checked component const constraint constructor ' +
'continue eager fixed fori functor global include method mixin ' +
'object parallel params process protected pure sealed tailcall ' +
'trait virtual volatile ' ;
/* do, return, let, yield keywords with ! at the end are added later */
/* define names of main libraries in F# Core so we can link to it
http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/manual/namespaces.html
*/
var modules =
'Array Array2D Array3D Array4D ComparisonIdentity HashIdentity List ' +
'Map Seq SequenceExpressionHelpers Set CommonExtensions Event ' +
'ExtraTopLevelOperators LanguagePrimitives NumericLiterals Operators ' +
'OptimizedClosures Option String NativePtr Printf' ;
/* 17.2 & 17.3 */
var functions =
'abs acos asin atan atan2 ceil cos cosh exp ' +
'floor log log10 pown round sign sin sinh sqrt tan tanh ' +
'fst snd KeyValue not min max ' +
'ignore stdin stdout stderr ' ;
/* 17.2 Object Transformation Operators */
var objectTransformations =
'box hash sizeof typeof typedefof unbox'
/* 17.2 Exceptions */
var exceptions =
'failwith invalidArg raise rethrow' ;
/* 3.11 Pre-processor Declarations / Identifier Replacements*/
var constants =
'__SOURCE_DIRECTORY__ __SOURCE_FILE__ __LINE__';
/* Pervasives Types & Overloaded Conversion Functions */
var datatypes =
'bool byref byte char decimal double exn float float32 ' +
'FuncConvert ilsigptr int int16 int32 int64 int8 ' +
'nativeint nativeptr obj option ref sbyte single string uint16 ' +
'uint32 uint64 uint8 unativeint unit enum async seq dict ' ;
function fixComments(match, regexInfo) {
var css = (match[0].indexOf("///") == 0) ? 'color1' : 'comments';
return [new SyntaxHighlighter.Match(match[0], match.index, css)];
}
this.regexList = [
/* 3.3 Conditional compilation & 13.3 Compiler Directives + light /light off*/
{ regex: /\s*#\b(light|if|else|endif|indent|nowarn|r(eference)?|I|include|load|time|help|q(uit)?)/gm, css: 'preprocessor' },
{ regex: SyntaxHighlighter.regexLib.singleLineCComments, css:'comments' },
{ regex: SyntaxHighlighter.regexLib.multiLineCComments, css: 'comments' },
{ regex: /\s*\(\*[\s\S]*?\*\)/gm, css: 'comments' },
{ regex: SyntaxHighlighter.regexLib.doubleQuotedString, css: 'string' },
{ regex: /'[^']?'/gm, css: 'string' },
{ regex: new RegExp(this.getKeywords(keywords1), 'gm'), css: 'keyword' },
{ regex: /\s*(do|let|yield|return)*\!/gm, css: 'keyword' },
{ regex: new RegExp(this.getKeywords(modules), 'gm'), css: 'keyword' },
{ regex: new RegExp(this.getKeywords(functions), 'gm'), css: 'functions' },
{ regex: new RegExp(this.getKeywords(objectTransformations), 'gm'), css: 'keyword' },
{ regex: new RegExp(this.getKeywords(exceptions), 'gm'), css: 'keyword' },
{ regex: new RegExp(this.getKeywords(constants), 'gm'), css: 'constants' },
{ regex: new RegExp(this.getKeywords(datatypes), 'gm'), css: 'keyword' }
];
this.forHtmlScript(SyntaxHighlighter.regexLib.aspScriptTags);
};
SyntaxHighlighter.brushes.FSharp.prototype = new SyntaxHighlighter.Highlighter();
SyntaxHighlighter.brushes.FSharp.aliases = ['f#', 'f-sharp', 'fsharp'];
SyntaxHighlighter.config.bloggerMode = true;
SyntaxHighlighter.defaults['toolbar'] = false;
SyntaxHighlighter.all();
//]]>
</script>
<hr />
<h2>
All posts in this series:</h2>
<ul>
<li>
<a href="http://andrewtweddle.blogspot.com/2012/12/one-potato-two-potato.html">One Potato</a>: A description of the problem
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2012/12/two-potato-basic-equations.html">Two potato</a>: The basic equations
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/01/three-potato-algebraic-solution.html">Three potato</a>: An algebraic formula for the last person left
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/03/four-binary-calculation-method.html">Four</a>: A neat calculation method using binary numbers
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/03/five-potato-verifying-formulae-with-f.html">Five potato</a>: F# functions to check the various formulae
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/05/six-potato-building-calculation-graph.html">Six potato</a>: Using a calculation graph to see the pattern
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/06/seven-potato-generalizing-to-other.html">Seven potato</a>: Generalizing to other intervals
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/06/more-efficient-circle-elimination.html">More</a>: An efficient algorithm for arbitrary intervals
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2014/02/researching-josephus-problem.html">Online research</a>: In which I discover that it is called the Josephus problem
</li>
</ul>
<hr />
<h2>
Introduction</h2>
In the first four posts in the series I derived Mathematical formulae for calculating the last person left in a circle if every second person is asked to leave (continuing until only one person remains).
<br />
<br />
In this post I am going to write F# scripts to check these formulae.
<br />
<br />
Why F#?
<br />
<ul>
<li>Functional programming languages often express Mathematical concepts very elegantly</li>
<li>I felt like learning a functional language (though I had originally planned to learn Haskell)</li>
<li>I opened the F# interactive console on a whim and the solutions were flowing before I had time to change my mind!</li>
</ul>
<br />
I'm very happy with how natural it felt to use F#. And it does build on my knowledge of .Net. So I don't regret my somewhat accidental choice of F# instead of Haskell.
<br />
<br />
<hr />
<h2>
Brute force computation</h2>
The following F# functions use a brute force approach to calculate the number of the last person left in the circle:
<br />
<br />
<pre class="brush: fsharp">let rec getLastLabelInCircleByBruteForce circle =
match circle with
| onlyOneLeft :: [] -> onlyOneLeft
| nextToSkip :: nextOut :: restOfCircle
-> getLastLabelInCircleByBruteForce (restOfCircle @ [nextToSkip])
| _ -> raise (System.ArgumentException("The circle mustn't be empty!"))
let calcLastLeftInCircleByBruteForce sizeOfCircle =
getLastLabelInCircleByBruteForce [1..sizeOfCircle]
</pre>
<br />
Here's a short explanation of the F# syntax...
<br />
<ul>
<li>
"let rec <functionName> <parameter> =" defines a recursive function taking a single parameter.
</li>
<li>
F# is strongly typed. It uses <a href="http://en.wikipedia.org/wiki/Hindley%E2%80%93Milner">Hindley-Milner type inference</a> to deduce parameter types.
</li>
<li>"match circle with" does pattern matching on circle.</li>
<li>Each pipe ("|") is the start of a pattern. Whatever comes after "->" is the result if the pattern is the first to match.</li>
</ul>
<br />
You will also need to know a few things about lists in F#. I recommend reading <a href="http://en.wikibooks.org/wiki/F_Sharp_Programming/Lists">this wikibooks article</a> or <a href="http://blogs.msdn.com/b/chrsmith/archive/2008/07/10/mastering-f-lists.aspx">this article</a> by Microsoft's Chris Smith.
<br />
<br />
Note the following...
<br />
<ul>
<li>
F# lists are linked lists, not the .Net List class.
</li>
<li>
"head::tail" is a list comprising an <u>element</u> called head pointing to a <u>sub-list</u> called tail.
</li>
<li>
"[]" is an empty list.
</li>
<li>
So "onlyOneLeft :: []" matches a list with a single item (the answer).
</li>
<li>
"nextToSkip :: nextOut :: restOfCircle" matches the first two items in the list. restOfCircle is the remaining people in the circle (which could be the empty list).
</li>
<li>
"_" is a placeholder which will match anything.
</li>
<li>"list1 @ list2" concatenates two lists.</li>
<li>"[1..N]" creates a list with the numbers 1, 2, 3, ... N.
</li>
</ul>
<br />
So the algorithm repeatedly moves the first person to the end of the list and skips the second person. It does this recursively until a single person is left.
If you like, we are rotating the circle towards us, instead of moving around the circle (which would involve unnecessary complexity, such as keeping track of our current position and clocking over correctly when we reached the end of the list).
<br />
<br />
Let's run the fsi application and calculate the results for circles with 10 and 10,000 people (with timing switched on):<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheweNuSJ1T1XjmuVbkCoWVLjm7R7kgqHHfTUlCOrzSPMwECDP4ePcrZhmnNiXZO7NU59gPNKBhylbwJoCDQIf7P3WC9cj1-lN7Nk7xmXwRxn_HhH3gzWVlHIjSqVD5Jr4PwG4/s1600/calcLastLeftInCircleByBruteForceWithTiming.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="436" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheweNuSJ1T1XjmuVbkCoWVLjm7R7kgqHHfTUlCOrzSPMwECDP4ePcrZhmnNiXZO7NU59gPNKBhylbwJoCDQIf7P3WC9cj1-lN7Nk7xmXwRxn_HhH3gzWVlHIjSqVD5Jr4PwG4/s655/calcLastLeftInCircleByBruteForceWithTiming.PNG" width="655" /></a></div>
<br />
This appears to be quite slow. It took around 0.9 seconds to run the calculation for a circle with 10,000 people.
<br />
<br />
This is partly because "(restOfCircle @ [nextToSkip])" is an O(n) operation, where n is the size of restOfCircle.
<br />
<br />
<hr />
<h2>
Recursive computation</h2>
In <a href="http://andrewtweddle.blogspot.com/2012/12/two-potato-basic-equations.html">the second post</a> in the series I derived the basic equations needed to solve the problem:
<br />
<br />
$$
\begin{align*}
f(1) &= 1 \tag{1} \\
f(2n) &= 2.f(n) - 1 \tag{2} \\
f(2n+1) &= 2.f(n) + 1 \tag{3}
\end{align*}
$$
<br />
<br />
My second F# script uses these equations to calculate the solution:
<br />
<br />
<pre class="brush: fsharp">let rec calcLastLeftInCircleByHalving sizeOfCircle =
let (div, rem) = (sizeOfCircle / 2, sizeOfCircle % 2)
match (div,rem) with
| (0,1) -> 1
| (n,0) -> 2 * (calcLastLeftInCircleByHalving n) - 1
| (n,1) -> 2 * (calcLastLeftInCircleByHalving n) + 1
| (_,_) -> 0 // to suppress compiler warnings
</pre>
<br />
Notice how closely the first 3 patterns correspond to the 3 formulae.
<br />
<br />
The code also runs much faster than the brute force approach. So much faster that it calculated <b>all</b> the numbers up to a million in 0.629 seconds!
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjc99zOCvkhR2YAEk21UQ5L4XzeSv4NIOd3JOFACbwRM1f5tYqxnb5qZ7mlwO1YuWyfZb1s-Q4gMj5WL5tQrRNVWEzvr3yh1bAdRwUvSpVn1rkhGdRJdAGp_VkuQBqPBHvvr44/s1600/calcLastLeftInCircleByHalving.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="442" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjc99zOCvkhR2YAEk21UQ5L4XzeSv4NIOd3JOFACbwRM1f5tYqxnb5qZ7mlwO1YuWyfZb1s-Q4gMj5WL5tQrRNVWEzvr3yh1bAdRwUvSpVn1rkhGdRJdAGp_VkuQBqPBHvvr44/s665/calcLastLeftInCircleByHalving.PNG" width="665" /></a>
</div>
<br />
<br />
<hr />
<h2>
Computation via the algebraic formula</h2>
In my <a href="http://andrewtweddle.blogspot.com/2013/01/three-potato-algebraic-solution.html">third post</a> of the series I derived an algebraic formula for the last person left in the circle:
$$
f(n) = 2n + 1 - 2^{{\lfloor log_2 n \rfloor} + 1} \tag{9}
$$
In F# this becomes:
<br />
<br />
<pre class="brush: fsharp">let calcLastLeftInCircleByFormula n =
let floorOfLog = System.Math.Floor(System.Math.Log( double n, 2.0) )
2 * n + 1 - int ( 2.0 ** (floorOfLog + 1.0))
</pre>
<br />
<br />
Note that System.Math is the static class in the .Net framework for Mathematical calculations. Its Log() method has an overload which takes the base of the logarithm as its second parameter.
<br />
<br />
Running this for all the numbers up to a million takes 0.543 seconds - slightly quicker than the previous method...
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYKG85SRjpeTqCm-BdnUuiXuHm7eHRSy2CPLvlm8hb7SB68MASBtN_8h1b6mgpOgyblZCtrLpRp2aQQX7uQLXfihJsok0fV2l4Bc8cTh1aABx_cbEm0Na9UI4yy81jLCM-DXA/s1600/calcLastLeftInCircleByFormula.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="393" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYKG85SRjpeTqCm-BdnUuiXuHm7eHRSy2CPLvlm8hb7SB68MASBtN_8h1b6mgpOgyblZCtrLpRp2aQQX7uQLXfihJsok0fV2l4Bc8cTh1aABx_cbEm0Na9UI4yy81jLCM-DXA/s661/calcLastLeftInCircleByFormula.PNG" width="661" /></a></div>
<br />
<br />
Two things make me uncomfortable with this approach:
<br />
<ul>
<li>It feels inefficient to be doing floating point calculations to solve an integer problem.</li>
<li>I'm concerned that rounding errors could cause a calculation error</li>
</ul>
The rounding error concern is easily addressed. Simply add 0.5 to the value inside the int(...) conversion. But perhaps we should be avoiding floating point calculations altogether...
<br />
<br />
<hr />
<h2>
Computation via binary arithmetic</h2>
In <a href="http://andrewtweddle.blogspot.com/2013/03/four-binary-calculation-method.html">the fourth post in the series</a> I presented a very elegant rule for calculating the answer using binary representations.
<br />
<br />
This method can be re-stated (for computational convenience) as follows:
<ul>
<li>take the binary representation of the number</li>
<li>drop the leading digit</li>
<li>shift left</li>
<li>add 1</li>
</ul>
<br/>
Let's convert that to F#:
<br/>
<br/>
First we need to find the leading digit, so we can drop it. This is equivalent to finding the highest power of 2 that is less than (or equal to) the number. In other words it's the term $2^{\lfloor log_2 n \rfloor}$ in the algebraic formula.
<br/>
<br/>
This is the same as finding the left-most bit in the binary representation of the number and setting all remaining bits to zero.
<br />
<br />
We can do this easily by repeatedly shifting the number to the right (the final bit drops out) and simultaneously shifting the number 1 to the left each time (this multiplies by 2, giving the next higher power of 2). Repeat until the original number is 1, and the other number will be the power of 2 we are looking for.
<br />
<br />
In F# the ">>>" operator does a bitwise right shift, and "<<<" does a left shift. So the following pair of F# functions calculate $2^{\lfloor log_2 n \rfloor}$:
<br />
<br />
<pre class="brush: fsharp">
let rec calcFirstBinaryDigit powerOf2 shiftedNumber =
match shiftedNumber with
| 0 -> raise (System.ArgumentException("No power of 2 is less than zero"))
| 1 -> powerOf2
| _ -> calcFirstBinaryDigit (powerOf2 <<< 1) (shiftedNumber >>> 1)
let calcHighestPowerOfTwoNotGreaterThan = calcFirstBinaryDigit 1
</pre>
<br />
With this building block in place, we can easily express the binary calculation method as follows:
<br />
<br />
<pre class="brush: fsharp">
let calcLastLeftInCircleByBinaryFormula n =
((n - calcHighestPowerOfTwoNotGreaterThan n) <<< 1) + 1
</pre>
<br />
<br />
The binary formula took 0.465 seconds, which is marginally faster than the floating point formula:
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4IOIqiFonC841BdgQTFSf9-F2PuR3lcPr0U6u9IMqdXH_YEi7fp0qQJyDsSi9WuBZGaizy-h5zr042FBurhWqNDr1vdbGuInVNOTLGUafYPsxj0h7SiEZP3rUNtZslDO0Pms/s1600/calcLastLeftInCircleByBinaryFormula.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="506" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4IOIqiFonC841BdgQTFSf9-F2PuR3lcPr0U6u9IMqdXH_YEi7fp0qQJyDsSi9WuBZGaizy-h5zr042FBurhWqNDr1vdbGuInVNOTLGUafYPsxj0h7SiEZP3rUNtZslDO0Pms/s676/calcLastLeftInCircleByBinaryFormula.PNG" width="676" /></a></div>
<br />
<br />
<hr />
<h2>
Comparing the results of the various methods</h2>
Now let's return to the original purpose of writing these various F# scripts, which was to check the Mathematics.
<br />
<br />
First I'm going to create a data structure to hold all the answers from the various methods for a single size of circle...
<br />
<br />
<pre class="brush: fsharp">
type circleCalculation = {
SizeOfCircle: int;
LastLeftInCircleByBruteForce: int;
LastLeftInCircleByHalving: int;
LastLeftInCircleByFormula: int;
LastLeftInCircleByBinaryFormula: int
}
</pre>
<br />
<br />
Then I'm going to create a function to perform the calculation of a single record. I want this function to have a parameter allowing me to switch off the brute force method if I want to, since I know it is much slower than the other methods:
<br />
<br />
<pre class="brush: fsharp">
let getCircleCalculation includeBruteForce sizeOfCircle =
// Brute force is so slow to calculate. Provide the option to omit it...
let lastLeftInCircleByBruteForce =
match includeBruteForce with
| true -> calcLastLeftInCircleByBruteForce sizeOfCircle
| false -> 0
let lastLeftInCircleByHalving = calcLastLeftInCircleByHalving sizeOfCircle
let lastLeftInCircleByFormula = calcLastLeftInCircleByFormula sizeOfCircle
let lastLeftInCircleByBinaryFormula = calcLastLeftInCircleByBinaryFormula sizeOfCircle
let circleCalc = {
SizeOfCircle = sizeOfCircle;
LastLeftInCircleByBruteForce = lastLeftInCircleByBruteForce;
LastLeftInCircleByHalving = lastLeftInCircleByHalving;
LastLeftInCircleByFormula = lastLeftInCircleByFormula;
LastLeftInCircleByBinaryFormula = lastLeftInCircleByBinaryFormula
}
circleCalc
</pre>
<br />
<br />
Something that really amazed me is that I didn't need to specify the circleCalculation type in the function. F# is able to infer the data type from the usage pattern. Very neat!
<br />
<br />
With this in place, I can use the following script to check that the methods all match up:
<br />
<br />
<pre class="brush: fsharp">
// With brute force:
[1..1000] |> List.map (getCircleCalculation true) |> List.filter (
fun cc -> cc.LastLeftInCircleByBinaryFormula <> cc.LastLeftInCircleByBruteForce
|| cc.LastLeftInCircleByBinaryFormula <> cc.LastLeftInCircleByHalving
|| cc.LastLeftInCircleByBinaryFormula <> cc.LastLeftInCircleByFormula
)
// Without brute force:
[1..1000000] |> List.map (getCircleCalculation false) |> List.filter (
fun cc -> cc.LastLeftInCircleByBinaryFormula <> cc.LastLeftInCircleByHalving
|| cc.LastLeftInCircleByBinaryFormula <> cc.LastLeftInCircleByFormula
)
</pre>
<br />
<br />
Running this shows that all the methods produce the same result. Mission accomplished...
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsUGpYJ8dwWU4jhAMf3QlTJfXSCAYBq3Ba2MKlQogDSUhvJmM79RjdUtei45iJt7GmYb6XxUlkMHGpMNdovsJLqIWDr_T1Nqr7du2pXKSAYQQtjMpX7f06bYTX-JW3_ASxsV0/s1600/CompareCalculations.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="506" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsUGpYJ8dwWU4jhAMf3QlTJfXSCAYBq3Ba2MKlQogDSUhvJmM79RjdUtei45iJt7GmYb6XxUlkMHGpMNdovsJLqIWDr_T1Nqr7du2pXKSAYQQtjMpX7f06bYTX-JW3_ASxsV0/s676/CompareCalculations.PNG" width="676" /></a></div>
<br />
<hr />
<h2>Next time</h2>
In the next blog post in the series, I will show a different way of solving the problem. Instead of using the basic equations to recursively calculate a large number from the formula for smaller numbers, I'm going to work in the opposite direction.
<br/>
<br/>
What will emerge from this is a pattern of answers for successive values of the circle size. You might like to have a look at some of the earlier screen captures to see if you can work out what the pattern is.
<br/>
<br/>
<hr/>
Andrew Tweddlehttp://www.blogger.com/profile/02458479910895055369noreply@blogger.com0tag:blogger.com,1999:blog-10106192.post-55414171181538033182013-03-24T15:59:00.001-04:002014-02-08T10:24:26.788-05:00Four (a binary calculation method)<!-- From http://irrep.blogspot.com/2011/07/mathjax-in-blogger-ii.html: -->
<script src='http://cdn.mathjax.org/mathjax/latest/MathJax.js' type='text/javascript'>
MathJax.Hub.Config({
HTML: ["input/TeX","output/HTML-CSS"],
TeX: { extensions: ["AMSmath.js","AMSsymbols.js"],
equationNumbers: { autoNumber: "AMS" } },
extensions: ["tex2jax.js"],
jax: ["input/TeX","output/HTML-CSS"],
tex2jax: { inlineMath: [ ['$','$'], ["\\(","\\)"] ],
displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
processEscapes: true },
"HTML-CSS": { availableFonts: ["TeX"],
linebreaks: { automatic: true } }
});
</script>
<hr />
<h2>
All posts in this series:</h2>
<ul>
<li>
<a href="http://andrewtweddle.blogspot.com/2012/12/one-potato-two-potato.html">One Potato</a>: A description of the problem
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2012/12/two-potato-basic-equations.html">Two potato</a>: The basic equations
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/01/three-potato-algebraic-solution.html">Three potato</a>: An algebraic formula for the last person left
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/03/four-binary-calculation-method.html">Four</a>: A neat calculation method using binary numbers
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/03/five-potato-verifying-formulae-with-f.html">Five potato</a>: F# functions to check the various formulae
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/05/six-potato-building-calculation-graph.html">Six potato</a>: Using a calculation graph to see the pattern
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/06/seven-potato-generalizing-to-other.html">Seven potato</a>: Generalizing to other intervals
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/06/more-efficient-circle-elimination.html">More</a>: An efficient algorithm for arbitrary intervals
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2014/02/researching-josephus-problem.html">Online research</a>: In which I discover that it is called the Josephus problem
</li>
</ul>
<hr />
<h2>
Introduction</h2>
In my first three posts in the series I derived Mathematical formulae for calculating the last person left in a circle if every second person is asked to leave (continuing until only one person remains).
<br />
<br />
In this post I am going to show a really neat way of calculating the answer. It is based on the binary number representation of the number of people in the circle.
<br />
<br />
If you are unfamiliar with the binary number system, then I suggest looking for a basic tutorial on binary numbers first.
<br />
<br />
<hr />
<h2>
The binary calculation rule</h2>
Take the binary representation of the number of people in the circle. Move the left-most (non-zero) bit to the end. Convert back to decimal and you have the number of the last person left.
<br />
<br />
<hr/>
<h2>Some examples</h2>
$$ f(\underbrace{10}_{\text{decimal}}) = f(\underbrace{1010}_{\text{binary}}) = \underbrace{0101}_{\text{binary}} = \underbrace{5}_{\text{decimal}} $$
$$ f(\underbrace{13}_{\text{decimal}}) = f(\underbrace{1101}_{\text{binary}}) = \underbrace{1011}_{\text{binary}} = \underbrace{11}_{\text{decimal}} $$
<br />
<hr />
<h2>
Derivation</h2>
In my <a href="http://andrewtweddle.blogspot.com/2013/01/three-potato-algebraic-solution.html">third post</a> of the series I derived an algebraic formula for the last person left in the circle:
$$
f(n) = 2n + 1 - 2^{{\lfloor log_2 n \rfloor} + 1} \tag{9}
$$
I'd like to write that formula slightly differently:
$$
f(n) = 2(n - 2^{\lfloor log_2 n \rfloor} ) + 1 \tag{10}
$$
<br />
<br />
$2^{\lfloor log_2 n \rfloor}$ is just the highest power of 2 that is less than (or equal to) the number. Now each position in a binary representation represents a power of 2. So this represents the left-most bit in the binary representation of n (and all other positions zero).
<br />
<br />
Then $n - 2^{\lfloor log_2 n \rfloor}$ is just the original number n with its first (non-zero) binary digit dropped (i.e. replaced with a zero). Let's call this number m.
<br />
<br />
In binary, you can multiply a number by 2 by shifting all its bits one place to the left. So 2m+1 is just a "left shift" of m and set the rightmost bit to 1.
<br />
<br />
So we can express the binary method as:
<ul>
<li>take the binary representation of the number</li>
<li>drop the leading digit</li>
<li>append a 1 (i.e. shift left and add 1)</li>
<li>convert back to decimal</li>
</ul>
<br />
But since we are dropping the leading 1, and appending a trailing 1, you could see this as rotating the first bit around to the end.
<br />
<br />
<hr />
<h2>Next time</h2>
In the next post in the series, I will be writing code to calculate the answer using each of the methods presented in the first four posts.
<br />
<br />
<hr />
Andrew Tweddlehttp://www.blogger.com/profile/02458479910895055369noreply@blogger.com0tag:blogger.com,1999:blog-10106192.post-6828461099895863412013-01-20T08:04:00.000-05:002014-02-08T10:24:58.069-05:00Three potato (an algebraic solution)<!-- From http://irrep.blogspot.com/2011/07/mathjax-in-blogger-ii.html: -->
<script src='http://cdn.mathjax.org/mathjax/latest/MathJax.js' type='text/javascript'>
MathJax.Hub.Config({
HTML: ["input/TeX","output/HTML-CSS"],
TeX: { extensions: ["AMSmath.js","AMSsymbols.js"],
equationNumbers: { autoNumber: "AMS" } },
extensions: ["tex2jax.js"],
jax: ["input/TeX","output/HTML-CSS"],
tex2jax: { inlineMath: [ ['$','$'], ["\\(","\\)"] ],
displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
processEscapes: true },
"HTML-CSS": { availableFonts: ["TeX"],
linebreaks: { automatic: true } }
});
</script>
<hr />
<h2>
All posts in this series:</h2>
<ul>
<li>
<a href="http://andrewtweddle.blogspot.com/2012/12/one-potato-two-potato.html">One Potato</a>: A description of the problem
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2012/12/two-potato-basic-equations.html">Two potato</a>: The basic equations
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/01/three-potato-algebraic-solution.html">Three potato</a>: An algebraic formula for the last person left
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/03/four-binary-calculation-method.html">Four</a>: A neat calculation method using binary numbers
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/03/five-potato-verifying-formulae-with-f.html">Five potato</a>: F# functions to check the various formulae
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/05/six-potato-building-calculation-graph.html">Six potato</a>: Using a calculation graph to see the pattern
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/06/seven-potato-generalizing-to-other.html">Seven potato</a>: Generalizing to other intervals
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/06/more-efficient-circle-elimination.html">More</a>: An efficient algorithm for arbitrary intervals
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2014/02/researching-josephus-problem.html">Online research</a>: In which I discover that it is called the Josephus problem
</li>
</ul>
<hr />
<h2>
Introduction</h2>
In <a href="http://andrewtweddle.blogspot.com/2012/12/one-potato-two-potato.html">my first post</a> of the series I described a situation where a number of people are sitting in a circle. Every second person is asked to leave. This continues until only one person remains. If the people are labelled 1 to N, what is the formula for that final person?
<br />
<br />
In <a href="http://andrewtweddle.blogspot.com/2012/12/two-potato-basic-equations.html">the second post</a> in the series I derived the basic equations needed to solve the problem:
<br />
<br />
$$
\begin{align*}
f(1) &= 1 \tag{1} \\
f(2n) &= 2.f(n) - 1 \tag{2} \\
f(2n+1) &= 2.f(n) + 1 \tag{3}
\end{align*}
$$
<br />
In this post I will use these equations to derive the formula for the label of the last person left.
<br />
<br />
In future posts I will show a more intuitive way of solving the problem (but still starting from these 3 equations).
<br />
<br />
<hr />
<h2>Derivation</h2>
Equations 2 and 3 allow us to calculate the formula for a number in terms of the formula for a smaller number. Starting from any number we can keep on repeating this process until we get the formula in terms of f(1).
<br />
<br />
For example, suppose there are 10 people in the circle. Then:
$$
\begin{align*}
f(10) &= 2.f(5) - 1 & \text{by equation (2)} \\
&= 2.(2.f(2) + 1) - 1 & \text{by equation (3)} \\
&= 2.(2.(2.f(1) - 1) + 1) - 1 & \text{by equation (2)} \\
&= 2.(2.(2.(1) - 1) + 1) - 1 & \text{by equation (1)} \\
&= 2.(2.(1) + 1) - 1 \\
&= 2.(3) - 1 \\
&= 5 \\
\end{align*}
$$
<br />
<hr />
We would like to apply this same process to an arbitrary number n. Hopefully a pattern will emerge that will allow us to generate a formula in terms of n.
<br />
<br />
One potential problem is that we do different things (add or subtract one), depending on whether the number at that step is odd or even. We can avoid this problem if we have a formula which doesn't depend on whether the argument is odd or even.
<br />
<br />
Fortunately it's quite easy to do. Just note that:
<br />
<br />
$$
2b-1 = \left\{
\begin{array}{l l}
-1 & \text{if $b = 0$}\\
+1 & \text{if $b = 1$}
\end{array} \right.
$$
<br />
So we can collapse equations 2 and 3 into this equation:
$$
\begin{align*}
f(2n + b) = 2.f(n) + 2b - 1 & \text{ where b $\in \{0,1\}$}\tag{4} \\
\end{align*}
$$
<br />
<hr />
Equations 2 and 3 depend on whether the number we are solving for is odd or even. And the number we are solving for halves after each step. My first instinct on seeing this pattern was to express n as a <a href="http://en.wikipedia.org/wiki/Binary_number">binary number</a>:
<br />
<br />
Suppose that:
$$
2^{k} \leq n < 2^{k+1}
$$
Then we can express n as:
$$
\begin{align*}
n = \sum_{{i}={0}}^{k}{a_{i}.2^i} & \text{ where $a_{i} \in \{0,1\}$}\tag{5}
\end{align*}
$$
Note that the leading binary digit must be 1:
$$
a_{k} = 1 \tag{6}
$$
<br />
<br />
<hr />
We're also going to need a well-known equation for binary sums:
$$
\sum_{{i}={0}}^{k-1}2^{i} = 2^{k} - 1 \tag{7}
$$
This is really just saying that if all the binary digits in a number are 1, then adding 1 will cause all columns to clock over to give the next higher power of 2. This is similar to how addition works in the decimal system. For example:
$$
10^{3} - 1 = 1000 - 1 = 999 = \sum_{{i}={0}}^{2}{9.10^{i}} = \sum_{{i}={0}}^{2}{(10-1).10^{i}}
$$
<br />
<br />
<hr />
Let's carry out the steps to derive the formula for f(n):
$$
\begin{align*}
f(n) &= f( \sum_{{i}={0}}^{k}{a_{i}.2^i} ) & \text{by equation (5)} \\
&= f( 2 \sum_{{i}={1}}^{k}{a_{i}.2^{i-1}} + a_{0} ) \\
&= 2.f( \sum_{{i}={1}}^{k}{a_{i}.2^{i-1}} ) + 2 a_{0} - 1 & \text{by equation (4)} \\
&\text{Let's rewrite this formula as follows:} \\
f(n) &= 2.f( \sum_{{i}={1}}^{k}{a_{i}.2^{i-1}} ) + \sum_{{i}={0}}^{0}[(2 a_{i}-1).2^{i}] & \text{after step 1} \\
&= 2.f( 2 \sum_{{i}={2}}^{k}{a_{i}.2^{i-2}} + a_{1} ) + \sum_{{i}={0}}^{0}[(2 a_{i}-1).2^{i}] \\
&= 2.[2.f( \sum_{{i}={2}}^{k}{a_{i}.2^{i-2}}) + 2 a_{1} - 1] + \sum_{{i}={0}}^{0}[(2 a_{i}-1).2^{i}] & \text{by equation (4)} \\
&= 2^{2}.f( \sum_{{i}={2}}^{k}{a_{i}.2^{i-2}}) + 2^{1}[2 a_{1} - 1] + \sum_{{i}={0}}^{0}[(2 a_{i}-1).2^{i}] \\
&= 2^{2}.f( \sum_{{i}={2}}^{k}{a_{i}.2^{i-2}}) + \sum_{{i}={0}}^{1}[(2 a_{i}-1).2^{i}] & \text{after step 2} \\
&= \text{ ...} \\
&= 2^{j}.f( \sum_{{i}={j}}^{k}{a_{i}.2^{i-j}}) + \sum_{{i}={0}}^{j-1}[(2 a_{i}-1).2^{i}] & \text{after some step j, $1 \leq j \leq k $} \\
&= \text{ ...} \\
&= 2^{k}.f( \sum_{{i}={k}}^{k}{a_{i}.2^{i-k}}) + \sum_{{i}={0}}^{k-1}[(2 a_{i}-1).2^{i}] & \text{after step k} \\
\end{align*}
$$
<br />
So what I've done is to break the formula on the right into two terms, each with a summation. Then I've progressively eaten away at the left summation while building up the right.
<br />
<br />
Now we just need to expand and simplify:
$$
\begin{align*}
f(n) &= 2^{k}.f( \sum_{{i}={k}}^{k}{a_{i}.2^{i-k}}) + \sum_{{i}={0}}^{k-1}[(2 a_{i}-1).2^{i}] \\
&= 2^{k}.f(a_{k}) + 2 [\sum_{{i}={0}}^{k-1}a_{i}.2^{i}] - \sum_{{i}={0}}^{k-1}2^{i} \\
&= 2^{k}.f(a_{k}) + 2 [\sum_{{i}={0}}^{k-1}a_{i}.2^{i} + a_{k}.2^{k} - a_{k}.2^{k} ] - (2^{k} - 1) & \text{ by equation (7)} \\
&= 2^{k}.f(1) + 2 [\sum_{{i}={0}}^{k}a_{i}.2^{i} - 1.2^{k} ] - 2^{k} + 1 & \text{ since $a_{k} = 1$ by equation (6)} \\
&= 2^{k}.1 + 2 [\sum_{{i}={0}}^{k}a_{i}.2^{i} - 2^{k} ] - 2^{k} + 1 & \text{ by equation (1) } \\
&= 2^{k} + 2 [n - 2^{k} ] - 2^{k} + 1 & \text{ by equation (5) } \\
&= 2^{k} + 2n - 2^{k+1} - 2^{k} + 1 \\
&= 2n+1 - 2^{k+1} & \tag{8} \\
\end{align*}
$$
<br />
Since we can easily calculate k from n, that is our solution.
<br />
<br />
<hr />
<h2>The calculation method</h2>
Let's summarize how we solve this for a particular value of n:
<ol>
<li>Double n and add 1</li>
<li>Find the largest power of 2 that is less than or equal to n, and double it</li>
<li>Subtract the second number from the first</li>
<li>That is your answer!</li>
</ol>
<hr />
Let's check it using our previous example of n = 10:
$$
2^{3} \leq 10 < 2^{4}
$$
Hence k = 3.
<br />
<br />
So:
$$
\begin{align*}
f(10) &= 2 \times 10 + 1 - 2^{3+1} \\
&= 20 + 1 - 2^{4} \\
&= 21 - 16 \\
&= 5 \\
\end{align*}
$$
Which is the answer we got previously!
<br />
<br />
<hr />
<h2>The algebraic formula</h2>
If we want to formalize things we can also express the formula in terms of n only. Simply note that:
$$ k = \lfloor log_2 n \rfloor $$
So that:
$$
f(n) = 2n + 1 - 2^{{\lfloor log_2 n \rfloor} + 1} \tag{9}
$$
<br />
<hr />
<h2>Conclusion</h2>
So now we have a formula. But it took a lot of algebra. And we still don't have an intuitive feel for why the formula comes out the way it does.
<br />
<br />
In my next few blog posts I'm going to present a different way of looking at the problem. Instead of going straight for brute force calculation, we are going to apply a bit more insight first.
<br />
<br />
<hr />
Andrew Tweddlehttp://www.blogger.com/profile/02458479910895055369noreply@blogger.com0tag:blogger.com,1999:blog-10106192.post-61414074240631970662012-12-16T15:37:00.001-05:002014-02-08T10:25:21.140-05:00Two potato (the basic equations)<!-- From http://irrep.blogspot.com/2011/07/mathjax-in-blogger-ii.html: -->
<script src='http://cdn.mathjax.org/mathjax/latest/MathJax.js' type='text/javascript'>
MathJax.Hub.Config({
HTML: ["input/TeX","output/HTML-CSS"],
TeX: { extensions: ["AMSmath.js","AMSsymbols.js"],
equationNumbers: { autoNumber: "AMS" } },
extensions: ["tex2jax.js"],
jax: ["input/TeX","output/HTML-CSS"],
tex2jax: { inlineMath: [ ['$','$'], ["\\(","\\)"] ],
displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
processEscapes: true },
"HTML-CSS": { availableFonts: ["TeX"],
linebreaks: { automatic: true } }
});
</script>
<hr />
<h2>
All posts in this series:</h2>
<ul>
<li>
<a href="http://andrewtweddle.blogspot.com/2012/12/one-potato-two-potato.html">One Potato</a>: A description of the problem
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2012/12/two-potato-basic-equations.html">Two potato</a>: The basic equations
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/01/three-potato-algebraic-solution.html">Three potato</a>: An algebraic formula for the last person left
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/03/four-binary-calculation-method.html">Four</a>: A neat calculation method using binary numbers
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/03/five-potato-verifying-formulae-with-f.html">Five potato</a>: F# functions to check the various formulae
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/05/six-potato-building-calculation-graph.html">Six potato</a>: Using a calculation graph to see the pattern
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/06/seven-potato-generalizing-to-other.html">Seven potato</a>: Generalizing to other intervals
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/06/more-efficient-circle-elimination.html">More</a>: An efficient algorithm for arbitrary intervals
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2014/02/researching-josephus-problem.html">Online research</a>: In which I discover that it is called the Josephus problem
</li>
</ul>
<hr />
<h2>Introduction</h2>
In my <a href="http://andrewtweddle.blogspot.com/2012/12/one-potato-two-potato.html">previous blog entry</a> I described a problem from the 2010 South African Mathematics Olympiad. In the next few posts I'm going to show you a few ways of solving the problem. But in this blog entry, I'm just going to set up the equations which the various solutions will all make use of.
<br />
<br />
Let's start with a recap of the problem...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVnxp-scUWSr1k2Bc7oNA_VtcvDiGQx0TxtarUoGkhm3bjZY3v_UqSILYJaYrPkb9n0LPzwq4bK-4bwsOg4NJP8sv0dJFebMr_ayxlsiW04S_ZhAlKbUhKQOsY3NHEr5LzLds/s1600/Circle10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="310" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVnxp-scUWSr1k2Bc7oNA_VtcvDiGQx0TxtarUoGkhm3bjZY3v_UqSILYJaYrPkb9n0LPzwq4bK-4bwsOg4NJP8sv0dJFebMr_ayxlsiW04S_ZhAlKbUhKQOsY3NHEr5LzLds/s320/Circle10.png" width="320" /></a></div>
<br />
<br />
Imagine n people sitting in a circle. Label them 1 to n in a clockwise direction. Imagine walking around the circle tapping every second person on the shoulder (person 2, 4, 6 and so on). People leave the circle when they are tapped. This continues until only one person is left. What is the label of that person?<br />
<br />
<hr />
<h2>Derivation</h2>
<br />
Firstly let's define f(n) to be the label of the last person remaining when there are n people in the circle.<br />
<br />
Clearly f(1) = 1.<br />
<br />
What we'd like to do is find expressions for f(n) in terms of f(m) where m is smaller than n.<br />
<br />
Every second person is being asked to leave with roughly half as many people remaining after each walk around the circle. This suggests that we should consider the cases of n being odd and even separately.<br />
<br />
<hr />
<br />
So let's see what happens when there are an even number of people around the circle, say 2n people.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKmwmfauyuSbeDR-DmNKw-5eIxvM1u1F9-Xf62RAlYCvL1TtsDD8ejkH3_I12fAJfpl_85eX4l3EY5x5xTUKLGKMCobW2vz06vGzXEsBp_XDhzm2wDYnh0_NAh1e-Y0rTOEUE/s1600/Circle_Even.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKmwmfauyuSbeDR-DmNKw-5eIxvM1u1F9-Xf62RAlYCvL1TtsDD8ejkH3_I12fAJfpl_85eX4l3EY5x5xTUKLGKMCobW2vz06vGzXEsBp_XDhzm2wDYnh0_NAh1e-Y0rTOEUE/s320/Circle_Even.png" width="319" /></a></div>
<br />
In our first trip around the circle, the n even numbered people will leave the circle. This will leave n odd-numbered people.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjG3jy3GvgAwazCBE41SbbgJTCkWI3-WB5mEbgtgkh-WbviLQunUepUhe5njUN-cNKbtLwpE2HpR94pDsqMN0YyLKEMIj6B0stwv_kfdMlk6xoqGaACY-KiT4KWZwJyi6Aw9Yw/s1600/Circle_Even_AfterFirstPass.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjG3jy3GvgAwazCBE41SbbgJTCkWI3-WB5mEbgtgkh-WbviLQunUepUhe5njUN-cNKbtLwpE2HpR94pDsqMN0YyLKEMIj6B0stwv_kfdMlk6xoqGaACY-KiT4KWZwJyi6Aw9Yw/s320/Circle_Even_AfterFirstPass.png" width="319" /></a></div>
<br />
Let's re-label these odd people as 1, 2, ..., n. Then the (new) label of the last person remaining would equal f(n). That comes straight from the definition of f(n).<br />
<br />
But we are interested in the original label of this last person. That is not f(n) but 2.f(n) - 1. This is because 2k - 1 is the kth odd number.<br />
<br />
Hence f(2n) = 2 f(n) - 1.<br />
<br />
<hr />
<br />
Next we will find a similar formula for circles with an odd number of people.<br />
<br />
We already have a formula for n = 1. All other positive odd numbers can be expressed as 2n+1 for some positive integer n.<br />
<br />
Initially the situation looks like this for 2n+1 people:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXQmADs7JG3ANeYmSON-HOSIa_l-Ax-75w8SUdQl0_aYPZI6YEtIsucBpSySsEyHnVQJlD7zMxrs2re2DFBDg-d5Tk_MqYXDa3YkHkrYqaz1U4adHrJp84Pd7RFbMAxZtBqac/s1600/Circle_Odd_02.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="318" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXQmADs7JG3ANeYmSON-HOSIa_l-Ax-75w8SUdQl0_aYPZI6YEtIsucBpSySsEyHnVQJlD7zMxrs2re2DFBDg-d5Tk_MqYXDa3YkHkrYqaz1U4adHrJp84Pd7RFbMAxZtBqac/s320/Circle_Odd_02.png" width="320" /></a></div>
<br />
We would like to express f(2n+1) in terms of f(n). That means still having n people in the circle after the first round of removals. <br />
<br />
To do this we need to remove n+1 people. The first n people removed are all even numbered as before. But now the (n+1)th person removed is person number 1. This leaves the following arrangement:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0lX-ooUnSYKyLIQDKb1P0dE9VmAS9SPugBsJy9DIPK8vUwz-nAv9hwcYZ8QAbXNyXqK-RmyG-ypoPvFW10gI7jcToEPOThCI4I3weqEhLRiIqAhxXL4-kiXfvkpTqQy_ySpE/s1600/Circle_Odd_AfterFirstPass.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="261" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0lX-ooUnSYKyLIQDKb1P0dE9VmAS9SPugBsJy9DIPK8vUwz-nAv9hwcYZ8QAbXNyXqK-RmyG-ypoPvFW10gI7jcToEPOThCI4I3weqEhLRiIqAhxXL4-kiXfvkpTqQy_ySpE/s320/Circle_Odd_AfterFirstPass.png" width="320" /></a></div>
<br />
Note that the kth person in the new circle of people has a label of 2k+1.<br />
<br />
So we can re-label the remaining people from 1 to n as before, and f(n) is the new label of the last person remaining if we continue the elimination process. But in the original labelling scheme, this becomes 2.f(n) + 1.<br />
<br />
So we have shown that f(2n+1) = 2.f(n) + 1.<br />
<br />
<hr />
<h2>The basic equations</h2>
If we put all of this together, we get the following set of equations (where n is a positive integer):<br />
<br />
$$
\begin{align*}
f(1) &= 1 \tag{1} \\
f(2n) &= 2.f(n) - 1 \tag{2} \\
f(2n+1) &= 2.f(n) + 1 \tag{3}
\end{align*}
$$
<br />
<hr />
<h2>Next time</h2>
In my next few blog posts I will use these equations to provide a formula for f(n).
<br />
<br />
<hr />
<br />
Andrew Tweddlehttp://www.blogger.com/profile/02458479910895055369noreply@blogger.com0tag:blogger.com,1999:blog-10106192.post-74181390021813943392012-12-02T09:17:00.000-05:002014-02-08T10:29:50.652-05:00One potato (the problem statement)<hr />
<h2>
All posts in this series:</h2>
<ul>
<li>
<a href="http://andrewtweddle.blogspot.com/2012/12/one-potato-two-potato.html">One Potato</a>: A description of the problem
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2012/12/two-potato-basic-equations.html">Two potato</a>: The basic equations
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/01/three-potato-algebraic-solution.html">Three potato</a>: An algebraic formula for the last person left
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/03/four-binary-calculation-method.html">Four</a>: A neat calculation method using binary numbers
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/03/five-potato-verifying-formulae-with-f.html">Five potato</a>: F# functions to check the various formulae
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/05/six-potato-building-calculation-graph.html">Six potato</a>: Using a calculation graph to see the pattern
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/06/seven-potato-generalizing-to-other.html">Seven potato</a>: Generalizing to other intervals
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2013/06/more-efficient-circle-elimination.html">More</a>: An efficient algorithm for arbitrary intervals
</li>
<li>
<a href="http://andrewtweddle.blogspot.com/2014/02/researching-josephus-problem.html">Online research</a>: In which I discover that it is called the Josephus problem
</li>
</ul>
<hr />
<h2>
Introduction</h2>
Some months back I discovered the web site of the SA Math Foundation including <a href="http://www.samf.ac.za/QuestionPapers.aspx?AspxAutoDetectCookieSupport=1" target="_blank">past papers for the South African Mathematics Olympiad</a>. Back in high school I used to love solving Mathematics problems. So I downloaded a bunch of question papers and starting solving some of them.
<br />
<br />
This series of blog postings describes some interesting solutions I found to one of the problems.
<br />
<br />
<I>[Edit: I've since discovered that it is known as the Josephus problem]</I>
<br />
<br />
<hr />
<h2>The problem statement</h2>
One of the first papers I glanced at was the 2010 senior paper, second round.<br />
<br />
The first question described a situation in which there are 10 people in a circle, and every 2nd person is asked to leave the circle. This continues until only one person remains. If we number the people 1 to 10, and eliminate person 2 then 4 and so on, what is the number of the last person left?<br />
<br />
So it works much like the children's game "One potato, two potato", except that every 2nd person is eliminated, not every 8th person.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVnxp-scUWSr1k2Bc7oNA_VtcvDiGQx0TxtarUoGkhm3bjZY3v_UqSILYJaYrPkb9n0LPzwq4bK-4bwsOg4NJP8sv0dJFebMr_ayxlsiW04S_ZhAlKbUhKQOsY3NHEr5LzLds/s1600/Circle10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="397" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVnxp-scUWSr1k2Bc7oNA_VtcvDiGQx0TxtarUoGkhm3bjZY3v_UqSILYJaYrPkb9n0LPzwq4bK-4bwsOg4NJP8sv0dJFebMr_ayxlsiW04S_ZhAlKbUhKQOsY3NHEr5LzLds/s1600/Circle10.png" width="409" /></a></div>
<br />
<hr />
<h2>The plan</h2>
Later I was thinking back to the problem and made a fortuitous mistake. I accidentally thought that the problem was phrased as 2010 people, not 10 people (Math Olympiad problems often use the current year in the problem statement).<br />
<br />
So I ended up solving the problem generally, not just for the number 10 (which can easily be solved by brute force).
<br />
<br />
In the next few blog postings I intend to show you a few solutions to the problem. As I did with the <a href="http://andrewtweddle.blogspot.com/2012/11/why-pyramid-of-hexagons-is-cubic.html" target="_blank">Pyramid of Hexagons</a> problem, I want to provide an algebraic proof as well as intuitive insight into why the proof is true.<br />
<br />
So my plan is as follows:<br />
<ul>
<li>First I will generate the basic equations which all the solutions will use</li>
<li>Thereafter I will provide my first algebraic solution</li>
<li>Then I will try to give some insight into an interesting pattern behind the formula <em>Hint: There is a link to the binary number system.</em></li>
<li>Then I will give a simpler proof inspired by this pattern.</li>
<li>And finally I will provide the code I wrote to generate the illustrations.</li>
</ul>
<br />
<hr />
<h2>Disclaimer</h2>
While the visualization should be very helpful, it's not going to be as striking as my Pyramid of Hexagons visualization.
<br />
<br />
So I'm hoping that a reader of this blog will be able to find an even more elegant way of explaining the formula.
<br />
<br />
<hr />
Andrew Tweddlehttp://www.blogger.com/profile/02458479910895055369noreply@blogger.com0tag:blogger.com,1999:blog-10106192.post-15171310820796365352012-12-02T08:09:00.002-05:002013-04-07T09:43:18.348-04:00A Powershell script to sample CPU utilizations by process<!-- Alex Gorbatchev syntax highlighter hosted on AWS (donation given) -->
<link href='http://alexgorbatchev.com/pub/sh/current/styles/shCore.css' rel='stylesheet' type='text/css'/>
<link href="http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css" rel="stylesheet" type="text/css"></link>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js" type="text/javascript"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shAutoloader.js" type="text/javascript"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPowershell.js" type="text/javascript"></script>
<script type="text/javascript">//<![CDATA[
SyntaxHighlighter.config.bloggerMode = true;
SyntaxHighlighter.defaults['toolbar'] = false;
SyntaxHighlighter.all();
//]]>
</script>
We recently had an issue at work where CPU utilizations on our app servers started "flat-lining" at 100%. This starved our WCF service hosts of CPU cycles, leading to complaints of slow performance.<br />
<br />
The performance decline was dramatic and it didn't occur straight after a release. This suggested that something had changed elsewhere in the organisation.<br />
<br />
<hr />
The approach on our project has been to regularly release new modules side-by-side with the legacy system. Integration messages keep the data in the two systems synchronized. This significantly minimizes risk compared to a "big bang" approach.<br />
<br />
But some users continue using the old system surreptitiously. Printing is a case in point. Print driver issues had been a frequent source of problems in the past. So some users had gone back to using the old system for printing.<br />
<br />
Unbeknownst to us, the legacy team had removed the ability to print documents through the legacy system. So the printing load had suddenly increased by 50%.<br />
<br />
This led to other problems in the system. For example, our performance logging queue started building up on one of the app servers. Messages are inserted in parallel, but the logging service is throttled to only process one message at a time. So when the logging service is starved of CPU cycles, it starts falling behind. This leads to a constant drain on CPU, making further "flat-lining" more likely.<br />
<br />
<hr />
We had been aware for some time that printing could cause performance problems. So fortunately I already had a design in place for moving most of the printing load onto the print servers, where CPU was under-utilized. But it would take a week or two to develop and test the code.<br />
<br />
As an interim solution we commissioned a few more app servers.<br />
<br />
<hr />
To check whether printing was the sole source of problems, I took some random snapshots of resource monitor graphs on one of the application servers. At times printing was clearly causing the CPU to flatline at 100%. But at other times the app servers flatlined when no printing was taking place. So I suspected that printing was not the only cause of high CPU usage. Instead of app servers being able to recover from short-lived "distress states", the extra printing load had caused performance to reach a runaway situation.<br />
<br />
To quantify this, I wrote a Powershell script to use WMI to sample CPU utilization for each process on each app server. I wrote the results to a csv file, opened it in Excel, and generated a pivot table off the data.<br />
<br />
This highlighted some other issues that we hadn't been aware of before. For example, there was an SNMP monitoring process, SysEdge, which was consuming 25% CPU on 3 of the app servers for long periods each morning. I alerted the infrastructure team and they were able to fix this issue so that SysEdge was consuming under 1% CPU on all app servers.
<br />
<br />
<hr />
You can download the script from this location... <a href="https://docs.google.com/open?id=0B1vT2kRT7jlAa3k0ZGpSaGNYTjQ" target="_blank">Get-CPUUtilizationByServerAndProcess.ps1:</a>
<br />
<br />
<pre class='brush: ps'>
param (
[string[]] $serverNames = $( throw 'The list of server names was not provided' ),
[int] $coresPerServer = $( throw 'The number of cores per server was not provided' ),
[int] $repetitions = 1,
[int] $intervalInSeconds = 60
)
1..$repetitions | foreach-object {
$repetition = $_
[DateTime] $now = [DateTime]::Now
Write-Host "Getting CPU utilizations at $now (repetition $repetition)" -foregroundColor Green
foreach ($server in $serverNames)
{
Write-Host " Getting processes on $server" -foregroundColor Cyan
$prc = gwmi Win32_PerfFormattedData_PerfProc_Process -computerName $server # To get around bug where it is zero the first time called
$prc = gwmi Win32_PerfFormattedData_PerfProc_Process -computerName $server | ? { $_.Name -ne '_Total' -and $_.Name -ne 'Idle' }
$recordedAt = [DateTime]::Now
$summary = $prc | select-object IDProcess,Name,PercentProcessorTime,WorkingSet,@{n='PercentTime';e={$_.PercentProcessorTime/$coresPerServer}}
foreach ($processSummary in $summary)
{
$processName = $processSummary.Name
$processName = ($processName.Split('#'))[0]
$percentTime = $processSummary.PercentTime
$workingSet = $processSummary.WorkingSet
$record = new-object -typeName 'PSObject' -property @{
Server = $server
Process = $processName
Repetition = $repetition
AvgCPU = [Math]::Round( $percentTime, 2 )
WorkingSet = $workingSet
RecordedAt = $recordedAt
Year = $recordedAt.Year
Month = $recordedAt.Month
Day = $recordedAt.Day
DayOfWeek = $recordedAt.DayOfWeek
Hour = $recordedAt.Hour
Minute = $recordedAt.Minute
}
$record
}
}
$timeTillNextRepetition = $now.AddSeconds($intervalInSeconds) - [DateTime]::Now
$secondsToWait = $timeTillNextRepetition.TotalSeconds
if ($secondsToWait -gt 0)
{
Start-Sleep -Seconds $secondsToWait
}
}
</pre>
<hr />
A few notes on the Powershell script...
<br />
<br />
You will notice that I make the WMI call twice. This is because the first call for a particular app server will return all zeroes. I suspect the first call for each app server could be moved outside the loop (e.g. have a loop zero whose data is discarded). But it was good enough for my purposes.
<br />
<br />
<hr />
I also kept the following piece of code to illustrate a point...
<br />
<pre class='brush: ps'>
1..$repetitions | foreach-object {
$repetition = $_
...
}
</pre>
I used to write code like this a lot, assigning the $_ iteration variable to another variable. This was for 2 reasons...
<br />
<br />
Firstly, for debugging purposes. I would assign the variable a value to test. Then copy the remainder of the inner script block into the Powershell REPL.
<br />
<br />
And secondly, so that I could nest loops but still access the outer iteration variable.
<br />
<br />
However there is an easier way to get the same effect...
<br />
<br />
<pre class='brush: ps'>
foreach ($repetion in 1..$repetitions) {
...
}
</pre>
<br />
<hr />
We have since gone live with the new printing solution. And so far things are looking very promising. CPU load has only decreased slightly, but its "spikiness" seems to have improved significantly. Average execution times of service calls appear to have decreased by up to 18% at the busiest time of day. And it certainly looks like the print processes are terminating faster than before, presumably because print spooling is now taking place locally.
<br />
<br />
This is also the first time that we have moved an asynchronous process to a different server, thus improving the performance of the synchronous processes which affect user perception of speed. I can see a lot of opportunity for doing more of this in future.
<br />
<br />
<hr />
Andrew Tweddlehttp://www.blogger.com/profile/02458479910895055369noreply@blogger.com0tag:blogger.com,1999:blog-10106192.post-44960099839054490132012-12-02T03:34:00.001-05:002012-12-02T08:16:59.599-05:00My toolkit for troubleshooting performance issuesFor the past couple of years I've been involved in a long-running project to replace many of the software applications for a large hospital network. For much of that time I've been the team lead for the production support team (although more recently my role has changed to being the team's designer, using <a href="http://www.sparxsystems.com/products/ea/index.html" target="_blank">Enterprise Architect</a> to create UML diagrams for various enhancements to deployed modules).<br />
<br />
Over that time I've spent a lot of time troubleshooting and fixing performance issues. This has ranged from identifying and fixing problems caused by database blocking, hunting down memory leaks with <a href="http://www.windbg.org/" target="_blank">windbg</a> and <a href="http://www.stevestechspot.com/" target="_blank">sosex</a> and investigating the causes of high CPU utilization on app servers.<br />
<br />
<hr />
My approach invariably starts with finding a way to gather the right data to allow me to pinpoint where the problem lies.<br />
<br />
Even in a high pressure severity 1 outage, this is still my most common starting point. The data allows me to cut out a whole lot of areas of the system for further investigation. With just a few areas to look at in detail, I can then delegate in-depth troubleshooting across team members.<br />
<br />
In a crisis, the last thing you must do is panic and take a shotgun approach to looking for problems. First make sure you understand the problem. Data is a very objective way of doing this. But you can't do that unless you have data readily at hand and a toolkit of scripts and habits for rapidly gathering and analyzing that data.<br />
<br />
<hr />
The general purpose tools that I keep on using are <a href="http://en.wikipedia.org/wiki/Windows_PowerShell" target="_blank">Powershell</a> and Excel pivot tables.<br />
<br />
These are complemented by specific sources of data for each layer of the application, such as:<br />
<ul>
<li>Various SQL DM views and query plans for understanding SQL performance problems</li>
<li>Our performance logs (saved to the database) for analyzing WCF service call durations</li>
<li>WMI for app server and print server performance</li>
<li>Memory dumps for memory leak issues at the client</li>
</ul>
<hr />
In addition to this, there are some other tools that are very useful for getting a gut feel for a problem, such as SCOM (<a href="http://en.wikipedia.org/wiki/System_Center_Operations_Manager" target="_blank">Systems Center Operations Manager</a>) and Windows Resource Monitor.<br />
<br />
SCOM graphs are great for getting a high level view of what's happening across a range of servers. <br />
<br />
Resource Monitor is very useful for seeing what's happening on your servers at a point in time. The Process Explorer utility from the <a href="http://www.sysinternals.com/" target="_blank">sysinternals</a> suite is more powerful (we have it installed on all our app servers). But I've found that Resource Monitor is usually good enough for my purposes.<br />
<br />
<hr />
Interestingly enough, I haven't yet needed to use a code profiler, though this is probably the tool most developers think of first when you mention performance troubleshooting.<br />
<br />
Even when the problem is with the code, there are usually other ways of pinpointing which code is at fault.<br />
<br />
For example, one of the developers wrote code to hook into the NHibernate layer and write a debug message whenever a possible N+1 selects problem was detected. So those kind of problems can be picked up by the "new development" teams before they get deployed into the production environment.<br />
<br />
We also have a <a href="http://www.castleproject.org/projects/dynamicproxy/" target="_blank">Castle Dynamix Proxy</a> interceptor which hooks into the web service proxy and logs performance data for every WCF service call made.<br />
<br />
We have a monthly release cycle, and after each release we run a query against the performance logs to look for service calls which are performing significantly worse than a month earlier (this is the only reasonable baseline, since service call volumes differ significantly by day of the week and time of the month). So this also helps us to find poorly performing code without the need for a profiler.<br />
<br />
<hr />
Over the coming months I would like to share more details with you, including some of the Powershell and SQL scripts I've written to gather and analyze the data.<br />
<br />
<hr />
UPDATE:
<br />
<ul>
<li><a href="http://andrewtweddle.blogspot.com/2012/12/a-powershell-script-to-sample-cpu.html">A post about a Powershell script to sample CPU utilization by process</a></li>
</ul>
Andrew Tweddlehttp://www.blogger.com/profile/02458479910895055369noreply@blogger.com0tag:blogger.com,1999:blog-10106192.post-45024507090128635942012-11-25T02:12:00.001-05:002013-03-23T02:26:00.319-04:00hexnet.org and the pyramid of hexagonsThe <a href="http://hexnet.org">hexnet.org</a> web site recently found <a href="http://andrewtweddle.blogspot.com/2012/11/why-pyramid-of-hexagons-is-cubic.html">my blog posting on why a "pyramid" of hexagons is cubic</a> and linked to it on Google+.
Here is <a href="http://hexnet.org/content/cubic-sums-centered-hexagonal-numbers">a link to their explanation</a> of the same fact.
Andrew Tweddlehttp://www.blogger.com/profile/02458479910895055369noreply@blogger.com0tag:blogger.com,1999:blog-10106192.post-33651487809260218032012-11-17T10:49:00.001-05:002013-04-07T09:42:12.859-04:00Blender model for the pyramid of hexagons<!-- Alex Gorbatchev syntax highlighter hosted on AWS (donation given) -->
<link href='http://alexgorbatchev.com/pub/sh/current/styles/shCore.css' rel='stylesheet' type='text/css'/>
<link href="http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css" rel="stylesheet" type="text/css"></link>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js" type="text/javascript"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shAutoloader.js" type="text/javascript"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPython.js" type="text/javascript"></script>
<script type="text/javascript">//<![CDATA[
SyntaxHighlighter.config.bloggerMode = true;
SyntaxHighlighter.defaults['toolbar'] = false;
SyntaxHighlighter.all();
//]]>
</script>
In <a href="http://andrewtweddle.blogspot.com/2012/11/why-pyramid-of-hexagons-is-cubic.html" target="_blank">my previous post</a> I provided a visual explanation of why a pyramid of hexagons is cubic. I created the visuals for that post using <a href="http://www.blender.org/" target="_blank">Blender 2.64</a> and <a href="http://www.python.org/" target="_blank">Python</a>. For those interested in playing with the Blender model, I have uploaded it (with its embedded Python script) to <a href="https://docs.google.com/open?id=0B1vT2kRT7jlAbjFFWVl5NlY2ZTQ" target="_blank">this Google docs drive</a>.
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWveRk9Ba6_cn9CaFcXjs45WL0_HF3TdeSiJAix_SuAUuN3o67LJlf8-0r43p1pJ1pRsUzNV11KE1tmD_T7e_xzm6v7GFqHlc8HMye6ePGozlqCsYa0zgRn08OwK28okdRBBY/s1600/Blender_HexPyramid.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWveRk9Ba6_cn9CaFcXjs45WL0_HF3TdeSiJAix_SuAUuN3o67LJlf8-0r43p1pJ1pRsUzNV11KE1tmD_T7e_xzm6v7GFqHlc8HMye6ePGozlqCsYa0zgRn08OwK28okdRBBY/s1600/Blender_HexPyramid.PNG" /></a></div>
<br />
Bear in mind that this is my first foray into both Blender and Python. So it's unlikely to be "idiomatic" Python or an example of Blender best practices!<br />
<br />
<hr />
<br />
To reduce size, the model only contains various cameras and lights. These are contained in layers 0, 8, 9, 10, 18 and 19 as can be seen at the bottom of the 3D view:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiUqN5SHA6SAdp5BHVqWwzx29s92GrNoL_4ywdy0HO_aqJOqlEQqtcPwOc5QY1QGeGNIUjztkZJTltmX2z3xAfgTMitrc_QvyysJXG6cmyT9aaunzDzF_QQPfqmkcP-sEAyxo/s1600/LayersBeforeRunningScript.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="92" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiUqN5SHA6SAdp5BHVqWwzx29s92GrNoL_4ywdy0HO_aqJOqlEQqtcPwOc5QY1QGeGNIUjztkZJTltmX2z3xAfgTMitrc_QvyysJXG6cmyT9aaunzDzF_QQPfqmkcP-sEAyxo/s320/LayersBeforeRunningScript.PNG" width="320" /></a></div>
<br />
<hr />
<br />
To create the blocks and hexagons you need to run the embedded script. First change from the "Default" layout to "Scripting" layout. Ensure the "Text" script is selected. And click on the "Run script" button.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwA57Mm5bEA_Pyvg65EUQpwZsLDo_R2NLWkHYaNN4hI8MIxDHP6M5UF1A8ph6pLDB6Pmat21uPAqvMACbv1up_lG086YI9C0NuK5de9AX9X4g2hXR1ppNSWtxN3Nd-pSnnarE/s1600/RunningTheScript.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwA57Mm5bEA_Pyvg65EUQpwZsLDo_R2NLWkHYaNN4hI8MIxDHP6M5UF1A8ph6pLDB6Pmat21uPAqvMACbv1up_lG086YI9C0NuK5de9AX9X4g2hXR1ppNSWtxN3Nd-pSnnarE/s1600/RunningTheScript.PNG" /></a></div>
<br />
Then change back to the default layout. You will see that layers 1 to 4 and 11 to 14 now show dots to indicate that they contain objects:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinjEmVSeSxNytfCQX-RW2xJs1z4jYndjWjpKFPNuevcXOw4IuLryu-e8sgG_koPPsfX3ke5qRCAm4U0MKijZUo1MhqDpa0jrfsez4bRL-NPOJYG409lgJ8KdIHcLc9Rsue0Fs/s1600/LayersAfterRunningScript.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="77" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinjEmVSeSxNytfCQX-RW2xJs1z4jYndjWjpKFPNuevcXOw4IuLryu-e8sgG_koPPsfX3ke5qRCAm4U0MKijZUo1MhqDpa0jrfsez4bRL-NPOJYG409lgJ8KdIHcLc9Rsue0Fs/s320/LayersAfterRunningScript.PNG" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<hr />
<div class="separator" style="clear: both; text-align: center;">
</div>
Layers 1 to 4 (the numbering starts at zero) in the top left block contain the various shells making up the cube. Layers 11 to 14 in the bottom left block contain the various layers making up the pyramid. <br />
<br />
Since these two structures overlap, you shouldn't select both at the same time.<br />
<br />
<hr />
<br />
However when you want to start again, you should select just these 8 layers, hit A to toggle selection of all objects, and DELETE to delete the objects. Make sure you haven't selected any of the layers with cameras and lights, otherwise you will accidentally delete these too.<br />
<br />
<hr />
<br />
Use F12 to render the image as seen by the selected camera. But first you need to make sure that you have selected the layer that the camera and light are in, otherwise you will just see a silhouette. <br />
<br />
Also make sure that the correct camera is selected for the scene...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitOXRNQg6tSNMByuIIYr5lv_LQjdKB9P7TRkTso75piPP-V1W-aMYvDcPYrCTF2BbHQHJDu4QxQYJv33CiluI9u0yw9lxQ2i0l-v6yXjmuBUNbf3uOITUWWtIFVWriOPFxnhU/s1600/SelectingACamera.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitOXRNQg6tSNMByuIIYr5lv_LQjdKB9P7TRkTso75piPP-V1W-aMYvDcPYrCTF2BbHQHJDu4QxQYJv33CiluI9u0yw9lxQ2i0l-v6yXjmuBUNbf3uOITUWWtIFVWriOPFxnhU/s1600/SelectingACamera.PNG" /></a></div>
<br />
<hr />
<div class="separator" style="clear: both; text-align: left;">
</div>
The Python script allows you to generate an entire cube or Pyramid of a particular size using the following lines of code at the end of the script:<br />
<br />
<pre class="brush: py;gutter: false">
createCube(4)
createHexPyramid(4)
</pre>
<br />
However you can also generate just part of the structure. This gives more insight into how the cube can be constructed from the cornerstone outwards. For example, the following code generates the outermost ring of the outermost shell in a 4x4x4 cube:<br />
<br />
<pre class="brush: py;gutter: false">
createRingOfBlocks(4, 4, 4)
</pre>
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwbgCUxUeFb_qglhcb_VFlKt35fIYvGEI7G35uNFbskqnySyV9MmXMGOox9wn0bG-Uv5_U5K9LGh0a2Q8tscNVdr25dASMoy54jEdMO86TWHjiMCs4H0taFbMKCspG55gLzAI/s1600/OuterRingOfCube.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwbgCUxUeFb_qglhcb_VFlKt35fIYvGEI7G35uNFbskqnySyV9MmXMGOox9wn0bG-Uv5_U5K9LGh0a2Q8tscNVdr25dASMoy54jEdMO86TWHjiMCs4H0taFbMKCspG55gLzAI/s320/OuterRingOfCube.PNG" width="313" /></a></div>
<br />
<br />
And here is the code that does the same for the pyramid:<br />
<br />
<pre class="brush: py;gutter: false">
createRingOfHexTiles(4, 4, 4, radius=1, thickness=1)
</pre>
<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" height="183" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKr2Hi2CrkI9W4FgqDBitBzLAWtOtZmfvmrGSKOHMWI9z6AY6odtIfVLHRytjPjRhc8NQbnHa5tyIGJpW4pT0xcUmEmVQj6O2pG8ceIEwkn6vbNzWv3hns6imobyWQIKABhSA/s320/OuterRingOfHexagon.PNG" width="320" /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
...or from another angle...</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCdXsbRdZn-MznEvjAp19rbHMdvJgdKs4sRcP7VtWG22RpH79Dp1GCW3D4QxzsPsuc6Mz8jb1RGkpspjNjoVBu5B4eoAlFHFWsEbwCtSAWaYV45aDE_wsdk9iD87n016r6ggE/s1600/OuterRingOfHexagon_02.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="315" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCdXsbRdZn-MznEvjAp19rbHMdvJgdKs4sRcP7VtWG22RpH79Dp1GCW3D4QxzsPsuc6Mz8jb1RGkpspjNjoVBu5B4eoAlFHFWsEbwCtSAWaYV45aDE_wsdk9iD87n016r6ggE/s320/OuterRingOfHexagon_02.PNG" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<hr />
<br />
One quirk to note about the code...
<br />
<br />
I calculated the South-Western line of blocks to start from the orange block going to the yellow block (in fact, originally the orange block was coloured red). My thinking was to label each line from 0 to n-1, but ignore hex zero (since it is also hex n-1 of the previous line). It turned out that wasn't the best idea as far as hex colours go.
<br/>
<br/>
Instead of re-calculating the formulae for all the hexagons, I took a short-cut and simply remapped the colour of each hexagon as if it was the hexagon following it (in clockwise order)...
<br />
<pre class="brush: py">
def getColorForBlockOrHex(layer, ring, direction, position):
# There is a better colour scheme than the one I chose.
# Luckily it is easy to change to it.
# Each hex/block uses the colour that the hex after it would have been assigned.
(direction, position) = getDirAndPosOfNextClockwiseBlockOrHex( direction, position, ring)
startColor = getStartColorForDirection(direction)
if ring == 1 or ring == 2:
(r, g, b) = startColor
else:
nextDir = getNextDir(direction)
endColor = getStartColorForDirection(nextDir)
(r1, g1, b1) = startColor
(r2, g2, b2) = endColor
ratio1 = (ring - position)/(ring-1)
ratio2 = (position - 1)/(ring-1)
r = ratio1 * r1 + ratio2 * r2
g = ratio1 * g1 + ratio2 * g2
b = ratio1 * b1 + ratio2 * b2
return (r,g,b)
</pre>
<br />
<hr />
Here is the full Python script:
<pre class="brush: py">
import bpy
import math
from math import *
# From http://blenderscripting.blogspot.com/2011/05/blender-25-python-selecting-layer.html:
def selectLayer(layer):
return tuple(i == layer for i in range(0, 20))
# From http://wiki.blender.org/index.php/Dev:2.5/Py/Scripts/Cookbook/Code_snippets/Materials_and_textures:
def makeMaterial(name, diffuse, specular, alpha):
mat = bpy.data.materials.new(name)
mat.diffuse_color = diffuse
mat.diffuse_shader = 'LAMBERT'
mat.diffuse_intensity = 1.0
mat.specular_color = specular
mat.specular_shader = 'COOKTORR'
mat.specular_intensity = 0.5
mat.alpha = alpha
mat.ambient = 1
return mat
def setMaterial(ob, mat):
me = ob.data
me.materials.append(mat)
# ************************************************
# Following code by Andrew Tweddle, November 2012:
# ************************************************
# -----------------------------------
# Code common to blocks and hexagons:
# -----------------------------------
class Direction:
C = 0
SW = 1
W = 2
NW = 3
NE = 4
E = 5
SE = 6
def DirToString(direction):
if direction == Direction.C:
return "C"
elif direction == Direction.SW:
return "SW"
elif direction == Direction.W:
return "W"
elif direction == Direction.NW:
return "NW"
elif direction == Direction.NE:
return "NE"
elif direction == Direction.E:
return "E"
else:
return "SE"
def getNextDir(direction):
if direction == Direction.C:
return Direction.C
elif direction == Direction.SE:
return Direction.SW
else:
return direction + 1
def getDirAndPosOfNextClockwiseBlockOrHex(direction, position, ring):
if direction == Direction.C:
return (direction, position)
if position == ring - 1:
newDir = getNextDir(direction)
return (newDir, 1)
return (direction, position + 1)
def getStartColorForDirection(direction):
if direction == Direction.C:
return (1,1,1) # White
elif direction == Direction.SW:
return (1, 0, 0) # Red
elif direction == Direction.W:
return (1, 1, 0) # Yellow
elif direction == Direction.NW:
return (0, 1, 0) # Green
elif direction == Direction.NE:
return (0, 1, 1) # Cyan
elif direction == Direction.E:
return (0, 0, 1) # Blue
else: # direction == Direction.SE
return (1, 0, 1) # Magenta
def getColorForBlockOrHex(layer, ring, direction, position):
# There is a better colour scheme than the one I chose.
# Luckily it is easy to change to it.
# Each hex/block uses the colour that the hex after it would have been assigned.
(direction, position) = getDirAndPosOfNextClockwiseBlockOrHex( direction, position, ring)
startColor = getStartColorForDirection(direction)
if ring == 1 or ring == 2:
(r, g, b) = startColor
else:
nextDir = getNextDir(direction)
endColor = getStartColorForDirection(nextDir)
(r1, g1, b1) = startColor
(r2, g2, b2) = endColor
ratio1 = (ring - position)/(ring-1)
ratio2 = (position - 1)/(ring-1)
r = ratio1 * r1 + ratio2 * r2
g = ratio1 * g1 + ratio2 * g2
b = ratio1 * b1 + ratio2 * b2
return (r,g,b)
# ----------------------------------
# Code specific to blocks and cubes:
# -----------------------------------
def getBlockPositionInLastLayer(ring, direction, position):
if direction == Direction.C:
return (0,0,0)
elif direction == Direction.SW:
return (ring-1, ring-position-1,0)
elif direction == Direction.W:
return (ring-1, 0,position)
elif direction == Direction.NW:
return (ring-position-1, 0, ring-1)
elif direction == Direction.NE:
return (0, position,ring-1)
elif direction == Direction.E:
return (0, ring-1, ring-position-1)
else: # direction == Direction.SE
return (position, ring-1, 0)
def createBlock(layer, ring, direction, position, numberOfLayers):
(x,y,z) = getBlockPositionInLastLayer(ring, direction, position)
if layer < numberOfLayers:
offset = numberOfLayers-layer
(x,y,z) = (x+offset,y+offset,z+offset)
blockName = "Block_L" + str(layer) + "R" + str(ring) + DirToString(direction) + "_" + str(position)
blenderLayer = layer - ring + 1 # For bottom-up layering use: blenderLayer = layer
layerSelection = selectLayer(blenderLayer)
bpy.context.scene.layers[blenderLayer] = True # Otherwise bpy.context.object is not the newly added cube!
bpy.ops.mesh.primitive_cube_add(location=(x, y, z), layers=layerSelection)
bpy.context.object.name = blockName
bpy.context.object.dimensions = (0.995,0.995,0.995)
# Note: Made it slightly smaller to create shadows between adjacent blocks of the same colour
#
# Create a material for the new block:
diffuseColor = getColorForBlockOrHex(layer, ring, direction, position)
matName = "Mat_" + blockName
mat = makeMaterial(name = matName, diffuse = diffuseColor, specular=(1,1,1), alpha=1)
setMaterial(bpy.context.object, mat)
def createLineInRingOfBlocks(layer, ring, direction, numberOfLayers):
for pos in range(1,ring):
createBlock(layer, ring, direction, pos, numberOfLayers)
def createRingOfBlocks(layer, ring, numberOfLayers):
if ring == 1:
createBlock(layer, 1, Direction.C, 1, numberOfLayers)
else:
for dir in range(1,7):
createLineInRingOfBlocks(layer, ring, dir, numberOfLayers)
def createLayerOfBlocks(layer, numberOfLayers):
for ring in range(1,layer+1):
createRingOfBlocks(layer, ring, numberOfLayers)
def createCube(numberOfLayers):
for layer in range(1, numberOfLayers+1):
createLayerOfBlocks(layer, numberOfLayers)
# ----------------------------------
# Code specific to hexagons:
# -----------------------------------
def getHexPositionOnFlatSurface(ring, direction, position, radius):
sqrt3 = math.sqrt(3)
if direction == Direction.C:
return ( 0, 0)
elif direction == Direction.SW:
return ( -1.5 * position * radius, -sqrt3 * (ring-1) * radius + sqrt3 / 2 * position * radius)
elif direction == Direction.W:
return ( -1.5 * (ring - 1) * radius, -sqrt3 / 2 * (ring - 1 - 2 * position ))
elif direction == Direction.NW:
return ( -1.5 * (ring - 1 - position) * radius, sqrt3 / 2 * (ring - 1 + position ) * radius )
elif direction == Direction.NE:
return ( 1.5 * position * radius, sqrt3 * ( ring - 1 - position / 2 ) )
elif direction == Direction.E:
return ( 1.5 * (ring-1) * radius, sqrt3 / 2 * radius * ( ring - 1 - 2 * position))
else: # direction == Direction.SE
return ( 1.5 * radius * ( ring - 1 - position), - sqrt3 / 2 * radius * ( ring - 1 + position ))
def getHexPosition(layer, ring, direction, position, numberOfLayers, radius, thickness):
(x,y) = getHexPositionOnFlatSurface(ring, direction, position, radius)
z = thickness * (numberOfLayers - layer)
return (x,y,z)
def createHexTile(layer, ring, direction, position, numberOfLayers, radius, thickness):
(x,y,z) = getHexPosition(layer, ring, direction, position, numberOfLayers, radius, thickness)
hexName = "Hex_L" + str(layer) + "R" + str(ring) + DirToString(direction) + "_" + str(position)
blenderLayer = 10 + layer - ring + 1 # For bottom-up layering, use blenderLayer = 10 + layer
# Note: hexagons are in a separate set of layers from blocks
layerSelection = selectLayer(blenderLayer)
bpy.context.scene.layers[blenderLayer] = True # Otherwise bpy.context.object is not the newly added cube!
# Make it slightly smaller to create shadows between adjacent hexes of the same colour:
adjustedThickness = thickness * 0.995
adjustedRadius = radius * 0.995
bpy.ops.mesh.primitive_cylinder_add(vertices=6, radius=adjustedRadius, depth=adjustedThickness, end_fill_type='NGON', location=(x,y,z), rotation=(0,0,radians(30)), layers=layerSelection)
bpy.context.object.name = hexName
# Create a material for the new hex tile:
diffuseColor = getColorForBlockOrHex(layer, ring, direction, position)
matName = "Mat_" + hexName
mat = makeMaterial(name = matName, diffuse = diffuseColor, specular=(1,1,1), alpha=1)
setMaterial(bpy.context.object, mat)
def createLineInRingOfHexTiles(layer, ring, direction, numberOfLayers, radius, thickness):
if ring == 1:
createHexTile(layer, 1, Direction.C, 1, numberOfLayers, radius, thickness)
else:
for pos in range(1,ring):
createHexTile(layer, ring, direction, pos, numberOfLayers, radius, thickness)
def createRingOfHexTiles(layer, ring, numberOfLayers, radius, thickness):
if ring == 1:
createLineInRingOfHexTiles(layer, ring, Direction.C, numberOfLayers, radius, thickness)
else:
for dir in range(1,7):
createLineInRingOfHexTiles(layer, ring, dir, numberOfLayers, radius, thickness)
def createLayerOfHexTiles(layer, numberOfLayers, radius, thickness):
for ring in range(1,layer+1):
createRingOfHexTiles(layer, ring, numberOfLayers, radius, thickness)
def createHexPyramid(numberOfLayers, radius = 1, thickness = 1):
for layer in range(1, numberOfLayers+1):
createLayerOfHexTiles(layer, numberOfLayers, radius, thickness)
# --------------------------------
# Generate cubes and hex pyramids:
# --------------------------------
# Use any number of layers up to 7.
# More than that and hexes/blocks will be placed in camera layers,
# making deleting more difficult...
createCube(4)
createHexPyramid(4)
# -------------------------------------------------------------
# Sample code to generate cubes and hex pyramids incrementally:
# -------------------------------------------------------------
# You can build up the cube incrementally e.g.
# createLineInRingOfBlocks(4, 4, Direction.SW, 4)
# createLineInRingOfHexTiles(4, 4, Direction.SW, 4, radius=1, thickness=1)
#
# createRingOfBlocks(4, 4, 4)
# createRingOfHexTiles(4, 4, 4, radius=1, thickness=1)
</pre>
<hr />
Anyway, that's it. Enjoy!
Andrew Tweddlehttp://www.blogger.com/profile/02458479910895055369noreply@blogger.com0tag:blogger.com,1999:blog-10106192.post-72418233760336866872012-11-11T05:04:00.000-05:002013-03-23T02:26:59.392-04:00Why a pyramid of hexagons is cubicBelow is a "pyramid" made out of hexagonal blocks:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS6jWDhlC7c-T4cNuv2ZHCRixv1EwtGwI94wKaqPN5269eT5ViEmoehoszSIY6D-QRPOvqA1nzxE3LrAu8ebwXnkA-BFNQrzWUZVGuoTRRVR7c8DYmesDQ6NoUazsB3fxf8tk/s1600/RainbowPyramidOfHexagons.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="230" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS6jWDhlC7c-T4cNuv2ZHCRixv1EwtGwI94wKaqPN5269eT5ViEmoehoszSIY6D-QRPOvqA1nzxE3LrAu8ebwXnkA-BFNQrzWUZVGuoTRRVR7c8DYmesDQ6NoUazsB3fxf8tk/s320/RainbowPyramidOfHexagons.PNG" width="320" /></a></div>
<br />
Surprisingly, such a structure always contains a cubic number of hexagonal tiles (aka hexes). The structure above is 4 high and contains 4 x 4 x 4 = 64 hexes.<br />
<br />
In a previous blog post, I gave <a href="http://andrewtweddle.blogspot.com/2012/11/proof-that-pyramid-of-hexagons-contains.html" target="_blank">an algebraic proof of why this must be so</a>. Now I present a visual demonstration to help you <em>really</em> understand why!<br />
<br />
In a way, what I'm trying to show is this...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjt8qrTkYIdS9QE9GzkSvNzUvcFcSNq23EbPHJXgx1QqWvO2h9yPqrBcYo-L1fu9scXUUYOtaSzAv8JZg2jNQqx4UDigN_WEqYabe4aooUcJXRF0Ug-RZORZ2mBQdF-vFRsCVQ/s1600/HexPyramidCubeEquality.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjt8qrTkYIdS9QE9GzkSvNzUvcFcSNq23EbPHJXgx1QqWvO2h9yPqrBcYo-L1fu9scXUUYOtaSzAv8JZg2jNQqx4UDigN_WEqYabe4aooUcJXRF0Ug-RZORZ2mBQdF-vFRsCVQ/s1600/HexPyramidCubeEquality.PNG" /></a></div>
<br />
<br />
<div class="separator" style="clear: both; text-align: left;">
Let's look at the pyramid of hexagons from almost directly above. See how each hex has an equivalent block in the cube...</div>
<div class="separator" style="clear: both; text-align: left;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgA-SW0AfHOtA7Hmg3f5ovSsUgb-R-fLLV4M_a4-kOpUvE9cuk4oKf18svNcU3iuoM0Hha0VmRB8XNg-1uyWRSgOOn4bEP0EOGuRY4DExJcxYVJdShCZw926ty74dfzZDUsUyE/s1600/HexPyramidCubeEquality02.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgA-SW0AfHOtA7Hmg3f5ovSsUgb-R-fLLV4M_a4-kOpUvE9cuk4oKf18svNcU3iuoM0Hha0VmRB8XNg-1uyWRSgOOn4bEP0EOGuRY4DExJcxYVJdShCZw926ty74dfzZDUsUyE/s1600/HexPyramidCubeEquality02.png" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
</div>
<div class="separator" style="clear: both; text-align: left;">
But what about the hidden pieces in the pyramid and cube - do they also correspond directly?</div>
<div class="separator" style="clear: both; text-align: left;">
</div>
<div class="separator" style="clear: both; text-align: left;">
Yes, they do. The visible pieces of each structure form a "shell" over that structure. Remove these shells and you are left with a smaller pyramid and cube. The outer shells of these smaller structures map across in exactly the same way. Keep on peeling away the layers and you see that every hex tile maps to a unique block in the cube and vice versa.</div>
<div class="separator" style="clear: both; text-align: left;">
</div>
<div class="separator" style="clear: both; text-align: left;">
I set up a spreadsheet to show how this nesting works...</div>
<div class="separator" style="clear: both; text-align: left;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcftNd1-CdUF6WOARONBDu_jVc5gqDXweMcy1c_wJzV0CwvZOZnUJl97ezKr0rNyKJzxmISqi3JQ0rAECYkUng1CpANp1jnQMGs47E-AAHTxDU7ujJ1F2o6LZ9m_tR5bHjznw/s1600/ExcelSpreadsheetOfPyramidCubeEquations_02.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcftNd1-CdUF6WOARONBDu_jVc5gqDXweMcy1c_wJzV0CwvZOZnUJl97ezKr0rNyKJzxmISqi3JQ0rAECYkUng1CpANp1jnQMGs47E-AAHTxDU7ujJ1F2o6LZ9m_tR5bHjznw/s1600/ExcelSpreadsheetOfPyramidCubeEquations_02.PNG" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
</div>
<div class="separator" style="clear: both; text-align: left;">
</div>
<div class="separator" style="clear: both; text-align: left;">
And that, my friends, is how beautiful Mathematics can be!</div>
<div class="separator" style="clear: both; text-align: left;">
</div>
Andrew Tweddlehttp://www.blogger.com/profile/02458479910895055369noreply@blogger.com1tag:blogger.com,1999:blog-10106192.post-88857487840821445832012-11-08T10:12:00.000-05:002013-03-23T02:27:24.309-04:00Some hints for why the pyramid of hexagons is cubicIn my previous two posts <a href="http://andrewtweddle.blogspot.com/2012/11/an-observation-pyramid-of-hexagons-has.html">I noted that a pyramid built of hexagonal boards contains a cubic number of hexagons</a> and I provided <a href="http://andrewtweddle.blogspot.com/2012/11/proof-that-pyramid-of-hexagons-contains.html" target="_blank">an algebraic proof</a> of this.<br />
<br />
My goal is to provide a visual explanation of why this is so.<br />
<br />
For those who would like to tackle this challenge themselves, here are a few visual hints...<br />
<br />
Is the following shape a hexagon?<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhU8CsdxuZ5zM8_Oro-hM0t9dbLK7KMV4zM00dPYYDUAQtaNwmQIECiIMz6_RnP1_0U9GmoszjSXYeQDmh8VeUdVdhrKA_B4x_DYSJGraT3lY7f65OWmE8iu9C-3uT4qBbiQz8/s1600/HexagonalSilhouette_04.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="286" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhU8CsdxuZ5zM8_Oro-hM0t9dbLK7KMV4zM00dPYYDUAQtaNwmQIECiIMz6_RnP1_0U9GmoszjSXYeQDmh8VeUdVdhrKA_B4x_DYSJGraT3lY7f65OWmE8iu9C-3uT4qBbiQz8/s320/HexagonalSilhouette_04.PNG" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<br />
Or the silhouette of a cube?<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrhnXizSnyN6vGImT5Lv9vUS-amdih-5S1IWIUFVBb3BHcP9PlTXkLb6V4HjRSBGJdLevDvLGdCVzZHVRlPUgI6lS3T1z90k7bjCSY4MaL3ZmPQubSot5A-Bt7tKjoQPslbNg/s1600/CubeOfBlocks_04.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrhnXizSnyN6vGImT5Lv9vUS-amdih-5S1IWIUFVBb3BHcP9PlTXkLb6V4HjRSBGJdLevDvLGdCVzZHVRlPUgI6lS3T1z90k7bjCSY4MaL3ZmPQubSot5A-Bt7tKjoQPslbNg/s320/CubeOfBlocks_04.PNG" width="306" /></a></div>
<br />
<br />
Or the silhouette of the base and two adjacent sides of the same cube?<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEim7GpMu7f8vBEvz3YmScJIgVcwtwDWkr0simVkl2gZNEJmrzUh58mCS2A0PkfFYbXUKQH1HRl3ql9BVZxZ4JO1XvHlfD0HwMMwxoFrrfcGWzj-hU1uvSMr87brwmS8q0DsgTc/s1600/FloorAndTwoWalls_03.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEim7GpMu7f8vBEvz3YmScJIgVcwtwDWkr0simVkl2gZNEJmrzUh58mCS2A0PkfFYbXUKQH1HRl3ql9BVZxZ4JO1XvHlfD0HwMMwxoFrrfcGWzj-hU1uvSMr87brwmS8q0DsgTc/s320/FloorAndTwoWalls_03.PNG" width="300" /></a></div>
<br />
<br />
<div class="separator" style="clear: both; text-align: left;">
It's also possible to view this last figure from below, like this:</div>
<div class="separator" style="clear: both; text-align: left;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRrrWacL-GGJNedFx4pBKv5Xa3u9XF7MscY63DtOdqQqRCxfJLG_9Nj_bscfgzPkDZGQkjVOYHk8f-R-ozmkwrAxiR0lew6Ph-QEHEW_c36YBI-6_0to0ZJ68SwbwtgBR6J6Y/s1600/FloorAndTwoWalls_ViewFromBelow_02.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="318" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRrrWacL-GGJNedFx4pBKv5Xa3u9XF7MscY63DtOdqQqRCxfJLG_9Nj_bscfgzPkDZGQkjVOYHk8f-R-ozmkwrAxiR0lew6Ph-QEHEW_c36YBI-6_0to0ZJ68SwbwtgBR6J6Y/s320/FloorAndTwoWalls_ViewFromBelow_02.PNG" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
</div>
<div class="separator" style="clear: both; text-align: left;">
</div>
<div class="separator" style="clear: both; text-align: left;">
Looking from below makes it easier to see the cornerstone and edge pieces which aren't visible in the previous figure. So you can count the blocks more easily.</div>
<div class="separator" style="clear: both; text-align: left;">
</div>
<div class="separator" style="clear: both; text-align: left;">
At this point you might want to count the number of blocks adjacent to the white cornerstone (including diagonally). And the number of blocks 2 steps away. And the number of blocks 3 steps away.</div>
<div class="separator" style="clear: both; text-align: left;">
</div>
<div class="separator" style="clear: both; text-align: left;">
Are you seeing a pattern yet?</div>
<div class="separator" style="clear: both; text-align: left;">
</div>
Andrew Tweddlehttp://www.blogger.com/profile/02458479910895055369noreply@blogger.com0tag:blogger.com,1999:blog-10106192.post-73852219771993772922012-11-03T15:39:00.001-04:002013-03-24T16:51:28.082-04:00Proof that a pyramid of hexagons contains a cubic number of tiles<!-- From http://irrep.blogspot.com/2011/07/mathjax-in-blogger-ii.html: -->
<script src='http://cdn.mathjax.org/mathjax/latest/MathJax.js' type='text/javascript'>
MathJax.Hub.Config({
HTML: ["input/TeX","output/HTML-CSS"],
TeX: { extensions: ["AMSmath.js","AMSsymbols.js"],
equationNumbers: { autoNumber: "AMS" } },
extensions: ["tex2jax.js"],
jax: ["input/TeX","output/HTML-CSS"],
tex2jax: { inlineMath: [ ['$','$'], ["\\(","\\)"] ],
displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
processEscapes: true },
"HTML-CSS": { availableFonts: ["TeX"],
linebreaks: { automatic: true } }
});
</script>
In <a href="http://andrewtweddle.blogspot.com/2012/11/an-observation-pyramid-of-hexagons-has.html" target="_blank">my previous blog post</a> I observed that there are a cubic number of hexagons in a pyramid of hexagonal boards (such as found in the game <a href="http://www.burleygames.com.gridhosted.co.uk/board-games/take-it-easy/" target="_blank">Take It Easy</a>).<br />
<br />
My main aim is to provide a visual explanation of why this is so. But first I would like to prove the result algebraically...<br />
<br />
The first few equations are reminders of some well-known mathematical results which we will need.<br />
<br />
<hr />
<!-- ================================================== -->
<!-- 1) -->
If f is a function defined on the numbers 0, 1, ... n, then
<br />
<br />
$$ \sum_{{i}={1}}^{n}[f(i)-f(i-1)] = f(n)-f(0) \tag{1}$$
<br />
This is true because if you expand out the summation, all the terms except f(n) and f(0) cancel out:<br />
$$ [f(n)-f(n-1)] + [f(n-1)-f(n-2)]+...+[f(2)-f(1)] + [f(1)-f(0)] $$
<br />
<br />
<hr />
<!-- ================================================== -->
<!-- 2) -->
Recall the formula for the triangular number series:
$$ \sum_{{i}={1}}^{n}i= \frac{n \cdot \left(n+1\right)}{2} \tag{2} $$
<br />
<em>[You can easily prove this using equation 1. Just set $ f(i) = \frac{i(i+1)}{2} $. Then $ f(i) - f(i-1) = i $ .]</em>
<br />
<br />
<hr />
<!-- ================================================== -->
<!-- 3) -->
Expand:
$$ (n-1)^{3} = n^{3} -3n^{2} + 3n - 1 $$
Then rearrange the terms to give:
$$ n^{3} - (n-1)^{3} = 3n^{2}- 3n + 1 \tag{3} $$
<br />
<hr />
<!-- ================================================== -->
<!-- 4) -->
A hexagonal board can be seen as concentric rings of hexagonal tiles. Ring 1, the innermost "ring", simply contains the single hexagon at the centre of the board. Ring 2, just outside it, contains 6 hexagons. Ring 3 contains 12. Ring 4 contains 18, and so forth.<br />
<br />
So the number of hexagons in ring k is:<br />
$$
r(k)=\begin{cases}
1& \text{if k = 1,}\\
6(k-1)& \text{if k > 1}
\end{cases}
$$
<br />
<hr />
<!-- ================================================== -->
<!-- 5) -->
Hence the number of hexagons in all the rings from 1 to k is:<br />
<br />
$$
\begin{align*}
h(k) &= \sum_{{i}={1}}^{k}r(i) \\
&= r\left(1\right) + \sum_{{i}={2}}^{k}[6(i-1)] \\
&= 1 + 6 \cdot \sum_{{i}={1}}^{k-1}i & \text{by re-indexing the summation} \\
&= 1 + 6 \cdot \frac{ \left(k-1\right) \cdot k }{2} & \text{by equation (2)} \\
&= 1 + 3k^{2} - 3k \\
&= 3k^{2} - 3k + 1 \tag{4}
\end{align*}
$$
<br />
<hr />
<!-- ================================================== -->
<!-- 6) -->
Let $ f(k) = k^{3} $
<br />
<br />
Then:
<br />
$$
\begin{align*}
f(k) - f(k-1) &= k^{3} - (k-1)^{3} \\
&= 3k^{2} - 3k + 1 & \text{by equation (3) } \\
&= h(k) & \text{by equation (4) }
\end{align*}
$$
Hence:
$$ h(k) = f(k) - f(k-1) \tag{5} $$
<br />
<hr />
<!-- ================================================== -->
<!-- 7) -->
So the number of hexagonal tiles in a hexagonal pyramid with n layers is:<br />
<br />
$$
\begin{align*}
p(n)
&= \sum_{{k}={1}}^{n}h(k) \\
&= \sum_{{k}={1}}^{n}[f(k) - f(k-1)] & \text{by equation (5)} \\
&= f(n) - f(0) & \text{by equation (1)} \\
&= n^{3} - 0^{3} \\
&= n^{3}
\end{align*}
$$
<br />
<br />
Q.E.D.
<br />
<br />
<hr />
Andrew Tweddlehttp://www.blogger.com/profile/02458479910895055369noreply@blogger.com0