SOURCE

console 命令行工具 X clear

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

function tutorial1() {
  //测试数据
  rows = 3;
  cols = 5;
  rebuildInputParams();
  
  document.getElementById('rows').value = rows;
  document.getElementById('cols').value = cols;
  
  document.getElementById('11').value = 2;
  document.getElementById('12').value = 5;
  document.getElementById('13').value = 6;
  document.getElementById('14').value = 7;
  document.getElementById('15').value = 11;

  document.getElementById('21').value = 8;
  document.getElementById('22').value = 10;
  document.getElementById('23').value = "";
  document.getElementById('24').value = 4;
  document.getElementById('25').value = 18;
  
  document.getElementById('31').value = 6;
  document.getElementById('32').value = 10;
  document.getElementById('33').value = 12;
  document.getElementById('34').value = 9;
  document.getElementById('35').value = 20;
  
  document.getElementById('tutorial').src = "http://files.eduuu.com/img/2016/09/20/100257_57e098d116164.jpg";
}

function tutorial2() {
  //测试数据
  rows = 5;
  cols = 4;
  rebuildInputParams();
  
  document.getElementById('rows').value = rows;
  document.getElementById('cols').value = cols;
  
  document.getElementById('11').value = 1;
  document.getElementById('12').value = 5;
  document.getElementById('13').value = 30;
  document.getElementById('14').value = 6;

  document.getElementById('21').value = 2;
  document.getElementById('22').value = 7;
  document.getElementById('23').value = 63;
  document.getElementById('24').value = 9;
  
  document.getElementById('31').value = 3;
  document.getElementById('32').value = 9;
  document.getElementById('33').value = 108;
  document.getElementById('34').value = 12;
  
  document.getElementById('41').value = "";
  document.getElementById('42').value = "";
  document.getElementById('43').value = "";
  document.getElementById('44').value = "";
  
  document.getElementById('51').value = 5;
  document.getElementById('52').value = 13;
  document.getElementById('53').value = 234;
  document.getElementById('54').value = 18;
  
  document.getElementById('tutorial').src = "http://files.eduuu.com/img/2016/12/19/184856_5857bb18a0709.jpg";
}

//初始化参数行列数以及重新布局输入框
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 (let r = 0; r < rows; r++) {
    for (let 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>';
  }
	resetDOM();
}

function resetDOM() {
  document.getElementById('warning').style.display = "none";
  document.getElementById('msg').style.display = "none";
	document.getElementById('patternCount').innerHTML = "";
  document.getElementById('solve').innerHTML = "";
}

//开始
init();

function run() {
  //testData();
  
  //获取参数,params 二维数据,targets[行,列,解]
  getParamsData();
  if (rows < 4 && cols < 4) {
    //数据太少,请脑算找规律
    document.getElementById('warning').style.display = "block";
    return null;
  }
  //分析数据生成行列计算规则,生成 patterns 列表,包括行规则和列规则
  analyse();
  //逐个未知数套用行列规则
  solve();
}

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

