Javascript 所有行和列和相等的二维4x4数组

Javascript 所有行和列和相等的二维4x4数组,javascript,arrays,Javascript,Arrays,我有一个由16个奇数组成的拟心律级数(数组):1,3,5,…,29,31 我应该将它们放入二维4×4数组中,这样所有行和列的总和都是相同的。那是64号 有多少方法可以做到这一点? 旋转或镜像组合也被视为不同的方式 最简单的方法是排列所有数组元素并检查行和列中的和。 这项工作类似于幻方问题,但对角线和在这里不必相等 做这件事最有效的方法是什么,最好是用JavaScript Example: Input array: 1 3 5 7 9 11 13 15 17 19 21 23 25 27

我有一个由16个奇数组成的拟心律级数(数组):1,3,5,…,29,31

我应该将它们放入二维4×4数组中,这样所有行和列的总和都是相同的。那是64号

有多少方法可以做到这一点? 旋转或镜像组合也被视为不同的方式

最简单的方法是排列所有数组元素并检查行和列中的和。 这项工作类似于幻方问题,但对角线和在这里不必相等

做这件事最有效的方法是什么,最好是用JavaScript

Example:
Input array:
 1  3  5  7
 9 11 13 15
17 19 21 23
25 27 29 31

One of the results:
 1 11 21 31
29 23  9  3
27  5 19 13
 7 25 15 17

肯定不是最快的方法,但它可能有用

生产549504变体大约需要200秒

const startTime = Date.now();
const inputArray = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31];
const target = 64;

const combinationsOfFour = {};

//==================================================================
/*
  Section.1
  Find all valid combinations of four values from
  the input array that total the target.
  Store each combination in a nested object
  grouped by the first value in the combination,
  and referenced by a comma separated string of
  the combination.

  eg
  look_up_table = {
    1: {
      '1,2,3,4': [1, 2, 3, 4],
      '1,2,4,3': [1, 2, 4, 3],
      ...
    },
    2: {
      ...
    }
  }
*/

// --------------------------------------------

/*
  Take groups of four ascending values, and find all
  individual combinations, and assign them to their
  corresponding group in the lookup table,
  referenced by the comma separated string of their values
  eg
  abcd => 'a,b,c,d', 'a,b,d,c', 'a,c,b,d', 'a,c,d,b'...
*/
const assignAllVariants = groupA => {
  groupA.forEach((valA, indexA) => {
    const groupB = [...groupA];
    groupB.splice(indexA, 1);

    groupB.forEach((valB, indexB) => {
      const groupC = [...groupB];
      groupC.splice(indexB, 1);

      groupC.forEach((valC, indexC) => {
        const groupD = [...groupC];
        groupD.splice(indexC, 1);

        const valD = groupD[0];

        const combination = [valA, valB, valC, valD];
        combinationsOfFour[valA][combination.join(",")] = combination;
      });
    });
  });
};

// ----------------------------------------------

/*
  Get each combination of four ascending values that total the target

  * Note -  the input array would need an initial sort in cases where
          it is not already in ascending order.

    - loop through each value in the input array
      - assign that value to 'first'
      - beginning with the following value,
        loop through each value in the array
        - assign that value to 'second'
        - beginning with the following value,
          loop through each value in the array
          - assign that value to 'third'

          - subtract the sum of first, second and third
            from the target
          - assign this value to 'fourth'

          - check that fourth is greater than third,
            and less than or equal to the last value
            in the array.

            * Note if the input array is comprised of values with
              some other spacing eg(1, 3, 6, 10, 15...)
              then the value of 'fourth' would need to checked
              against the array for validity

          - All valid groups of four are passed to the
            function assignAllVariants
*/
const createGroup = (target, values) => {
  let first, second, third, fourth;

  values.forEach(val => (combinationsOfFour[val] = {}));

  return values.forEach((value, index) => {
    first = value;

    for (let i = index + 1; i < values.length - 2; i++) {
      second = values[i];

      for (let j = i + 1; j < values.length - 1; j++) {
        third = values[j];
        fourth = target - first - second - third;

        if (fourth <= third) {
          break;
        }

        if (fourth <= values[values.length - 1]) {
          const group = [first, second, third, fourth];
          assignAllVariants(group);
        }
      }
    }
  });
};

