Permalink
Browse files

Add exercise: Fundamental theorem of arithmetic

Reviewers: emily

Reviewed By: emily

Differential Revision: http://phabricator.khanacademy.org/D82
  • Loading branch information...
1 parent 7374bcd commit 49acfef7fda2b856d5a5bddb3c3b07b40399e19b @beneater beneater committed May 25, 2012
Showing with 329 additions and 0 deletions.
  1. +268 −0 exercises/the_fundamental_theorem_of_arithmetic.html
  2. +61 −0 utils/interactive.js
View
268 exercises/the_fundamental_theorem_of_arithmetic.html
@@ -0,0 +1,268 @@
+<!DOCTYPE html>
+<html data-require="math math-format word-problems graphie interactive">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>The fundamental theorem of arithmetic</title>
+ <script src="../khan-exercise.js"></script>
+</head>
+<body>
+<div class="exercise">
+ <div class="problems">
+ <div>
+ <div class="vars">
+ <var id="PRIMES">[2, 3, 5, 7, 11, 13]</var>
+ <var id="NUMBER">randFromArray([
+ 4, 6, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 22, 24, 25, 26,
+ 27, 28, 30, 32, 33, 35, 36, 39, 40, 42, 44, 45, 48, 49, 50,
+ 52, 54, 55, 56, 60, 63, 65, 66, 70, 72, 75, 77, 78, 80, 81,
+ 84, 88, 90, 91, 96, 98, 99])
+ </var>
+ <var id="FACTORIZATION">getPrimeFactorization(NUMBER)</var>
+ <var id="EXPONENTS">_.reduce(FACTORIZATION, function(exponents, base) {
+ exponents[base] = exponents[base] + 1 || 1;
+ return exponents;
+ }, {})
+ </var>
+ <var id="HAS_EXPONENTS">_.reduce(_.values(EXPONENTS), function(hasExponents, num) {
+ return hasExponents || num !== 1;
+ }, false)
+ </var>
+ <var id="EXPONENT_HINT">_.map(EXPONENTS, function(num, prime) {
+ return cardinal(num) + " &lt;code&gt;" + prime + "&lt;/code&gt;" + (num === 1 ? "" : "s");
+ })
+ </var>
+ <var id="SOLUTION">_.reduce(PRIMES, function(factors, prime) {
+ if (EXPONENTS[prime] &gt; 1) {
+ factors.push(prime + "^" + EXPONENTS[prime]);
+ } else if (EXPONENTS[prime] === 1) {
+ factors.push(prime);
+ }
+ return factors;
+ }, []).join("\\cdot")
+ </var>
+ <var id="REMAINING">_.map(FACTORIZATION, function(factor, step){
+ var remain = NUMBER;
+ _.each(FACTORIZATION.slice(0, step), function(n) { remain /= n; })
+ return remain;
+ })
+ </var>
+ <var id="GRAPHIE">null</var>
+ </div>
+
+ <p class="question">
+ Find the prime factorization of <code><var>NUMBER</var></code>.
+ </p>
+
+ <div class="problem">
+ <p>
+ Use the arrows to change the exponent on each prime number below to see if you can find the
+ prime factorization.
+ </p>
+ <div class="graphie">
+ // Save a reference to KhanUtil.currentGraph.graph to workaround the issue
+ // of the custom validator depending on KhanUtil.currentGraph and
+ // KhanUtil.currentGraph changing because of the second graphie element
+ // in the hints.
+ GRAPHIE = graph;
+
+ init({
+ range: [[0, 10], [-11, 0]]
+ });
+ addMouseLayer();
+
+ graph.primes = [];
+
+ graph.computeTotal = function() {
+ return _.reduce(PRIMES, function(total, prime) {
+ return total * pow(prime, graph.primes[prime].exponent);
+ }, 1);
+ };
+
+ graph.totalLabel = label([7, -10], 1, "right", {fontSize: 25});
+ graph.updateTotal = function() {
+ this.totalLabel.remove();
+ this.totalLabel = label([7, -10], graph.computeTotal(), "right", {fontSize: 25});
+
+ var answerPreview = _.reduce(PRIMES, function(answer, prime) {
+ if (graph.primes[prime].exponent &gt; 1) {
+ answer.push(prime + "^" + graph.primes[prime].exponent);
+ } else if (graph.primes[prime].exponent === 1) {
+ answer.push(prime);
+ }
+ return answer;
+ }, []).join("\\cdot");
+ answerPreview += " = " + graph.computeTotal();
+
+ $("#answer-preview code").text(answerPreview);
+ MathJax.Hub.Queue(["Reprocess", MathJax.Hub, $("#answer-preview")[0]]);
+ };
+
+ _.each(PRIMES, function(prime, y) {
+ var yCoord = -y * 1.5 - 1;
+ graph.primes[prime] = {
+ exponent: 0,
+ baseLabel: label([2, yCoord], prime, "left", {fontSize: 25, color: "#aaa"}),
+ expLabel: label([2, yCoord + 0.3], 0, "center", {fontSize: 20, color: "#aaa"}),
+ expandLabel: label([3, yCoord], 1, "right", {fontSize: 20, color: "#aaa"}),
+ factorLabel: label([7, yCoord], 1, "right", {fontSize: 25, color: "#aaa"}),
+ upArrow: addArrowWidget({
+ coord: [2, yCoord + 0.7],
+ direction: "up"
+ }),
+ downArrow: addArrowWidget({
+ coord: [2, yCoord - 0.1],
+ direction: "down"
+ }),
+ update: function() {
+ this.expLabel.remove();
+ this.expandLabel.remove();
+ this.factorLabel.remove();
+
+ this.expLabel = label([2, yCoord + 0.3], this.exponent, "center", {fontSize: 20});
+ this.factorLabel = label([7, yCoord], pow(prime, this.exponent),
+ "right", {fontSize: 25});
+ if (this.exponent === 0) {
+ this.baseLabel.css({color: "#aaa"});
+ this.expLabel.css({color: "#aaa"});
+ this.expandLabel = label([3, yCoord], "1", "right", {fontSize: 20, color: "#aaa"});
+ this.factorLabel.css({color: "#aaa"});
+ } else {
+ this.baseLabel.css({color: "black"});
+ this.expandLabel = label([3, yCoord],
+ getPrimeFactorization(pow(prime, this.exponent)).join("\\cdot"),
+ "right", {fontSize: 20});
+ }
+
+ if (this.exponent &gt;= 5 || (prime &gt;= 11 &amp;&amp; this.exponent &gt;= 3)) {
+ this.upArrow.hide();
+ } else {
+ this.upArrow.show();
+ }
+
+ if (this.exponent &lt;= 0) {
+ this.downArrow.hide();
+ } else {
+ this.downArrow.show();
+ }
+ }
+ };
+ graph.primes[prime].upArrow.onClick = function() {
+ graph.primes[prime].exponent += 1;
+ graph.primes[prime].update();
+ graph.updateTotal();
+ };
+ graph.primes[prime].downArrow.onClick = function() {
+ graph.primes[prime].exponent -= 1;
+ graph.primes[prime].update();
+ graph.updateTotal();
+ };
+
+ label([2.5, yCoord], "=", "right", {color: "#ccc"});
+ label([6.5, yCoord], "=", "right", {color: "#ccc"});
+ graph.primes[prime].downArrow.hide();
+ });
+
+ line([0, -9.25], [8, -9.25]);
+ label([0, -9], "\\times", "above right", {fontSize: 25});
+ </div>
+ </div>
+
+ <div class="solution" data-type="custom">
+ <div class="instruction">
+ Click the orange arrows to change your answer.
+ <p id="answer-preview"><code>=1</code></p>
+ </div>
+ <div class="guess">GRAPHIE.computeTotal()</div>
+ <div class="validator-function">
+ if (guess === 1) {
+ return "";
+ }
+ return guess === NUMBER;
+ </div>
+ <div class="show-guess">
+ _.each(PRIMES, function(prime) {
+ GRAPHIE.primes[prime].exponent = 0;
+ });
+ _.each(getPrimeFactorization(guess), function(prime) {
+ GRAPHIE.primes[prime].exponent += 1;
+ });
+ _.each(PRIMES, function(prime) {
+ GRAPHIE.primes[prime].update();
+ });
+ GRAPHIE.updateTotal();
+ </div>
+ <div class="show-guess-solutionarea">
+ _.each(PRIMES, function(prime) {
+ GRAPHIE.primes[prime].exponent = 0;
+ });
+ _.each(getPrimeFactorization(guess), function(prime) {
+ GRAPHIE.primes[prime].exponent += 1;
+ });
+ GRAPHIE.updateTotal();
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="hints">
+ <div>
+ <div class="graphie" id="factor-tree" style="float: left;">
+ graph.cx = 0;
+ graph.y = 0;
+ graph.curr = NUMBER;
+ init({
+ range: [[-1, FACTORIZATION.length + 1], [-2 * FACTORIZATION.length - 0.5, 1]],
+ scale: [30, 30]
+ });
+ label([graph.cx + 1, graph.y], graph.curr);
+ </div>
+ <p style="height: 60px">We can use a factor tree to break <code><var>NUMBER</var></code> into its prime
+ factorization. Which of the prime numbers divides into <code><var>NUMBER</var></code>?</p>
+ </div>
+ <div data-each="FACTORIZATION.slice(0, -1) as I, FACTOR">
+ <div class="graphie" data-update="factor-tree">
+ path([[graph.cx + 1, graph.y - 0.5], [graph.cx, graph.y - 1.5]]);
+ path([[graph.cx + 1, graph.y - 0.5], [graph.cx + 2, graph.y - 1.5]]);
+ graph.y -= 2;
+ graph.cx += 1;
+ graph.curr = graph.curr / FACTOR;
+ label( [graph.cx - 1, graph.y], FACTOR, {color: BLUE} );
+ circle( [graph.cx - 1, graph.y], 0.5);
+ graph.lastLabel = label( [graph.cx + 1, graph.y], graph.curr );
+ </div>
+ <p style="height: 38px">
+ <code><var>REMAINING[I]</var></code> is divisible by <code class="hint_blue"><var>FACTOR</var></code>,
+ leaving us with <code><var>REMAINING[I] / FACTOR</var></code>.
+ </p>
+ </div>
+ <div>
+ <div class="graphie" data-update="factor-tree">
+ circle( [graph.cx + 1, graph.y], 0.5);
+ graph.lastLabel.remove();
+ label( [graph.cx + 1, graph.y], graph.curr, {color: BLUE} );
+ </div>
+ <p>
+ <code class="hint_blue"><var>FACTORIZATION[FACTORIZATION.length-1]</var></code>
+ is prime, so we're done factoring.
+ </p>
+ </div>
+ <div>
+ <p>
+ The prime factorization of <code><var>NUMBER</var></code> is:
+ </p>
+ <p><code class="hint_blue">
+ \qquad<var>FACTORIZATION.join("\\space\\color{black}{\\cdot}\\space")</var>
+ </code></p>
+ <div data-if="HAS_EXPONENTS">
+ <p>
+ Because there <var>FACTORIZATION[0] === FACTORIZATION[1] ? "are" : "is"</var>
+ <var>toSentence(EXPONENT_HINT)</var>, we can use exponents to write the prime factorization as:
+ </p>
+ <p><code>\qquad\blue{<var>SOLUTION</var>} = <var>NUMBER</var></code></p>
+ </div>
+ </div>
+ </div>
+
+</div>
+</body>
+</html>
View
61 utils/interactive.js
@@ -1123,6 +1123,67 @@ $.extend(KhanUtil, {
},
+ addArrowWidget: function(options) {
+ var arrowWidget = $.extend({
+ graph: KhanUtil.currentGraph,
+ direction: "up",
+ coord: [0, 0],
+ onClick: function() {}
+ }, options);
+ var graph = arrowWidget.graph;
+
+ if (arrowWidget.direction === "up") {
+ arrowWidget.visibleShape = graph.path([
+ [arrowWidget.coord[0], arrowWidget.coord[1] - 4 / graph.scale[1]],
+ [arrowWidget.coord[0] - 4 / graph.scale[0], arrowWidget.coord[1] - 4 / graph.scale[1]],
+ [arrowWidget.coord[0], arrowWidget.coord[1] + 4 / graph.scale[1]],
+ [arrowWidget.coord[0] + 4 / graph.scale[0], arrowWidget.coord[1] - 4 / graph.scale[1]],
+ [arrowWidget.coord[0], arrowWidget.coord[1] - 4 / graph.scale[1]]
+ ], { stroke: null, fill: KhanUtil.ORANGE });
+ } else if (arrowWidget.direction === "down") {
+ arrowWidget.visibleShape = graph.path([
+ [arrowWidget.coord[0], arrowWidget.coord[1] + 4 / graph.scale[1]],
+ [arrowWidget.coord[0] - 4 / graph.scale[0], arrowWidget.coord[1] + 4 / graph.scale[1]],
+ [arrowWidget.coord[0], arrowWidget.coord[1] - 4 / graph.scale[1]],
+ [arrowWidget.coord[0] + 4 / graph.scale[0], arrowWidget.coord[1] + 4 / graph.scale[1]],
+ [arrowWidget.coord[0], arrowWidget.coord[1] + 4 / graph.scale[1]]
+ ], { stroke: null, fill: KhanUtil.ORANGE });
+ }
+
+ arrowWidget.mouseTarget = graph.mouselayer.circle(
+ graph.scalePoint(arrowWidget.coord)[0], graph.scalePoint(arrowWidget.coord)[1], 15);
+ arrowWidget.mouseTarget.attr({fill: "#000", "opacity": 0.0});
+
+ $(arrowWidget.mouseTarget[0]).css("cursor", "pointer");
+ $(arrowWidget.mouseTarget[0]).bind("vmousedown vmouseover vmouseout", function(event) {
+ if (event.type === "vmouseover") {
+ arrowWidget.visibleShape.animate({ scale: 2, fill: KhanUtil.ORANGE }, 20);
+ } else if (event.type === "vmouseout") {
+ arrowWidget.visibleShape.animate({ scale: 1, fill: KhanUtil.ORANGE }, 20);
+ } else if (event.type === "vmousedown" && (event.which === 1 || event.which === 0)) {
+ if (!arrowWidget.hidden) {
+ arrowWidget.onClick();
+ }
+ return false;
+ }
+ });
+
+ arrowWidget.hide = function() {
+ arrowWidget.visibleShape.hide();
+ arrowWidget.hidden = true;
+ $(arrowWidget.mouseTarget[0]).css("cursor", "default");
+ };
+
+ arrowWidget.show = function() {
+ arrowWidget.visibleShape.show();
+ arrowWidget.hidden = false;
+ $(arrowWidget.mouseTarget[0]).css("cursor", "pointer");
+ };
+
+ return arrowWidget;
+ },
+
+
createSorter: function() {
var sorter = {};
var list;

0 comments on commit 49acfef

Please sign in to comment.