SOURCE

console 命令行工具 X clear

                    
>
console
//参数行列数量
var rows = 2;
var cols = 3;

//初始化参数行列数以及重新布局输入框
function init() {
  document.getElementById('rows').value = rows;
  document.getElementById('cols').value = cols;
  rebuildInputParams();
}

//参数行列数量调整
function dimChange() {
  rows = document.getElementById('rows').value;
  cols = document.getElementById('cols').value;
  rebuildInputParams();
}

//按照分组参数重设参数输入框
function rebuildInputParams() {
  var paramsDiv = document.getElementById('params');
  paramsDiv.innerHTML = "";
  for (r = 0; r < rows; r++) {
    for (c = 0; c < cols; c++) {
      var input = document.createElement('input');
      input.id = (r + 1) * 10 + c + 1;
      input.type = "Number";
      paramsDiv.appendChild(input);
    }
    paramsDiv.innerHTML += '<br><br>';
  }
}

//运算符,支持一元一次系数运算
function add(a, b) {
  if (Array.isArray(a)) {
    return [a[0] + b, a[1]];
  }
  if (Array.isArray(b)) {
    return [a + b[0], b[1]];
  }
  return a + b;
}

function minus(a, b) {
  if (Array.isArray(a)) {
    return [a[0] - b, a[1]];
  }
  if (Array.isArray(b)) {
    return [a - b[0], b[1]];
  }
  return a - b;
}

function multiply(a, b) {
  if (Array.isArray(a)) {
    return [a[0] * b, a[1] * b];
  }
  if (Array.isArray(b)) {
    return [a * b[0], a * b[1]];
  }
  return a * b;
}

function divide(a, b) {
  if (Array.isArray(a)) {
    if (b !== 0) {
      return [a[0] / b, a[1] / b];
    } else {
      return null;
    }
  }
  if (Array.isArray(b)) {
    if (b[0] !== 0 && b[1] !== 0) {
      return [a / b[0], a / b[1]];
    } else {
      return null;
    }
  }

  if (b !== 0) {
    return a / b;
  } else {
    return null;
  }
}
//**************************************
//运算符数组,以及对应的显示符号和优先级
var operations = [{
  func: add,
  symbo: '+',
  priority: 1
},
{
  func: minus,
  symbo: '-',
  priority: 1
},
{
  func: multiply,
  symbo: '×',
  priority: 2
},
{
  func: divide,
  symbo: '÷',
  priority: 2
}];

//行和列的运算全排列
var colOperations, rowOperations;
function createOperationIndexList() {
  colOperations = permutation(operations.length, cols - 1);
  rowOperations = permutation(operations.length, rows - 1);
}

//行和列的参数全排列
var colParams, rowParams;
function createParamIndexList() {
  colParams = permutation(cols);
  rowParams = permutation(rows);
}

//用户输入的参数
var params = [];
//要求解的参数位置
var target = []; //row, column, solved
//获得参数数组
function getParamsData() {
  params = [];
  for (r = 0; r < rows; r++) {
    row = [];
    for (c = 0; c < cols; c++) {
      var input = document.getElementById((r + 1) * 10 + c + 1);
      if (input.value.trim() === "") {
        row.push(null);
        target.push([r, c, false]);
      } else {
        row.push(Number(input.value));
      }
    }
    params.push(row);
  }
}

//生成系数全排列,有重复。
function permutation(indexLength, selected) {
  selected = selected || indexLength;
  var permutations = [];
  var p = [];
  for (i = 0; i < Math.pow(indexLength, selected); i++) {
    p = convertBase(i, indexLength);
    while (p.length < selected) {
      p.push(0);
    }
    permutations.push(p);
  }
  return permutations;
}

//将任意数字转换为以 base 底的数字数组
function convertBase(num, base) {
  var ct = [];
  var quotient, remainder;
  while (true) {
    quotient = parseInt(num / base);
    remainder = num % base;
    ct.push(remainder);
    if (quotient > 0) {
      num = quotient;
    } else {
      break;
    }
  }
  return ct;
}