// ======================================================
/*
  Section.2
    - Loop through the values in the combinations table
      created in section 1.
      - Set the given combination to the first row of the grid.
      - Remove the values of that combination from a lookup table
        created from the input array.
      - Taking the value in the first position of the first row,
      - loop through the corresponding group in the combinations table
        - Check that the combination does not contain values already removed
          from the lookup table, or collide with existing values in the grid.
        - Apply this combination to the first column of the grid.
        - Repeat this process with the 2nd row, 2nd column, 3rd row...

        - If the fourth row is successfully assigned then add that completed
          grid to validLayouts

*/

const getGrid = (inputArray, combinations) => {
  let grid = [[], [], [], []];
  const validLayouts = [];

  const gridToString = grid => {
    return grid.map(row => row.join(",")).join("|");
  };

  // Check given combination against a lookup table of used/ unused values
  const checkLookup = (combination, start, lookUp) => {
    if (start > 0) {
      for (let i = start; i < 4; i++) {
        if (!lookUp[combination[i]]) {
          return false;
        }
      }
      return true;
    } else {
      return true;
    }
  };

  // Check given combination against existing values in the grid
  const checkAgainstGrid = (combination, n, axis) => {
    if (axis === "x") {
      if (n > 0) {
        for (let i = 4 - n + 1; i < 4; i++) {
          if (combination[4 - i] !== grid[n][4 - i]) {
            return false;
          }
        }
        return true;
      } else {
        return true;
      }
    } else if (axis === "y") {
      for (let i = 4 - n; i < 4; i++) {
        if (combination[4 - i] !== grid[4 - i][n]) {
          return false;
        }
      }
      return true;
    }
  };

  // Update lookup table
  const removeUsedValues = (combination, n, lookUp) => {
    const newLookUp = { ...lookUp };
    for (let i = n; i < 4; i++) {
      newLookUp[combination[i]] = false;
    }
    return newLookUp;
  };

  // -----------------------------------
  /*
    Only needed when examining failed grid attempts,
    can be removed, but minimal performance impact
    on the given set.
  */
  // Use to clear grid after unsuccessful branch
  const cleanUpGrid = (n, axis) => {
    if (axis == "x") {
      grid[n].splice(n);
    } else if (axis == "y") {
      for (let i = n + 1; i < 4; i++) {
        grid[i].splice(n);
      }
    }
  };
  // ------------------------------------------------

  // Assign passing combinations to the corresponding grid column
  const assignCol = (combination, n, lookUp) => {
    let newLookUp;
    // Check combination against lookup table and current grid values
    if (
      checkLookup(combination, n + 1, lookUp) &&
      checkAgainstGrid(combination, n, "y")
    ) {
      // remove used digits from lookup table
      newLookUp = removeUsedValues(combination, n, lookUp);

      // assign combination to column
      for (let i = n + 1; i < 4; i++) {
        grid[i][n] = combination[i];
      }

      Object.keys(combinations[grid[n + 1][0]]).forEach(ref => {
        const combination = combinations[grid[n + 1][0]][ref];

        assignRow(combination, n + 1, newLookUp);
      });

      cleanUpGrid(n, "y");
    }
  };

  // Assign passing combinations to the corresponding grid row
  const assignRow = (combination, n, lookUp) => {
    // Check combination against lookup table and current grid values
    let newLookUp;
    if (
      checkLookup(combination, n, lookUp) &&
      checkAgainstGrid(combination, n, "x")
    ) {
      // remove used digits from lookup table
      newLookUp = removeUsedValues(combination, n, lookUp);

      // assign combination to row
      grid[n] = [...combination];

      if (n === 3) {
        validLayouts.push(gridToString(grid));
      } else {
        Object.keys(combinations[grid[0][n]]).forEach(ref => {
          const combination = combinations[grid[0][n]][ref];

          assignCol(combination, n, newLookUp);
        });

        cleanUpGrid(n, "x");
      }
    }
  };

  // create initial lookup table from input array
  const lookUp = {};
  inputArray.forEach(val => (lookUp[val] = true));

  // main outer loop
  Object.keys(combinations).forEach(group => {
    Object.keys(combinations[group]).forEach(ref => {
      const combination = combinations[group][ref];

      assignRow(combination, 0, lookUp);
    });
  });
  return validLayouts;
};

//-------------------------------------------------------

createGroup(target, inputArray);
const validGrids = getGrid(inputArray, combinationsOfFour);

