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.
-
- - 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).
- - 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.
- - This is the only set of 4-digit numbers with this property.
-
+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;
+}
```