//检查数组是否有重复的元素,运算符可以全排列,参数数据只能全组合
function isArrayRepeat(array) {
  var arr = array;
  for (i = 0; i < arr.length - 1; i++) {
    for (j = i + 1; j < arr.length; j++) {
      if (arr[i] === arr[j]) {
        return true;
      }
    }
  }
  return false;
}

//开始
init();

function run() {
  testData();
  
  //获取参数
  getParamsData();
  //生成行列运算列表
  createOperationIndexList();
  //生成行列参数列表
  createParamIndexList();
  //分析并生成 patterns 列表
  analyse();
  //
  solve();
}

function testData() {
  //测试数据
  rows = 3;
  cols = 4;
  rebuildInputParams();
  
  document.getElementById('rows').value = rows;
  document.getElementById('cols').value = cols;
  
  document.getElementById('11').value = 2;
  document.getElementById('12').value = 7;
  document.getElementById('13').value = 10;
  document.getElementById('14').value = 24;

  document.getElementById('21').value = 3;
  document.getElementById('22').value = 5;
  document.getElementById('23').value = 6;
  document.getElementById('24').value = 21;

  document.getElementById('31').value = 4;
  document.getElementById('32').value = 6;
  document.getElementById('33').value = 5;
  document.getElementById('34').value = "";
}

//分析数据并生成规则数组
var patterns;
function analyse() {
  patterns = [];

  //按列计算数据规则
  console.log('按列分析数据规则:');
  //选定一个行数据排列方式,选定一个运算符排列方式
  for (rp = 0; rp < rowParams.length; rp++) {
    var paramsIndex = rowParams[rp]; //********选定一种数据排列方式********
    //参数不能重复代入计算,运算可以重复
    if (isArrayRepeat(paramsIndex)) {
      continue;
    }
    //运算排列列表
    for (ro = 0; ro < rowOperations.length; ro++) {
      var operationsIndex = rowOperations[ro]; //********选定一种运算排列方式********
      //逐列计算结果
      result = [];
      for (j = 0; j < cols; j++) {
        //跳过有空值的列
        var hasNull = false;
        for (i = 0; i < rows; i++) {
          if (params[i][j] === null) {
            hasNull = true;
            break;
          }
        }
        if (hasNull) {
          result.push(null);
          continue;
        }

        //按顺序提取该列数据
        var paramsData = [];
        for (i = 0; i < paramsIndex.length; i++) {
          paramsData.push(params[paramsIndex[i]][j]);
        }

        //将数据和运算整合计算
        var ans = evaluate(paramsData, operationsIndex, null);
        result.push(ans);
      }

      //检查结果是否为等差数列
      var as = true;
      var a = null;
      var b = null;
      //通过任意两个有效数据计算差
      for (i = 0; i < result.length - 1; i++) {
        if (result[i] !== null) {
          a = [result[i], i];
          for (j = i + 1; j < result.length; j++) {
            if (result[j] !== null) {
              b = [result[j], j];
              break;
            }
          }
          if (b) {
            break;
          }
        }
      }
      if (a && b) {
        d = (b[0].answer - a[0].answer) / (b[1] - a[1]);
        //逐个比对
        for (i = 0; i < result.length; i++) {
          if (result[i]) {
            if ((result[i].answer - a[0].answer) !== d * (i - a[1])) {
              as = false;
              break;
            }
          }
        }

        //如果是等差数列,说明 rp 的参数排列方式结合 ro 的运算组合可以产生一个规律
        if (as) {
          patterns.push({
            type: 'row',
            paramsIndex: paramsIndex,
            operationsIndex: operationsIndex,
            delta: d,
            result: result
          });
        }
      }
    }
  }
  document.getElementById("patternCount").innerText = patterns.length;
}

