# Khan/khan-exercises

Add exercise: Fundamental theorem of arithmetic

Reviewers: emily

Reviewed By: emily

Differential Revision: http://phabricator.khanacademy.org/D82
1 parent 7374bcd commit 49acfef7fda2b856d5a5bddb3c3b07b40399e19b 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
268 exercises/the_fundamental_theorem_of_arithmetic.html
 @@ -0,0 +1,268 @@ + + + + + The fundamental theorem of arithmetic + + + +
+
+
+
+ [2, 3, 5, 7, 11, 13] + 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]) + + getPrimeFactorization(NUMBER) + _.reduce(FACTORIZATION, function(exponents, base) { + exponents[base] = exponents[base] + 1 || 1; + return exponents; + }, {}) + + _.reduce(_.values(EXPONENTS), function(hasExponents, num) { + return hasExponents || num !== 1; + }, false) + + _.map(EXPONENTS, function(num, prime) { + return cardinal(num) + " <code>" + prime + "</code>" + (num === 1 ? "" : "s"); + }) + + _.reduce(PRIMES, function(factors, prime) { + if (EXPONENTS[prime] > 1) { + factors.push(prime + "^" + EXPONENTS[prime]); + } else if (EXPONENTS[prime] === 1) { + factors.push(prime); + } + return factors; + }, []).join("\\cdot") + + _.map(FACTORIZATION, function(factor, step){ + var remain = NUMBER; + _.each(FACTORIZATION.slice(0, step), function(n) { remain /= n; }) + return remain; + }) + + null +
+ +

+ Find the prime factorization of NUMBER. +

+ +
+

+ Use the arrows to change the exponent on each prime number below to see if you can find the + prime factorization. +

+
+ // 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 > 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 >= 5 || (prime >= 11 && this.exponent >= 3)) { + this.upArrow.hide(); + } else { + this.upArrow.show(); + } + + if (this.exponent <= 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}); +
+
+ +
+

=1

+
+
GRAPHIE.computeTotal()
+
+ if (guess === 1) { + return ""; + } + return guess === NUMBER; +
+
+ _.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(); +
+
+ _.each(PRIMES, function(prime) { + GRAPHIE.primes[prime].exponent = 0; + }); + _.each(getPrimeFactorization(guess), function(prime) { + GRAPHIE.primes[prime].exponent += 1; + }); + GRAPHIE.updateTotal(); +
+
+
+
+ +
+
+
+ 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); +
+

We can use a factor tree to break NUMBER into its prime + factorization. Which of the prime numbers divides into NUMBER?

+
+
+
+ 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 ); +
+

+ REMAINING[I] is divisible by FACTOR, + leaving us with REMAINING[I] / FACTOR. +

+
+
+
+ circle( [graph.cx + 1, graph.y], 0.5); + graph.lastLabel.remove(); + label( [graph.cx + 1, graph.y], graph.curr, {color: BLUE} ); +
+

+ FACTORIZATION[FACTORIZATION.length-1] + is prime, so we're done factoring. +

+
+
+

+ The prime factorization of NUMBER is: +

+

+
+

+ Because there FACTORIZATION[0] === FACTORIZATION[1] ? "are" : "is" + toSentence(EXPONENT_HINT), we can use exponents to write the prime factorization as: +

+

 @@ -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;