console.log(validGrids.length);
console.log(`Duration: ${(Date.now() - startTime) / 1000}s`);


const startTime=Date.now();
常量输入=[1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31];
常数目标=64;
const combinationsOfFour={};
//==================================================================
/*
第1节
从中查找四个值的所有有效组合
总计目标的输入数组。
将每个组合存储在嵌套对象中
按组合中的第一个值分组,
并由逗号分隔的字符串
组合。
如
查找表={
1: {
'1,2,3,4': [1, 2, 3, 4],
'1,2,4,3': [1, 2, 4, 3],
...
},
2: {
...
}
}
*/
// --------------------------------------------
/*
以四个升序值为一组,找到所有值
单个组合,并将它们分配给各自的
查找表中的对应组,
由其值的逗号分隔字符串引用
如
abcd=>“a,b,c,d”,“a,b,d,c”,“a,c,b,d”,“a,c,d,d”,“a,c,d,b”。。。
*/
const assignAllVariants=groupA=>{
groupA.forEach((valA,indexA)=>{
常量groupB=[…groupA];
B组:拼接(indexA,1);
组B.forEach((valB,indexB)=>{
const groupC=[…groupB];
C组拼接(indexB,1);
groupC.forEach((valC,indexC)=>{
常量groupD=[…groupC];
D组拼接(indexC,1);
const valD=groupD[0];
常数组合=[valA,valB,valC,valD];
组合四[valA][composition.join(“,”)=组合;
});
});
});
};
// ----------------------------------------------
/*
获取四个升序值的每个组合,这四个值是目标的总和
*注意-在以下情况下,输入数组需要初始排序:
它还没有按升序排列。
-循环输入数组中的每个值
-将该值指定给“first”
-从以下值开始,
循环遍历数组中的每个值
-将该值指定给“秒”
-从以下值开始,
循环遍历数组中的每个值
-将该值指定给“第三个”
-减去第一、第二和第三的总和
从目标
-将此值指定给“第四个”
-检查第四个是否大于第三个,
并且小于或等于最后一个值
在数组中。
*请注意,如果输入数组由具有
其他一些间距,例如(1、3、6、10、15…)
然后需要检查'fourth'的值
检查数组的有效性
-所有四个有效组都将传递给
函数赋值变量
*/
const createGroup=(目标,值)=>{
让第一,第二,第三,第四;
forEach(val=>(four[val]={})的组合;
返回值。forEach((值,索引)=>{
第一个=值;
for(设i=index+1;irow.join(“,”).join(“|”);
};
//对照已使用/未使用值的查找表检查给定的组合
const checkLookup=(组合、开始、查找)=>{
如果(开始>0){
for(设i=start;i<4;i++){
如果(!查找[组合[i]]){
返回false;
}
}
返回true;
}否则{
返回true;
}
};
//对照网格中的现有值检查给定的组合
const checkAgainstGrid=(组合,n,轴)=>{
如果(轴==“x”){
如果(n>0){
for(设i=4-n+1;i<4;i++){
if(组合[4-i]!==网格[n][4-i]){
返回false;
}
}
返回true;
}否则{
返回true;
}
}否则如果(轴==“y”){
for(设i=4-n;i<4;i++){
if(组合[4-i]!==网格[4-i][n]){
返回false;
}
}
返回true;
}
};
//更新查找表
常量removeUsedValues=(组合,n,查找)=>{
const newLookUp={…lookUp};
for(设i=n;i<4;i++){
newLookUp[composition[i]]=false;
}
返回newLookUp;
};
// -----------------------------------
/*
仅在检查失败的网格尝试时需要,
可以删除,但对性能的影响最小
在给定的集合上。
*/
//用于在分支失败后清除网格
常量清理网格=(n,轴)=>{
如果(轴=“x”){
网格[n]。拼接(n);
}否则,如果(轴==“y”){
for(设i=n+1;i<4;i++){
网格[i]。拼接(n);
}
}
};
// ------------------------------------------------
//将传递组合指定给相应的轴网柱
const assignCol=(组合,n,查找)=>{
让新的查找;
//对照查找表和当前网格值检查组合
如果(
检查查找(组合,n+1,查找)&&
检查网格(组合,n,“y”)
) {
//从查找表中删除使用过的数字
newLookUp=removeUsedValue(co