一种算法,用于查找填充方形网格的数字序列

时间:2017-06-22 15:40:46

标签: javascript algorithm

鉴于数字1,2,3,4,5,6,7,8,我需要它们来替换x,以便每一边加上中心的数字。

*-*---*-*
|x| x |x|
*-*---*-*
|x| 12|x|
*-*---*-*
|x| x |x|
*-*---*-*

作为一个开始,我已经圈出数字以找到所有可能的组合。

var range = [1,2,3,4,5,6,7,8];
var target = 12;
var matches = [];

for (x = 0; x < range.length; x ++){
    for (y = 0; y < range.length; y ++){
        if (y === x){
            continue;
        }
        for (z = 0; z < range.length; z ++){
            if (z === y || z === x){
                continue;   
            }

            if (range[x] + range[y] + range[z] === target){
              matches.push([range[x], range[y], range[z]]);
            }
        }           
    }   
}

接下来,我将这些数字端到端地加在一起

for (j=0; j < matches.length; j++){
  for (k=0; k < matches.length; k++){
    if (j==k) continue;
    //if (matches[j][2] != matches[k][0]) continue;
    for (l=0; l < matches.length; l++){
      if (l==j || l==k) continue;
      //if (matches[k][2] != matches[l][0]) continue;
      for (m=0; m < matches.length; m++){
        if (m==j || m==k || m==l) continue;
        if (matches[l][2] != matches[m][0]) continue;
        if (matches[m][2] != matches[j][0]){
          console.log(matches[j], matches[k], matches[l], matches[m]);
        }

      }
    }
  }
}

我目前没有办理登机手续,以确保每个号码组合只使用一次,这就是我解决这个问题的方法。

我真的想知道解决这个问题的整体更好的方法。

3 个答案:

答案 0 :(得分:1)

实际上不需要枚举所有40,320个数字的排列,因为8个位置中的4个通过从目标中减去两个相邻值来自动填充。因此,只有4个变量,最多1,680个排列:

A   B   C
D  12   E
F   G   H

A和B的任何选择都决定了C,那么任何D的选择都决定了F,任何E的选择决定了H和G,所以A,B D和E都是变量。

您可以使用4个嵌套循环迭代执行此操作,如下所示,或递归地执行此操作,这将更容易适应其他网格大小。

for A is 1 to 8
    for B is any available number < target - A
        C = target - (A + B)
        if C is not available, skip to next B
        for D is any available number < target - A
            F = target - (A + D)
            if F is not available, skip to next D
            for E is any available number < target - C
                H = target - (C + E)
                if H is not available, skip to next E
                G = target - (F + H)
                if G is available, store this combination
            }
        }
    }
}

在最简单的迭代形式中,使用Daniel Wagner的建议只生成可以旋转和镜像的唯一解决方案,您将获得类似下面的代码示例。内循环中的代码只运行了56次,总共有142 indexOf()次调用。

function numberSquare(target) {
    for (var a = 1; a < 9; a++) {
        for (var c = a + 1; c < 9 && c < target - a; c++) {
            var b = target - (a + c);
            if ([a,c].indexOf(b) > -1  || b > 8) continue;
            for (var f = c + 1; f < 9 && f < target - a; f++) {
                var d = target - (a + f);
                if ([a,b,c,f].indexOf(d) > -1 || d > 8) continue;
                for (var h = a + 1; h < 9 && h < target - c && h < target - f; h++) {
                    if ([b,c,d,f].indexOf(h) > -1) continue;
                    var e = target - (c + h);
                    if ([a,b,c,d,f,h].indexOf(e) > -1 || e > 8) continue;
                    var g = target - (f + h);
                    if ([a,b,c,d,e,f,h].indexOf(g) > -1 || g > 8) continue;
                    document.write([a,b,c] + "<br>" + [d,'_',e] + "<br>" + [f,g,h] + "<br><br>");
                }
            }
        }
    }
}

numberSquare(12);
document.write("&times; 4 rotations and 2 mirrorings (8 solutions per result)");

答案 1 :(得分:1)

我花了一点时间重新思考这个方法。我认为一个很好的解决方案是拥有一个索引对象,以便循环组成中心数字的不同组合,你知道下一个数字需要从当前选择的最后一个数字开始,所以如果你采取

[1, 3, 8]

您知道您需要查看以8开头的组合

{
    ...,
    8: [[8, 1, 3], [8, 3, 1]]
}

这将只留下两个选项。

我确信我的代码可以重构,但已经很晚了!