//使用已知模型对未知参数求解
function solve() {
  var slv = document.getElementById('solve');
  var inner = "";
  slv.innerHTML = "";
  //对未知参数逐个求解
  while (true) {
    for (t = 0; t < target.length; t++) {
      //continue if solved
      if (target[t][2] === true) {
        continue;
      }
      //添加求解标题
      inner += "<h4>求解空白参数,位置(" + 
        (target[t][0] + 1).toString() + ", " +
        (target[t][1] + 1).toString() + ")</h4>";
      
      inner += "<ul>"
      //同行同列只能有一个未知数才能求解
      var r = 0;
      var c = 0;
      //与当前未知参数同列的未知参数个数
      for (i = 0; i < rows; i++) {
        if (params[i][target[t][1]] === null) {
          r += 1;
        }
      }
      //与当前未知参数同行的未知参数个数
      for (j = 0; j < cols; j++) {
        if (params[target[t][0]][j] === null) {
          c += 1;
        }
      }

      //使用只有一个未知数的规律计算
      if (r === 1) {
        for (p = 0; p < patterns.length; p++) {
          var pattern = patterns[p];
          inner += "<li>规则" + (p + 1).toString() + ", 等差数列公差为 : " + pattern.delta.toString();
          for (i = 0; i < pattern.result.length; i++) {
            if (pattern.result[i]) {
              inner += "<p>" + pattern.result[i].equation + " = " + pattern.result[i].answer + "</p>";
            }
          }
          var answer = 0;
          for (i = 0; i < pattern.result.length; i++) {
            //找到一个有效运算式,根据等差数列计算当前未知数据列的目标
            if (pattern.result[i]) {
              answer = pattern.result[i].answer + pattern.delta * (target[t][1] - i);
              break;
            }
          }
          var paramsData = [];
          for (i = 0; i < rows; i++) {
            if (params[pattern.paramsIndex[i]][target[t][1]] === null) {
              paramsData.push([0, 1]);
            } else {
              paramsData.push(params[pattern.paramsIndex[i]][target[t][1]]);
            }
          }
          var ans = evaluate(paramsData, pattern.operationsIndex, answer);
          inner += "<p>" + ans.equation.replace("0,1", "x") + " = " + ans.answer.toString() + "</p>";
          inner += "<p class=solution>x = " + ans.x.toString() + "</p>";
          inner += "</li>";
        }
      }
      if (c === 1) {}
      
      inner += "</ul>";
    }
    break; //while
  }
  slv.innerHTML = inner;
}

function evaluate(data, operationsIndex, answer) {
  //默认不解未知数
  var r = data[0];
  var s = r.toString();
  for (i = 0; i < operationsIndex.length; i++) {
    r = operations[operationsIndex[i]].func(r, data[i + 1]);
    if (i > 0 && operations[operationsIndex[i - 1]].priority < operations[operationsIndex[i]].priority) {
      s = '(' + s + ')';
    }
    s += operations[operationsIndex[i]].symbo + data[i + 1].toString();
  }
  //求解模式
  if (answer) {
    return {
      equation: s,
      answer: answer,
      x: (answer - r[0]) / r[1]
    };
  } else { //计算模式
    return {
      equation: s,
      answer: r,
      x: null
    };
  }
}
<h1>
  小学生数字找规律
</h1>
<p><input id=rows type=number onchange=dimChange()>
  组,每组
  <input id=cols type=number onchange=dimChange()>
  个数字。
</p>
<div>
  ---------------------------------------------
  <p>
    已知数字,请按顺序填写。要求解的位置请留白。
  </p>
  <div id=params>
  </div>
  ---------------------------------------------
  <br>
  <button onclick=run()>
    开始计算
  </button>
  <p>
    根据已知数据,共计算出
    <span id=patternCount>
      20
    </span>
    条规则
  </p>
  <div id=solve>
    <h4>
      求解空白1
    </h4>
    <ul id=patterns>
      <li>
        No.1
        <p>
          1+1+1=3
        </p>
        <p>
          2+3+4=9
        </p>
        <p>
          4+5+x=15
        </p>
        <p>
          x=6
        </p>
      </li>
      <li>
        No.2
      </li>
    </ul>
  </div>
</div>
input {
  width: 50px;
  height: 1.5rem;
  font-size: 1rem;
  font-weight: bold;
  text-align: center;
  margin: 0 10px;
}

input.error {
  background-color: lightpink;
  color: darkred;
}

span#patternCount {
  color: darkred;
  font-weight: bold;
  font-size: 1.2rem;
}

p.solution {
  font-weight: bold;
  color: red;
}