From 2613622ef090077a47e24c0e9d0af178bacfdb8b Mon Sep 17 00:00:00 2001 From: gikf <60067306+gikf@users.noreply.github.com> Date: Mon, 14 Jun 2021 08:31:28 +0200 Subject: [PATCH] fix(curriculum): rework Project Euler 61 (#42397) --- .../problem-61-cyclical-figurate-numbers.md | 231 ++++++++++++++++-- 1 file changed, 210 insertions(+), 21 deletions(-) diff --git a/curriculum/challenges/english/10-coding-interview-prep/project-euler/problem-61-cyclical-figurate-numbers.md b/curriculum/challenges/english/10-coding-interview-prep/project-euler/problem-61-cyclical-figurate-numbers.md index 04ace60de8..3f1e157026 100644 --- a/curriculum/challenges/english/10-coding-interview-prep/project-euler/problem-61-cyclical-figurate-numbers.md +++ b/curriculum/challenges/english/10-coding-interview-prep/project-euler/problem-61-cyclical-figurate-numbers.md @@ -10,37 +10,53 @@ dashedName: problem-61-cyclical-figurate-numbers Triangle, square, pentagonal, hexagonal, heptagonal, and octagonal numbers are all figurate (polygonal) numbers and are generated by the following formulae: -| Type of Number | Formula | Sequence | -| -------------- | --------------------------------------------------------------------- | --------------------- | -| Triangle | P3,n=n(n+1)/2 | 1, 3, 6, 10, 15, ... | -| Square | P4,n=n2 | 1, 4, 9, 16, 25, ... | -| Pentagonal | P5,n=n(3n−1)/2 | 1, 5, 12, 22, 35, ... | -| Hexagonal | P6,n=n(2n−1) | 1, 6, 15, 28, 45, ... | -| Heptagonal | P7,n=n(5n−3)/2 | 1, 7, 18, 34, 55, ... | -| Octagonal | P8,n=n(3n−2) | 1, 8, 21, 40, 65, ... | +| Type of Number | Formula | Sequence | +| -------------- | ---------------------------- | --------------------- | +| Triangle | $P_3(n) = \frac{n(n+1)}{2}$ | 1, 3, 6, 10, 15, ... | +| Square | $P_4(n) = n^2$ | 1, 4, 9, 16, 25, ... | +| Pentagonal | $P_5(n) = \frac{n(3n−1)}2$ | 1, 5, 12, 22, 35, ... | +| Hexagonal | $P_6(n) = n(2n−1)$ | 1, 6, 15, 28, 45, ... | +| Heptagonal | $P_7(n) = \frac{n(5n−3)}{2}$ | 1, 7, 18, 34, 55, ... | +| Octagonal | $P_8(n) = n(3n−2)$ | 1, 8, 21, 40, 65, ... | The ordered set of three 4-digit numbers: 8128, 2882, 8281, has three interesting properties. -
    -
  1. The set is cyclic, in that the last two digits of each number is the first two digits of the next number (including the last number with the first).
  2. -
  3. Each polygonal type: triangle (P3,127 = 8128), square (P4,91 = 8281), and pentagonal (P5,44 = 2882), is represented by a different number in the set.
  4. -
  5. This is the only set of 4-digit numbers with this property.
  6. -
+1. The set is cyclic, in that the last two digits of each number is the first two digits of the next number (including the last number with the first). +2. Each polygonal type: triangle ($P_3(127) = 8128$), square ($P_4(91) = 8281$), and pentagonal ($P_5(44) = 2882$), is represented by a different number in the set. +3. This is the only set of 4-digit numbers with this property. -Find the sum of the only ordered set of six cyclic 4-digit numbers for which each polygonal type: triangle, square, pentagonal, hexagonal, heptagonal, and octagonal, is represented by a different number in the set. +Find the sum of all numbers in ordered sets of `n` cyclic 4-digit numbers for which each of the $P_3$ to $P_{n + 2}$ polygonal types, is represented by a different number in the set. # --hints-- -`cyclicalFigurateNums()` should return a number. +`cyclicalFigurateNums(3)` should return a number. ```js -assert(typeof cyclicalFigurateNums() === 'number'); +assert(typeof cyclicalFigurateNums(3) === 'number'); ``` -`cyclicalFigurateNums()` should return 28684. +`cyclicalFigurateNums(3)` should return `19291`. ```js -assert.strictEqual(cyclicalFigurateNums(), 28684); +assert.strictEqual(cyclicalFigurateNums(3), 19291); +``` + +`cyclicalFigurateNums(4)` should return `28684`. + +```js +assert.strictEqual(cyclicalFigurateNums(4), 28684); +``` + +`cyclicalFigurateNums(5)` should return `76255`. + +```js +assert.strictEqual(cyclicalFigurateNums(5), 76255); +``` + +`cyclicalFigurateNums(6)` should return `28684`. + +```js +assert.strictEqual(cyclicalFigurateNums(6), 28684); ``` # --seed-- @@ -48,16 +64,189 @@ assert.strictEqual(cyclicalFigurateNums(), 28684); ## --seed-contents-- ```js -function cyclicalFigurateNums() { +function cyclicalFigurateNums(n) { return true; } -cyclicalFigurateNums(); +cyclicalFigurateNums(3); ``` # --solutions-- ```js -// solution required +function cyclicalFigurateNums(n) { + function getChains(chain, n, numberTypes, numsExcludingLastNeededType) { + if (chain.length === n) { + return [chain]; + } + + const nextNumbers = getNextNumbersInChain( + chain[chain.length - 1], + numsExcludingLastNeededType + ); + + const chains = []; + for (let j = 0; j < nextNumbers.length; j++) { + const nextNumber = nextNumbers[j]; + if (chain.indexOf(nextNumber) === -1) { + const nextChain = [...chain, nextNumber]; + chains.push( + ...getChains(nextChain, n, numberTypes, numsExcludingLastNeededType) + ); + } + } + return chains; + } + + function getNextNumbersInChain(num, numsExcludingLastNeededType) { + const results = []; + const beginning = num % 100; + numsExcludingLastNeededType.forEach(number => { + if (Math.floor(number / 100) === beginning) { + results.push(number); + } + }); + return results; + } + + function fillNumberTypes(n, numberTypes, numsExcludingLastNeededType) { + const [, lastTypeCheck, lastTypeArr] = numberTypes[n - 1]; + + for (let i = 1000; i <= 9999; i++) { + for (let j = 0; j < n - 1; j++) { + const [, typeCheck, typeArr] = numberTypes[j]; + if (typeCheck(i)) { + typeArr.push(i); + numsExcludingLastNeededType.add(i); + } + } + + if (lastTypeCheck(i)) { + lastTypeArr.push(i); + } + } + } + + function isCyclicalChain(chain, n, numberTypes) { + const numberTypesInChain = getNumberTypesInChain(chain, numberTypes); + + if (!isChainAllowed(numberTypesInChain, n)) { + return false; + } + + const isChainCyclic = + Math.floor(chain[0] / 100) === chain[chain.length - 1] % 100; + return isChainCyclic; + } + + function getNumberTypesInChain(chain, numberTypes) { + const numbersInChain = {}; + for (let i = 0; i < numberTypes.length; i++) { + const numberTypeName = numberTypes[i][0]; + numbersInChain[numberTypeName] = []; + } + + for (let i = 0; i < chain.length; i++) { + for (let j = 0; j < n; j++) { + const [typeName, , typeNumbers] = numberTypes[j]; + const typeNumbersInChain = numbersInChain[typeName]; + if (typeNumbers.indexOf(chain[i]) !== -1) { + typeNumbersInChain.push(chain[i]); + } + } + } + return numbersInChain; + } + + function isChainAllowed(numberTypesInChain, n) { + for (let i = 0; i < n; i++) { + const typeName = numberTypes[i][0]; + const isNumberWithTypeInChain = numberTypesInChain[typeName].length > 0; + if (!isNumberWithTypeInChain) { + return false; + } + + for (let j = i + 1; j < n; j++) { + const otherTypeName = numberTypes[j][0]; + if ( + isNumberRepeatedAsOnlyNumberInTwoTypes( + numberTypesInChain[typeName], + numberTypesInChain[otherTypeName] + ) + ) { + return false; + } + } + } + return true; + } + + function isNumberRepeatedAsOnlyNumberInTwoTypes( + typeNumbers, + otherTypeNumbers + ) { + return ( + typeNumbers.length === 1 && + otherTypeNumbers.length === 1 && + typeNumbers[0] === otherTypeNumbers[0] + ); + } + + function isTriangle(num) { + return ((8 * num + 1) ** 0.5 - 1) % 2 === 0; + } + + function isSquare(num) { + return num ** 0.5 === parseInt(num ** 0.5, 10); + } + + function isPentagonal(num) { + return ((24 * num + 1) ** 0.5 + 1) % 6 === 0; + } + + function isHexagonal(num) { + return ((8 * num + 1) ** 0.5 + 1) % 4 === 0; + } + + function isHeptagonal(num) { + return ((40 * num + 9) ** 0.5 + 3) % 10 === 0; + } + + function isOctagonal(num) { + return ((3 * num + 1) ** 0.5 + 1) % 3 === 0; + } + + const numberTypes = [ + ['triangle', isTriangle, []], + ['square', isSquare, []], + ['pentagonal', isPentagonal, []], + ['hexagonal', isHexagonal, []], + ['heptagonal', isHeptagonal, []], + ['octagonal', isOctagonal, []] + ]; + const numsExcludingLastNeededType = new Set(); + fillNumberTypes(n, numberTypes, numsExcludingLastNeededType); + + const nNumberChains = []; + const [, , lastType] = numberTypes[n - 1]; + for (let i = 0; i < lastType.length; i++) { + const startOfChain = lastType[i]; + nNumberChains.push( + ...getChains([startOfChain], n, numberTypes, numsExcludingLastNeededType) + ); + } + + const cyclicalChains = nNumberChains.filter(chain => + isCyclicalChain(chain, n, numberTypes) + ); + + let sum = 0; + for (let i = 0; i < cyclicalChains.length; i++) { + for (let j = 0; j < cyclicalChains[0].length; j++) { + sum += cyclicalChains[i][j]; + } + } + return sum; +} ```