地图瓦片修改算法无限递归:成因排查与修复方案
问题分析与修复
核心错误点
错误的入口条件限制
propagateChanges函数中原本用rDist <3 && cDist <3 && (rDist + cDist <3)判断是否处理当前瓦片,这个条件基于初始调用的瓦片坐标(而非中心坐标)做距离限制,完全违背“远离中心才递增”的逻辑,导致大量应处理的瓦片被跳过,同时不该处理的瓦片被错误触发递归。相邻瓦片的中心距离判断复用当前值
处理相邻瓦片时,复用当前瓦片的centerCoords(是否在中心4步内),而非单独检查相邻瓦片的位置。这会导致当前瓦片不在中心范围内,但相邻瓦片在中心范围内时,错误触发相邻瓦片的递归调用。对当前瓦片的无意义递归
无论当前瓦片是否已达最大值4,只要不在中心范围内就会递归调用自身,导致瓦片到4之后仍会触发一次递归,使depth超过4并触发overflow标记。isFurtherFromAll函数的逻辑漏洞
原函数仅通过compared.r + compared.c ===0排除距离完全相同的情况,但当相邻瓦片在某一方向更远、另一方向更近但总曼哈顿距离相等时,会错误允许递归;同时未以曼哈顿距离总和判断是否更远,逻辑不严谨。
修复后的代码
修改后的JavaScript部分
const rows = document.getElementsByTagName('tr'); var tiles = []; for (var r = 0; r < rows.length; r++) { tiles[r] = []; for (var c = 0; c < rows[r].cells.length; c++) { tiles[r][c] = { r: r, c: c, tile: rows[r].cells[c] }; } } function getTile(r, c) { if (r > -1 && r < 4 && c > -1 && c < 5) { return tiles[r][c]; } } function propagateChanges(currentTile, depth) { if (depth > maxDepth) { maxDepth = depth; } // 超过最大允许深度,直接返回 if (depth > 4) { overflowed = true; return; } const val = Number(currentTile.tile.textContent); // 瓦片值不符合条件,直接返回 if (val >= 4 || val <= 0) { return; } const currentCenterCoords = isCenterWithin4Tiles(currentTile.r, currentTile.c); // 当前瓦片在中心4步内,不需要递增 if (currentCenterCoords) { return; } // 递增瓦片值 currentTile.tile.textContent = val + 1; const newVal = val + 1; // 只有当新值还没到4时,才继续递归当前瓦片 if (newVal < 4) { setTimeout(() => { propagateChanges(currentTile, depth + 1); }, 1000); } // 处理相邻瓦片 const adjacents = [ getTile(currentTile.r - 1, currentTile.c), getTile(currentTile.r + 1, currentTile.c), getTile(currentTile.r, currentTile.c - 1), getTile(currentTile.r, currentTile.c + 1) ]; for (let i = 0; i < adjacents.length; i++) { const adjTile = adjacents[i]; if (!adjTile) continue; const adjVal = Number(adjTile.tile.textContent); // 相邻瓦片值不符合条件,跳过 if (adjVal >= 4 || adjVal <= 0) continue; const adjCenterCoords = isCenterWithin4Tiles(adjTile.r, adjTile.c); // 相邻瓦片在中心4步内,跳过 if (adjCenterCoords) continue; // 检查相邻瓦片是否比当前瓦片离所有中心更远 if (isFurtherFromAll(adjTile, currentTile, currentCenterCoords)) { setTimeout(() => { propagateChanges(adjTile, depth + 1); }, 1000); } } } function locateCenter(currentTile, R, C, depth) { if (depth > maxDepth) { maxDepth = depth; } if (depth > 4) { overflowed = true; return; } const val = Number(currentTile.tile.textContent); const rDist = Math.abs(currentTile.r - R); const cDist = Math.abs(currentTile.c - C); if (val > 0 && (rDist < 4 && cDist < 4 && (rDist + cDist < 4))) { const adjacents = [ getTile(currentTile.r - 1, currentTile.c), getTile(currentTile.r + 1, currentTile.c), getTile(currentTile.r, currentTile.c - 1), getTile(currentTile.r, currentTile.c + 1) ]; let hasLowerNumberedNeighbor = false; for (let i = 0; i < adjacents.length; i++) { const adjTile = adjacents[i]; if (!adjTile) continue; const adjVal = Number(adjTile.tile.textContent); if (adjVal === 0) { return; } else if (adjVal < 4 && adjVal < val) { hasLowerNumberedNeighbor = true; locateCenter(adjTile, R, C, depth + 1); } } if (!hasLowerNumberedNeighbor) { propagateChanges(currentTile, 0); } } } function isCenterWithin4Tiles(r, c) { const coords = []; // 检查曼哈顿距离<=3的区域(即4步以内) for (let rAway = -3; rAway <= 3; rAway++) { for (let cAway = -3; cAway <= 3; cAway++) { if (Math.abs(rAway) + Math.abs(cAway) > 3) continue; const targetR = r + rAway; const targetC = c + cAway; if (targetR < 0 || targetR >= 4 || targetC < 0 || targetC >=5) continue; const val = tiles[targetR][targetC].tile.textContent; if (val === '0') { coords.push({ r: targetR, c: targetC }); } } } return coords.length > 0 ? coords : false; } function isFurtherFromAll(next, current, comparisons) { // 遍历所有中心,检查next的曼哈顿距离是否都比current远 for (const center of comparisons) { const currentDist = Math.abs(current.r - center.r) + Math.abs(current.c - center.c); const nextDist = Math.abs(next.r - center.r) + Math.abs(next.c - center.c); // 如果next距离任何一个中心更近或相等,则返回false if (nextDist <= currentDist) { return false; } } return true; } var maxDepth = 0; var overflowed = false; function progressReport() { console.log("Maximum depth reached in recursive function: " + maxDepth); if (overflowed) { console.warn("OVERFLOW"); } else { setTimeout(progressReport, 3000); } } progressReport(); var clicked = false; document.getElementById("clickHere").addEventListener('click', function() { if (!clicked) { clicked = true; tiles[0][4].tile.textContent = '4'; locateCenter(tiles[0][4], 0, 4, 0); } });
CSS部分
table { font-size: 18px; } table td { width: 20px; height: 20px; }
HTML部分
<body> <table> <tr> <td>2</td> <td>2</td> <td>2</td> <td>1</td> <td id="clickHere">0</td> </tr> <tr> <td>1</td> <td>1</td> <td>2</td> <td>2</td> <td>1</td> </tr> <tr> <td>0</td> <td>0</td> <td>1</td> <td>2</td> <td>2</td> </tr> <tr> <td>0</td> <td>0</td> <td>1</td> <td>2</td> <td>3</td> </tr> </table> </body>
修复说明
- 移除错误的入口条件:
propagateChanges现在直接检查瓦片值和中心距离,不再依赖初始调用坐标的限制。 - 独立检查相邻瓦片的中心距离:每个相邻瓦片都单独调用
isCenterWithin4Tiles,确保只有不在中心范围内的瓦片才会被处理。 - 终止当前瓦片的递归条件:只有当瓦片递增后的值小于4时,才继续递归当前瓦片,避免无意义的调用。
- 简化
isFurtherFromAll逻辑:直接比较曼哈顿距离的总和,确保相邻瓦片确实比当前瓦片离所有中心更远,彻底避免循环递归。
内容的提问来源于stack exchange,提问作者Quack E. Duck
相关产品推荐
相关产品推荐

