检查我的矩阵是否是幻方 javascript

Check if my matrix is a magic square javascript

我们得到了一项作业,要使用输入框创建一个 3x3 矩阵,并编写代码来检查给定数字相加是否正确,从而构成一个幻方。没有提示框,让我们使用数组来完成任务。在使用 "document.myform....value" 方法之前,我已经使用输入框获取值,但我不知道如何使用数组来实现它。

这是我目前拥有的:

function myArray() {
  var matrix = new Array[];
  for (var i = 0; i < 3; i++) {
    matrix[i] = new Array(3)
  }
  enterValues();
}

function enterNum() {
  for (var row = 0; row < 3; row++) {
    for (var col = 0; col < 3; col++)
      matrix[row][col].innerHTML;
  }
}

function magicSquare() {
  var magSqu = false;
  var sum = 0;
  for (var col = 0; col < 3; col++) {
    sum = sum + matrix[0][col];
  }
  var rowSum = 0;
  for (var i = 1; i < 3; i++) {
    for (var j = 0; j < 3; j++) {
      rowSum = rowSum + matrix[i][j];
      if (rowSum != sum) {
        magSqu = false;
        break;
      }
    }
  }
  var sum_diagonal = 0;
  if (magSqu) {
    for (var i = 0; i < 3; i++)
      for (var j = 0; j < 3; j++)
        if (i == j)
          sum_diagonal += matrix[i][j];
    if (sum_diagonal != sum) {
      magSqu = false;
    }
  }
  if (magSqu) {
    sum_diagonal = 0;
    for (var i = 0; i < 3; i++)
      for (var j = 0; j < 3; j++)
        if ((i + j) == 2)
          sum_diagonal += matrix[i][j];
    if (sum_diagonal != sum) {
      magSqu = false;
    }
  }
  if (magSqu == true) {
    document.getElementById("result").innerHTML = "This is a magic square.";
  } else {
    document.getElementById("result").innerHTML = "This is not a magic square.";
  }
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>Magic Square</title>
  <link rel="stylesheet" type="text/css" href="style.css" />

</head>

<body>
  <table border='1'>
    <tbody>
      <tr>
        <td id="r1c1"><input type="text" name="inp11"></td>
        <td id="r1c2"><input type="text" name="inp12"></td>
        <td id="r1c3"><input type="text" name="inp13"></td>
      </tr>
      <tr>
        <td id="r2c1"><input type="text" name="inp21"></td>
        <td id="r2c2"><input type="text" name="inp22"></td>
        <td id="r2c3"><input type="text" name="inp23"></td>
      </tr>
      <tr>
        <td id="r3c1"><input type="text" name="inp31"></td>
        <td id="r3c2"><input type="text" name="inp32"></td>
        <td id="r3c3"><input type="text" name="inp33"></td>
      </tr>
    </tbody>
  </table>
  <input type="submit" id="magicButton" value="Compute Score" onclick="magicSquare();">
  <div id="result"> </div>
</body>

</html>

我哪里出错了?

您错过的大部分内容是

  1. 将数组定义为[]或new Array(),这是@ggorlen
  2. 所说的
  3. 我没有使用 name,而是将其更改为 id,这样我们就可以获得该特定元素的值。
  4. 使用输入标签时,使用 value 属性 而不是 innerHTML
  5. 获取内部值
  6. 我们需要在初始加载时调用函数 myArray()
  7. 将矩阵变量移动到顶层,因为它被所有函数公用。
  8. 删除了提交类型的输入按钮,这是可选的,因为它有助于保留用于演示目的的输入。

希望这能解决您的问题!

var matrix = [];
myArray();
function myArray() {
  for (var i = 0; i < 3; i++) {
    matrix[i] = new Array(3).fill(1)
  }
  enterValues();
}

function enterValues() {
  for (var row = 0; row < 3; row++) {
    for (var col = 0; col < 3; col++) {
      document.getElementById(`inp` + (row+ 1) + (col+ 1)).value = matrix[row][col];
    }
  }
}

function enterNum() {
  for (var row = 0; row < 3; row++) {
    for (var col = 0; col < 3; col++)
      matrix[row][col] = document.getElementById(`inp` + (row+ 1) + (col+ 1)).value;
  }
}

function magicSquare() {
  enterNum();
  var magSqu = false;
  var sum = 0;
  for (var col = 0; col < 3; col++) {
    sum = sum + matrix[0][col];
  }
  var rowSum = 0;
  for (var i = 1; i < 3; i++) {
    for (var j = 0; j < 3; j++) {
      rowSum = rowSum + matrix[i][j];
      if (rowSum != sum) {
        magSqu = false;
        break;
      }
    }
  }
  var sum_diagonal = 0;
  if (magSqu) {
    for (var i = 0; i < 3; i++)
      for (var j = 0; j < 3; j++)
        if (i == j)
          sum_diagonal += matrix[i][j];
    if (sum_diagonal != sum) {
      magSqu = false;
    }
  }
  if (magSqu) {
    sum_diagonal = 0;
    for (var i = 0; i < 3; i++)
      for (var j = 0; j < 3; j++)
        if ((i + j) == 2)
          sum_diagonal += matrix[i][j];
    if (sum_diagonal != sum) {
      magSqu = false;
    }
  }
  if (magSqu == true) {
    document.getElementById("result").innerHTML = "This is a magic square.";
  } else {
    document.getElementById("result").innerHTML = "This is not a magic square.";
  }
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>Magic Square</title>
  <link rel="stylesheet" type="text/css" href="style.css" />

</head>

<body>
  <table border='1'>
    <tbody>
      <tr>
        <td id="r1c1"><input type="text" id="inp11"></td>
        <td id="r1c2"><input type="text" id="inp12"></td>
        <td id="r1c3"><input type="text" id="inp13"></td>
      </tr>
      <tr>
        <td id="r2c1"><input type="text" id="inp21"></td>
        <td id="r2c2"><input type="text" id="inp22"></td>
        <td id="r2c3"><input type="text" id="inp23"></td>
      </tr>
      <tr>
        <td id="r3c1"><input type="text" id="inp31"></td>
        <td id="r3c2"><input type="text" id="inp32"></td>
        <td id="r3c3"><input type="text" id="inp33"></td>
      </tr>
    </tbody>
  </table>
  <input type="button" id="magicButton" value="Compute Score" onclick="magicSquare();">
  <div id="result"> </div>
</body>

</html>

代码中有很多问题。我的一般建议是以非常小的块工作,一次解决一个问题。代码看起来好像是一口气写出来的,没有定期测试以确保它能正常工作。当你开始运行它的时候,问题的数量已经积累到难以调试的地步。

一些问题包括:

  • 缺乏与 DOM 的互动。使用 innerHTML 设置结果是一个好的开始,但是没有从用户那里获取数据的逻辑。使用表单、事件侦听器或查询选择器从文档中提取数据。在继续使用幻数逻辑(或硬编码数据并在继续 UI 之前弄清楚幻数逻辑)之前确保它有效。 UI 和幻数逻辑应该完全相互分离。
  • 作用域:myArray() 是您为存储矩阵而设置的函数(将代码分解为函数是正确的想法!),但它从未被调用过,也不会被调用 return 供其他函数使用的矩阵。尽管您已经很好地将代码分解为函数,但请务必使用参数和 return 值并进一步将 row/column/diagonal 检查分解为它们自己的函数。为函数和变量赋予描述性名称(尽管如此,您通常已经做得很好,myArray)。
  • matrix[row][col].innerHTML; 不做任何事情(使用 = 符号分配给 innerHTML 或在表达式中使用 属性),enterValues() 打电话。这似乎是一项正在进行的工作,但这可能是一个很好的功能,可以隔离并开始从 DOM.
  • 中提取数据。
  • 在所有块上使用大括号:

    for (var i = 0; i < 3; i++)
      for (var j = 0; j < 3; j++)
        if (i == j)
          sum_diagonal += matrix[i][j];
    if (sum_diagonal != sum) {
      magSqu = false;
    }
    

    是一场等待发生的灾难,尽管它在这里看起来确实是正确的。 sum_diagonal 应该是 sumDiagonal(或 diagonalSum)--JS 对所有变量使用驼峰式命名法(类 使用 PascalCase)。内部循环不是必需的。重写可能是:

    for (let i = 0; i < 3; i++) {
      sumDiagonal += matrix[i][i];
    }
    
    if (sumDiagonal !== sum) {
      magSqu = false;
    }
    
  • Hardcoding 3 在这里工作,但我会尝试概括逻辑以在任何 n.

  • 上工作
  • 使用 === 进行所有比较。这避免了由于意外的类型强制转换而导致的严重错误。
  • 您的幻方测试无法解释数字不明确的情况。这会错误地将全零的 3x3 矩阵视为幻方。我建议展平列表并使用 Set 检查每个元素是否唯一。
  • 另一个错误是下面的代码:

    var rowSum = 0;
    for (var i = 1; i < 3; i++) {
      for (var j = 0; j < 3; j++) {
        rowSum = rowSum + matrix[i][j];
        if (rowSum != sum) { // <-- this is premature!
          magSqu = false;
          break;
        }
      }
    }
    

    在上面的代码片段中,行求和只应在行求和完成后进行测试,而不是在求和期间。它会错误地报告错误。尝试:

    let rowSum = 0;
    
    for (let i = 1; i < 3; i++) {
      for (let j = 0; j < 3; j++) {
        rowSum += matrix[i][j];            
      }
    
      if (rowSum !== sum) {
        magSqu = false;
        break;
      }
    }
    

考虑到这一点,我更喜欢将任务分解为使用数组内置函数而不是显式循环执行迭代的小函数的解决方案:

const sum = a => a.reduce((a, e) => a + e);
const diag = m => m.map((row, i) => row[i]);
const rotate = m => 
  m.map((_, i) => m.map((_, j) => m[j][i]).reverse())
;
const isMagic = square => {
  const targetSum = square.length && sum(square[0]);
  const valid = a => sum(a) === targetSum;
  const rotated = rotate(square);  
  const flat = square.flat();
  return new Set(flat).size === flat.length &&
    square.every(valid) &&
    rotated.every(valid) &&
    valid(diag(square)) &&
    valid(diag(rotated))
};

[
  [
    [2,7,6],
    [9,5,1],
    [4,3,8]
  ],
  [
    [8,1,6],
    [3,5,7],
    [4,9,2]
  ],
].forEach(e => console.log(isMagic(e)));

这满足了幻方的所有要求:所有数字都应该是唯一的,每一行、每一列和对角线的总和必须是相同的数字。

使用置换函数,我可以对其进行测试以确保它找到给定大小的所有预期正方形。注意:这是一项繁重的计算,可能会使浏览器挂起片刻。

const sum = a => a.reduce((a, e) => a + e);
const diag = m => m.map((row, i) => row[i]);
const rotate = m => 
  m.map((e, i) => e.map((_, j) => m[j][i]).reverse())
;
const isMagic = square => {
  const targetSum = square.length && sum(square[0]);
  const valid = a => sum(a) === targetSum;
  const rotated = rotate(square);  
  const flat = square.flat();
  return new Set(flat).size === flat.length &&
    square.every(valid) &&
    rotated.every(valid) &&
    valid(diag(square)) &&
    valid(diag(rotated))
};

function *permute(a, i=0) {
  if (i >= a.length) {
    yield a.slice();
  }

  for (let j = i; j < a.length; j++) {
    [a[i], a[j]] = [a[j], a[i]];
    yield *permute(a, i + 1);
    [a[i], a[j]] = [a[j], a[i]];
  }
};

const unflatten = a => a.reduce((acc, e, i) => {
  if (i % Math.sqrt(a.length) === 0) {
    acc.push([]);
  }
  
  acc[acc.length-1].push(e);
  return acc;
}, []);

const squares = [...permute(
    Array(9).fill().map((_, i) => i + 1)
  )].reduce((a, e) => {
    const candidate = unflatten(e);

    if (isMagic(candidate)) {
      a.push(candidate);
    }

    return a;
  }, [])
;

console.log(squares.length, "magic squares of size 3:");

for (const square of squares) {
  console.log(square.map(e => e.join(" ")).join("\n"));
}