Javascript开发不同尺寸幻方报错排查及通用生成校验算法求助
问题原因与修复方案
1. 类型错误问题修复
报错来自两个核心问题:
- 你的HTML代码中没有定义
id="time"、id="select"、id="attempting"的元素,页面加载后定时器直接给不存在的元素设置textContent,直接触发未定义错误 - 你仅在页面初始化时查询了一次
.box元素,后续调用creategrid动态生成的新格子没有同步更新到window.boxes变量中,赋值时也会触发未定义错误
修复方式:补充缺失的DOM元素,每次生成网格后重新查询.box集合,定时器放在DOM加载完成后再启动。
2. 通用幻方校验函数实现
n阶幻方的固定求和公式为 n * (n² + 1) / 2,不需要为每个尺寸写死校验逻辑,通用校验逻辑如下:
// 入参:扁平的幻方数组,阶数n function checkMagicSquare(arr, n) { const targetSum = n * (n * n + 1) / 2 // 校验每行 for (let i = 0; i < n; i++) { let rowSum = 0 for (let j = 0; j < n; j++) { rowSum += arr[i * n + j] } if (rowSum !== targetSum) return false } // 校验每列 for (let j = 0; j < n; j++) { let colSum = 0 for (let i = 0; i < n; i++) { colSum += arr[i * n + j] } if (colSum !== targetSum) return false } // 校验两条对角线 let diagonal1 = 0, diagonal2 = 0 for (let i = 0; i < n; i++) { diagonal1 += arr[i * n + i] diagonal2 += arr[i * n + (n - 1 - i)] } if (diagonal1 !== targetSum || diagonal2 !== targetSum) return false return true }
3. 多尺寸网格生成修复
你之前用charAt(0)取尺寸,只能适配个位数的阶数,而且没有处理参数类型转换,修改后的网格生成函数:
function creategrid(n) { boxparent.innerHTML = '' for (let i = 0; i < n; i++) { const rows = document.createElement('div'); rows.className = 'row'; for (let j = 0; j < n; j++) { const boxs = document.createElement('div'); boxs.className = 'box'; rows.appendChild(boxs); } boxparent.appendChild(rows); } // 生成完重新获取box集合 window.boxes = document.querySelectorAll(".box") }
额外优化提示:你现在用随机打乱数组暴力枚举的方式生成幻方,4阶以上的枚举量会暴涨,性能会非常差,建议替换成对应的幻方构造算法:奇数阶用罗伯法,双偶阶用对称交换法,单偶阶用斯特雷奇法,生成效率会提升几个数量级。
优化后完整可运行代码
HTML
<div>尝试次数:<span id="attempting">0</span></div> <div>耗时:<span id="time">0</span>ms</div> <div id="boxParent"></div> <form> <label>Choose a Size:</label> <select id="select" onchange='change()'> <option value="3">3 X 3</option> <option value="4">4 X 4</option> <option value="5">5 X 5</option> <option value="6">6 X 6</option> <option value="7">7 X 7</option> <option value="8">8 X 8</option> <option value="9">9 X 9</option> </select> </form>
CSS
.box { border: black 1px solid; background: white; opacity: 0.7; /* 适配大尺寸网格自动缩小格子 */ width: 40px; height: 40px; display: inline-flex; align-items: center; justify-content: center; } .row { display: block; width: 100%; }
JS
var numsarray = []; let timeDom = document.getElementById('time') let selectDom = document.getElementById('select') let attemptingDom = document.getElementById('attempting') var attempting = 0; var output = {}; var attempt = 0; let boxparent = document.getElementById('boxParent') let done = false; var interval = null; // 初始化生成3阶网格 creategrid(3) startTimer() generateMagic(3) function startTimer() { interval = setInterval(function() { attemptingDom.textContent = attempt; timeDom.textContent = parseFloat(timeDom.textContent)+1 },1) } function createnums (size) { const arrays = [] for( let i=1 ; i <= Math.pow(size,2) ; i++ ) { arrays.push(i); } return arrays; } let getnums = (size) => { const arrays = createnums(size); numsarray = []; var len = arrays.length; while( len > 0 ) { attempt+=1 var rans = Math.floor(Math.random() * len); numsarray.push(arrays[rans]); arrays.splice(rans, 1); len = len - 1; } return numsarray; } function creategrid(n) { boxparent.innerHTML = '' for (let i = 0; i < n; i++) { const rows = document.createElement('div'); rows.className = 'row'; for (let j = 0; j < n; j++) { const boxs = document.createElement('div'); boxs.className = 'box'; rows.appendChild(boxs); } boxparent.appendChild(rows); } window.boxes = document.querySelectorAll(".box") } function checkMagicSquare(arr, n) { const targetSum = n * (n * n + 1) / 2 // 校验行 for (let i = 0; i < n; i++) { let rowSum = 0 for (let j = 0; j < n; j++) { rowSum += arr[i * n + j] } if (rowSum !== targetSum) return false } // 校验列 for (let j = 0; j < n; j++) { let colSum = 0 for (let i = 0; i < n; i++) { colSum += arr[i * n + j] } if (colSum !== targetSum) return false } // 校验对角线 let d1 = 0, d2 = 0 for (let i = 0; i < n; i++) { d1 += arr[i * n + i] d2 += arr[i * n + (n - 1 - i)] } if (d1 !== targetSum || d2 !== targetSum) return false return true } function generateMagic(n) { done = false attempt = 0 timeDom.textContent = 0 clearInterval(interval) startTimer() while(!done) { output = getnums(n) if(checkMagicSquare(output, n)) { clearInterval(interval) for (let i = 0; i < output.length; i++) { window.boxes[i].textContent = output[i] } done = true break } } } // select切换回调 function change() { const size = Number(selectDom.value) creategrid(size) generateMagic(size) }
内容的提问来源于stack exchange,提问作者Shawn
相关产品推荐
相关产品推荐