var range = [1,2,3,4,5,6,7,8];
var target = 13;
var matches = [];
var keyedMatches = {
  "1": [],
  "2": [],
  "3": [],
  "4": [],
  "5": [],
  "6": [],
  "7": [],
  "8": []
};

let firstSteps = 0;

for (x = 0; x < range.length; x ++){firstSteps++
    for (y = 0; y < range.length; y ++){
        if (y === x){
            continue;
        }firstSteps++
        for (z = 0; z < range.length; z ++){
            if (z === y || z === x){
                continue;   
            }firstSteps++

            if (range[x] + range[y] + range[z] === target){
              matches.push([range[x], range[y], range[z]]);
              keyedMatches[range[x]].push([range[x], range[y], range[z]])
            }
        }           
    }   
}
console.log(keyedMatches);


let secondSteps = 0;

var currentSelection = [];
var usedNums = [];
for (j = 0; j < matches.length; j ++){secondSteps++;
  usedNums.push(matches[j][0]);
  usedNums.push(matches[j][1]);
  usedNums.push(matches[j][2]);


  var step2 = keyedMatches[usedNums[usedNums.length-1]];

  for (k=0; k < step2.length; k++){
    if(checkUsed(usedNums, step2[k])) continue;

    usedNums.push(step2[k][1]);
    usedNums.push(step2[k][2]);

    var step3 = keyedMatches[usedNums[usedNums.length-1]];

    for (l=0; l < step3.length; l++){
      if(checkUsed(usedNums, step3[l])) continue;
      usedNums.push(step3[l][1]);
      usedNums.push(step3[l][2]);


      var step4 = keyedMatches[usedNums[usedNums.length-1]];
      for (m=0; m < step4.length; m++){

        if(usedNums.indexOf(step4[m][1]) !== -1) continue;

        if (step4[m][2] != usedNums[0]) continue;

        usedNums.push(step4[m][1]);
        console.log(usedNums);

        // remove the used numbers
        usedNums.pop();

      }

      // remove the used numbers
      usedNums.pop();
      usedNums.pop();
    }

    // remove the used numbers
    usedNums.pop();
    usedNums.pop();
  }

  usedNums = [];

}

function checkUsed(unum, nnum){
  if (unum.indexOf(nnum[1]) === -1 && unum.indexOf(nnum[2]) === -1){
    return false;
  }
  return true;
}

答案 2 :(得分:0)

这是一个简单的实现。我确信通过动态编程方法我可以提供更有效的解决方案,但是到目前为止由于我的时间有限,我只能提出一种直接的方法。由于我使用one of the most efficient permutation algorithms in JS,因此结果非常快。

它的结果如下;

  • 获取所提供的数字数组的所有排列。
  • 检查我们是否有有效的排列。

返回的有效排列应解释为从左上角开始并顺时针插入的值。

function solve4(n,a){
  
  function perm(a){
    var r = [[a[0]]],
        t = [],
        s = [];
    if (a.length <= 1) return a;
    for (var i = 1, la = a.length; i < la; i++){
      for (var j = 0, lr = r.length; j < lr; j++){
        r[j].push(a[i]);
        t.push(r[j]);
        for(var k = 1, lrj = r[j].length; k < lrj; k++){
          for (var l = 0; l < lrj; l++) s[l] = r[j][(k+l)%lrj];
          t[t.length] = s;
          s = [];
        }
      }
      r = t;
      t = [];
    }
    return r;
  }
  
  function validChoices(chs,n){//console.log(chs)
    return chs.filter(ch => ch[0]+ch[1]+ch[2] === n &&
                            ch[2]+ch[3]+ch[4] === n &&
                            ch[4]+ch[5]+ch[6] === n &&
                            ch[6]+ch[7]+ch[0] === n);
  }
  
  return validChoices(perm(a),n);
}

console.log(solve4(12,[1,2,3,4,5,6,7,8]));

因此,您会看到输入12[1,2,3,4,5,6,7,8],我们有8个单独的解决方案;

[ [ 1, 5, 6, 4, 2, 7, 3, 8 ],
  [ 6, 4, 2, 7, 3, 8, 1, 5 ],
  [ 2, 7, 3, 8, 1, 5, 6, 4 ],
  [ 3, 8, 1, 5, 6, 4, 2, 7 ],
  [ 3, 7, 2, 4, 6, 5, 1, 8 ],
  [ 2, 4, 6, 5, 1, 8, 3, 7 ],
  [ 6, 5, 1, 8, 3, 7, 2, 4 ],
  [ 1, 8, 3, 7, 2, 4, 6, 5 ] ]