jane st
solution
- GCD
- 012345679
- middle row (solution)
- 283950617
possible approaches
- count down possible GCDs until one is found (picked this one)
- should be able to start at some # around 100MM and count down
- optimize for quickly ruling out that a GCD could be a solution
- lots of solutions for smaller numbers that we get to not worry about
- get valid sudoku solutions and then calculate gcds
- so many solutions, so many more with 10 possible combinations of used digits
things I have determined
gcd must generate 9 multiples
- must be distinct (otherwise doesn't solve column requirement of sudoku)
- must be between 12345678 and 987654321
- must not repeat digits
- must use the same set of 9 digits
some high numbers with > 9 valid multiples to test against
| num | multiples |
|---|---|
| 61728395 | 11 |
| 49382716 | 14 |
| 36984127 | 16 |
| 31759246 | 10 |
| 31745926 | 18 |
limits
- 987654321
- max without repeating digits 987654321
- 109739369
- 987654321 / 9 max gcd that could produce 9 distinct row numbers
- 061728395
- highest GCD to produce 9 distinct row numbers
- 014237856
- highest GCD to product 9 distinct row numbers that use the same digits (no 9)
- 012345679
- can place 2 rows
- 012345679
- can place 3 rows
- 012345679
- can place 9 rows
constants
const gridSize = 9; const fixedGridValues = [ [ null, null, null, null, null, null, null, '2', null], [ null, null, null, null, null, null, null, null, '5'], [ null, '2', null, null, null, null, null, null, null], [ null, null, '0', null, null, null, null, null, null], [ null, null, null, null, null, null, null, null, null], [ null, null, null, '2', null, null, null, null, null], [ null, null, null, null, '0', null, null, null, null], [ null, null, null, null, null, '2', null, null, null], [ null, null, null, null, null, null, '5', null, null], ]; // valid set can't be missing 0, 2, or 5 because of the given grid const validSets = [ new Set(['0', '2', '3', '4', '5', '6', '7', '8', '9']), new Set(['0', '1', '2', '4', '5', '6', '7', '8', '9']), new Set(['0', '1', '2', '3', '5', '6', '7', '8', '9']), new Set(['0', '1', '2', '3', '4', '5', '7', '8', '9']), new Set(['0', '1', '2', '3', '4', '5', '6', '8', '9']), new Set(['0', '1', '2', '3', '4', '5', '6', '7', '9']), new Set(['0', '1', '2', '3', '4', '5', '6', '7', '8' ]), ]; let lowestPossibleNumber = 12345678; let maxPossibleNumber = 987654321; const experimentallyDeterminedStart = maxPossibleNumber / 9;
functions
const setsEqual = (s0, s1) => s0.size === s1.size && [...s0].every((x) => s1.has(x));
function main() { for (let i = experimentallyDeterminedStart; i > 0; i--) { // write with process.stdout so that we can see where we are but it is immediately overwritten process.stdout.write('\r' + i); const validMultiples = getMultiplesOfGCDWithoutRepeatingDigits(i) if (validMultiples.length > 8) { console.log(); console.log(validMultiples); console.log(i + ' has ' + validMultiples.length + ' options'); const sortedValidMultiples = validMultiples .map(num => num.toString().padStart(9, '0')) .reduce((arr, m) => { const match = arr.find(e => setsEqual(e[0], new Set(m))); if (match !== undefined) { match[1].push(m); } return arr; }, validSets.map(s => [s, []])); console.log(sortedValidMultiples); sortedValidMultiples.forEach(m => { if (m[1].length > 8) { console.log(i + ' has ' + m + ' keyed options'); tryFillRows([], m[1], 0); } }); } } } main();
function tryFillRows(grid, rowNumbersAvailable, placingRow) { let possibleGrids = []; // if (placingRow === 5) { // console.log('got 4 rows'); // printSudokuGrid(grid); // } if (placingRow === 9) { printSudokuGrid(grid); process.exit(); return [grid]; } rowNumbersAvailable.forEach((rowOption, rowOptionIndex) => { let isValidPlacement = true; // check if conflicts with placed rows for (let column = 0; column < gridSize; column++) { const myColumnChar = rowOption[column]; for (let row = 0; row < placingRow; row++) { if (grid[row][column] === myColumnChar) { isValidPlacement = false; // console.log(grid); // console.log('cant place ' + rowOption); } } } // check if conflicts with fixed nums fixedGridValues[placingRow].forEach((fixedElement, fixedElementIndex) => { if (fixedElement !== null) { if (rowOption[fixedElementIndex] !== fixedElement) { isValidPlacement = false; // console.log(grid); // console.log('cant place ' + rowOption); } } }); // TODO check if conflicts with placed rows // if placingRow % 3 == 2 check squares if (isValidPlacement) { possibleGrids.concat( [ ...tryFillRows([...grid, rowOption.split("")], [...rowNumbersAvailable.slice(0, rowOptionIndex), ...rowNumbersAvailable.slice(rowOptionIndex + 1)], placingRow + 1) ] ); } }); return possibleGrids; }
function getMultiplesOfGCDWithoutRepeatingDigits(gcd) { // row would have to be 8 or 9 digits b/c only 1 leading zero let usableMultiples = []; let multiple = Math.ceil(lowestPossibleNumber / gcd) * gcd; while (multiple < maxPossibleNumber) { if (!numberHasRepeatedDigits(multiple)){ usableMultiples.push(multiple); } multiple += gcd; } return usableMultiples; }
function numberHasRepeatedDigits(num) { const str = num.toString(); const digits = new Set(str); return digits.size !== str.length; }
function printSudokuGrid(grid) { const separator = "+-------+-------+-------+"; for (let i = 0; i < grid.length; i++) { if (i % 3 === 0) console.log(separator); const row = grid[i] .map((val, idx) => (val === 0 ? "." : val)) // Use `.` for empty cells (0) .reduce((acc, val, idx) => { const groupSeparator = (idx + 1) % 3 === 0 ? " |" : ""; return acc + ` ${val}${groupSeparator}`; }, "|"); console.log(row); } console.log(separator); }
solution grid
|
3 9 5 0 6 1 7 2 8 |
0 6 1 7 2 8 3 9 5 |
7 2 8 3 9 5 0 6 1 |
|
9 5 0 2 8 3 6 1 7 |
6 1 7 9 5 0 2 8 3 |
2 8 3 6 1 7 9 5 0 |
|
8 3 9 5 0 6 1 7 2 |
5 0 6 1 7 2 8 3 9 |
1 7 2 8 3 9 5 0 6 |