嵌套递归,查找所有可能的件数

时间:2017-09-14 14:49:57

标签: javascript recursion permutation

给出一个示例输入:

[
    {"id":1,"currentBlack":1,"currentWhite":0,"max":1},
    {"id":2,"currentBlack":0,"currentWhite":1,"max":1},
]

输出输入的所有可能状态,其中currentBlack和currentWhite可以具有从初始值到最大值范围内的任何值。

此示例的正确输出:

[
    [
        {"id":1,"currentBlack":1,"currentWhite":0,"max":1},
        {"id":2,"currentBlack":0,"currentWhite":1,"max":1},
    ],
    [
        {"id":1,"currentBlack":1,"currentWhite":1,"max":1},
        {"id":2,"currentBlack":0,"currentWhite":1,"max":1},
    ],
    [
        {"id":1,"currentBlack":1,"currentWhite":1,"max":1},
        {"id":2,"currentBlack":1,"currentWhite":1,"max":1},
    ],
    [
        {"id":1,"currentBlack":1,"currentWhite":0,"max":1},
        {"id":2,"currentBlack":1,"currentWhite":1,"max":1},
    ]
]

实际输入的最大值在1到8之间,输入数组中的对象数量会更多。我的尝试低于(评论很多):

function allPossibleCounts(pieceCounts) {//pieceCounts is the input
        var collection = []; //used to collect all possible values
        recursiveCalls(pieceCounts); //runs recursive function
        return collection; //returns result

        function recursiveCalls(pieceCounts) {
            //if pieceCounts is already in collection then return, not yet implemented so duplicates are currently possible
            collection.push(pieceCounts);//inputs a potential value

            console.log(JSON.stringify(pieceCounts));//this is successfully logs the correct values
            console.log(JSON.stringify(collection));//collection isn't correct, all values at the top of the array are copies of each other

            for (let n in pieceCounts) {//pieceCounts should be the same at the start of each loop within each scope, aka pieceCounts should be the same at the end of this loop as it is at the start

                subBlackCall(pieceCounts);
                function subBlackCall(pieceCounts) {
                    if (pieceCounts[n].currentBlack < pieceCounts[n].max) {
                        pieceCounts[n].currentBlack++;//increment
                        recursiveCalls(pieceCounts);
                        subBlackCall(pieceCounts);//essentially you're either adding +1 or +2 or +3 ect all the way up to max and calling recursiveCalls() off of each of those incremented values
                        pieceCounts[n].currentBlack--;//decrement to return pieceCounts to how it was at the start of this function
                    }
                }

                subWhiteCall(pieceCounts);
                function subWhiteCall(pieceCounts) {
                    if (pieceCounts[n].currentWhite < pieceCounts[n].max) {
                        pieceCounts[n].currentWhite++;
                        recursiveCalls(pieceCounts);
                        subWhiteCall(pieceCounts);
                        pieceCounts[n].currentWhite--;
                    }
                }
            }
        }
    }

但是目前我的尝试输出了这个不敬虔的复制数组

[[{"id":1,"currentBlack":1,"currentWhite":1,"max":1},{"id":2,"currentBlack":1,"currentWhite":1,"max":1}],[{"id":1,"currentBlack":1,"currentWhite":1,"max":1},{"id":2,"currentBlack":1,"currentWhite":1,"max":1}],[{"id":1,"currentBlack":1,"currentWhite":1,"max":1},{"id":2,"currentBlack":1,"currentWhite":1,"max":1}],[{"id":1,"currentBlack":1,"currentWhite":1,"max":1},{"id":2,"currentBlack":1,"currentWhite":1,"max":1}],[{"id":1,"currentBlack":1,"currentWhite":1,"max":1},{"id":2,"currentBlack":1,"currentWhite":1,"max":1}]]

修改:工作代码:https://pastebin.com/qqFTppsY

2 个答案:

答案 0 :(得分:1)

pieceCounts[n]始终引用一个对象。您应该重新创建pieceCount以保存到集合中作为不同的对象。例如,您可以添加

pieceCounts = JSON.parse(JSON.stringify(pieceCounts)); // just clone 

recursiveCalls函数的开头。

答案 1 :(得分:1)

为了避免转换为JSON并返回,我建议使用Object.assign与数组上的map一起执行更深层次的副本:

&#13;
&#13;
function allPossibleCounts(pieceCounts) {
    var result = [],
        current = deeperCopy(pieceCounts);

    function deeperCopy(arr) {
        return arr.map( row => Object.assign({}, row) );
    }

    function recurse(depth) {
        // depth: indication of which value will be incremented. Each "row" has 
        // 2 items (black/white), so when depth is even, it refers to black, when 
        // odd to white. Divide by two for getting the "row" in which the increment
        // should happen.
        var idx = depth >> 1, // divide by 2 for getting row index
            prop = depth % 2 ? 'currentWhite' : 'currentBlack', // odd/even
            row = pieceCounts[idx];
        if (!row) { // at the end of the array
            // Take a copy of this variation and add it to the results
            result.push(deeperCopy(current));
            return; // backtrack for other variations
        }
        for (var value = row[prop]; value <= row.max; value++) {
            // Set the value of this property
            current[idx][prop] = value;
            // Collect all variations that can be made by varying any of 
            //   the property values that follow after this one
            recurse(depth+1);
            // Repeat for all higher values this property can get.
        }
    }

    recurse(0); // Start the process
    return result;
}

// Sample input
var pieceCounts = [
    {"id":1,"currentBlack":1,"currentWhite":0,"max":1},
    {"id":2,"currentBlack":0,"currentWhite":1,"max":1},
];
// Get results
var result = allPossibleCounts(pieceCounts);
// Output
console.log(result);
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;
&#13;
&#13;

我们的想法是使用递归:想象一下,除了第一个属性之外,所有属性的变化都可以解决问题。生成这些,然后将第一个属性值更改为下一个可能的值。再次重复所有变化的产生等。所有这些结果的组合将是第一个属性值也应该变化的解决方案。

这是递归的理想情况。当没有剩余属性值时,递归停止:在这种情况下,只有一个解决方案;将所有值设置为原样的那个。它可以添加到结果列表中。

可以像下面这样枚举属性:

row  currentBlack   currentWhite
---------------------------------
 0       0               1
 1       2               3
 2       4               5
 3       6               7
                ...
 n      2n-2            2n-1

我们可以将该数字称为 depth ,并在更深层递归的每一步增加它。给定深度,变化的属性定义为:

depth is even  => currentBlack
depth is odd   => currentWhite
row number = depth / 2 (ignoring the remainder)