//运算符,支持一元一次系数运算
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;
    }
  }
  //这里会变成 x ^ -1 要换一种解法
  if (Array.isArray(b)) {
    return null
    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 patterns;
function analyse() {
  patterns = [];
  //生成数据排列模式和运算排列模式
  var colParamsIndex = permutation(cols);
  var rowParamsIndex = permutation(rows);
  //n 个数据之间只能有 n - 1 个运算符
  var colOperationsIndex = permutation(operations.length, cols - 1)
  var rowOperationsIndex = permutation(operations.length, rows - 1);
  
  //行模式推算
  for (let cp = 0; cp < colParamsIndex.length; cp++) {
    if (isArrayRepeat(colParamsIndex[cp])) {
      continue; //数据不能重复排列,运算符可以
    } else {
      for (let co = 0; co < colOperationsIndex.length; co++) {
        var row = [];
        var result = [];
        for (let i = 1; i < rows; i++) {
					var ans = evaluate("row", i, colParamsIndex[cp], colOperationsIndex[co], null);
          if (ans === null) {
            row.push(null);
          } else {
            row.push(ans.answer);
          }
          result.push(ans);
        }
        var ap = isAP(row);
				if (ap !== null) {
          patterns.push({
            type: "row",
            paramsIndex: colParamsIndex[cp],
            operationsIndex: colOperationsIndex[co],
            result: result,
            ap: ap
          })
        }
      }
    }
  }
  //列模式推算
  for (let rp = 0; rp < rowParamsIndex.length; rp++) {
    if (isArrayRepeat(rowParamsIndex[rp])) {
      continue;
    } else {
      for (let ro = 0; ro < rowOperationsIndex.length; ro++) {
        var col = [];
        var result = [];
        for (let j = 0; j < cols; j++) {
          var ans = evaluate("col", j, rowParamsIndex[rp], rowOperationsIndex[ro], null);
          if (ans === null) {
            col.push(null);
          } else {
            col.push(ans.answer);
          }
          result.push(ans);
        }
        var ap = isAP(col);
        if (ap !== null) {
          patterns.push({
            type: "col",
            paramsIndex: rowParamsIndex[rp],
            operationsIndex: rowOperationsIndex[ro],
            result: result,
            ap: ap
          })
        }
      }
    }
  }
  document.getElementById('patternCount').innerText = patterns.length;
  document.getElementById('msg').style.display = 'block';
}

function solve() {
  var slv = document.getElementById('solve');
  var inner = "";
  slv.innerHTML = "";
  
  for (let p = 0; p < patterns.length; p++) {
    var pattern = patterns[p];
    
    //添加 DOM 结果列表
    inner += "<h4>规则" + (p + 1).toString() + " : 等差数列公差为 <span class=solution>" + pattern.ap[0].toString() + "</span></h4>";
    inner += "<ul>";
    for (let i = 0; i < pattern.result.length; i++) {
      if (pattern.result[i]) {
        inner += "<li>" + pattern.result[i].equation + " = " + pattern.result[i].answer + "</li>";
      }
    }

    for (let t = 0; t < targets.length; t++) {
      var target = targets[t];
      inner += "<p>未知数位置第 " + (target[0] + 1).toString() + " 行,第 " + (target[1] + 1).toString() + " 列</p>";
      inner += "<ul>";
      var index, ans;
      var r = 0;
      var c = 0;
      for (let i = 0; i < rows; i++) {
        if (params[i][target[1]] === null) {
          r += 1;
        }
      }
      for (let j = 0; j < cols; j++) {
        if (params[target[0]][j] === null) {
          c += 1;
        }
      }
      if (pattern.type === "row" && c === 1) {
        index = target[0];
      } else if (pattern.type === "col" && r === 1) {
        index = target[1];
      } else {
        index = null;
      }
      if (index !== null) {
      	ans = evaluate(pattern.type, index, pattern.paramsIndex, pattern.operationsIndex, pattern.ap[1][index]);
        console.log(ans);
        inner += "<li>" + ans.equation.replace('0,1', '<span class=solution>X</span>') + " = " + ans.answer.toString() + "</li>";
        inner += "<li class=solution> X = " + ans.x.toString() + "</li>";
      }
      inner += "</ul>";
    }
    inner += "</ul>";
  }
  slv.innerHTML = inner;
  //TODO 升级后,可以先求解行列中含单未知数的数据,再利用先求出的数据的规则求解多未知数的数据
}

//生成系数全排列,有重复。
function permutation(indexLength, selected) {
  selected = selected || indexLength;
  var permutations = [];
  var p = [];
  for (let 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 (let i = 0; i < arr.length - 1; i++) {
    for (let j = i + 1; j < arr.length; j++) {
      if (arr[i] === arr[j]) {
        return true;
      }
    }
  }
  return false;
}

function evaluate(type, index, paramsIndex, operationsIndex, answer) {
  //默认不解未知数
  var i, j;
  if (type === "row") {
    i = index;
    //检查是否有空值
    if (answer === null) {
      for (let j = 0; j < cols; j++) {
      	if (params[i][j] === null) {
        	return null;
      	}
    	}
    }

    var r = params[i][paramsIndex[0]];
    if (r === null) {
      r = [0, 1];
    }
    var s = r.toString();
    for (let j = 0; j < operationsIndex.length; j++) {
      var p = params[i][paramsIndex[j + 1]];
      if (p === null) {
        p = [0, 1];
      }
      r = operations[operationsIndex[j]].func(r, p);
      if (j > 0 && operations[operationsIndex[j - 1]].priority < operations[operationsIndex[j]].priority) {
        s = '(' + s + ')';//优先级较低的运算在前,要加括号
      }
      s += operations[operationsIndex[j]].symbo + p.toString();
    }
  } else if (type === "col") {
    j = index;
    if (answer === null) {
      for (let i = 0; i < rows; i++) {
        if (params[i][j] === null) {
          return null;
        }
      }
    }

    var r = params[paramsIndex[0]][j];
    if (r === null) {
      r = [0, 1];
    }
    var s = r.toString();
    for (let i = 0; i < operationsIndex.length; i++) {
      var p = params[paramsIndex[i + 1]][j];
      if (p === null) {
        p = [0, 1];
      }
      r = operations[operationsIndex[i]].func(r, p);
      if (i > 0 && operations[operationsIndex[i - 1]].priority < operations[operationsIndex[i]].priority) {
        s = '(' + s + ')';
      }
      s += operations[operationsIndex[i]].symbo + p.toString();
    }
  }

  //求解模式
  if (answer !== null) {
    return {
      equation: s,
      answer: answer,
      x: (r !== null) ? (answer - r[0]) / r[1] : null
    };
  } else { //计算模式
    return {
      equation: s,
      answer: r,
      x: null
    };
  }
}

//给定数组是否等差,返回公差 or null
function isAP(data) {
  var notnull = 0;
  var a = null;
  var b = null;
  for (let i = 0; i < data.length; i++) {
    if (data[i] !== null) {
      notnull += 1;
      if (a === null) {
        a = [data[i], i];
      } else if (b === null) {
        b = [data[i], i];
      }
    }
  }
  //不足3个数据不能判断
  if (notnull < 3) {
    return null;
  }

  var d = (a[0] - b[0]) / (a[1] - b[1]);
  for (let i = 0; i < data.length; i++) {
  	if (data[i] !== null && (data[i] - a[0]) !== (i - a[1]) * d) {
      return null;
    }
  }
  
  for (let i = 0; i < data.length; i++) {
    if (data[i] === null) {
      data[i] = (i - a[1]) * d + a[0];
    }
  }
  
  return [d, data];
}





<h1>
  小学生数字找规律
</h1>
<p><input id=rows type=number onchange=dimChange()>
  组,每组
  <input id=cols type=number onchange=dimChange()>
  个数字。
</p>
<div>
  ---------------------------------------------
  <p>
    已知数字,请按顺序填写。要求解的位置请留白。
  </p>
  <div id=tutorials>
    <button onclick=tutorial1()>示例1</button>
    <button onclick=tutorial2()>示例2</button>
  </div>
  <img id=tutorial src="http://files.eduuu.com/img/2016/12/19/184856_5857bb18a0709.jpg">
  <div id=params></div>
  ---------------------------------------------
  <br>
  <button onclick=run()>
    开始计算
  </button>
  <p id=warning style="color: red" hidden>至少有4行或者4列数据,否则不能确认规则成立。</p>
  <p id=msg hidden>
    根据已知数据,共计算出
    <span id=patternCount></span>
    条规则
  </p>
  <div id=solve></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;
}

.solution {
  font-weight: bold;
  color: darkred;
}

img {
  width: 450px;
  margin: 15px 0;
}