寻找Javascript中选项算法的排列。也许…?

时间:2017-11-24 21:24:27

标签: javascript arrays multidimensional-array permutation combinatorics

我不确定该怎么称呼这个问题,因此我找不到它的实例。我在JS写作,但伪代码算法是一个很好的解决方案。

说我想要一篮子3件商品。篮子里有牙膏,牙刷和小苏打。我有三个可供选择的供应商,我可以从不同的供应商中选择每个项目。

我将价格数据存储在二维数组中,如下所示:

productVendors = [
    [3,6,2],
    [9,4,5],
    [1,3,4]
];

每行是不同的供应商,每列是特定产品。 col1 = toothpastecol2 = toothbrushcol3 = baking soda,例如

productVendors[0][0] = 3代表第一家供应商的牙膏价格。

我需要一种算法,从 n 供应商处返回每个可能的 n 特定商品。生成的篮子将包含其中每个项目的坐标,而不是价格,如下所示:

           //item1   //item2    //item3
basket1 = [{x:0,y:0}, {x:1,y:1}, {x:2,y:2}];
basket2 = [{x:2,y:0}, {x:0,y:1}, {x:1,y:2}];
…

allBaskets = [basket1, basket2, basket3, etc…];

这个java论坛有一个几乎完全相同的问题正在解决,但我不明白。

算法: https://www.java-forums.org/advanced-java/81134-optimise-recursive-method-prints-all-possible-rows-2d-array.html#post351752

使用示例: https://www.java-forums.org/advanced-java/81134-optimise-recursive-method-prints-all-possible-rows-2d-array.html#post351876

我想完全理解一个解决方案,不仅有一些有用的东西。第一次在这里提出问题,所以提前感谢任何建议。

2 个答案:

答案 0 :(得分:0)

假设有 n 供应商和 n 商品我认为每件商品应来自不同的供应商。这可以按如下方式完成:

  1. 有一个递归函数gen(n,arr),其中arr是部分生成的篮子
  2. 如果 arr.length 等于 n - 将 arr 推送到购物篮列表并从功能返回
  3. 在函数内部你应该迭代每个项目id (i),如果 arr 不包含它 - 转到gen(n,[... arr ,{x:arr.length,y:i}])。这将添加一对代表您从下一个供应商(id为arr.length的供应商)
  4. 的项目
  5. 要检查ID是否已经存在,您可以执行以下操作:arr.filter(item => (item.y == id)).length > 0

答案 1 :(得分:0)

您的输出似乎包含3个可变组件的对象,这些组件可以采用 n 值:

  • 篮子阵列中的位置,
  • x坐标,
  • y坐标

在评论中,您解释说您希望 n n 解决方案,因此这意味着3个组件中的一个是重复的。似乎y坐标和期望结果中的索引是重复的:在您的示例中它们始终是相同的。

所有 n n 重复的排列

这归结为生成所有可能的 n 大小的列表,可以使用 n 值,允许重复。所有商品必须放在一个篮子里,每个商品只能一次,但并非所有供应商都必须由他们代表:购物篮中可能有两件或更多商品来自同一供应商。

您可以使用递归算法。在每个递归级别,我们确定一个特定项目的供应商。所有可能的供应商都会为它进行迭代。对于其中的每一个,都会对所有后续项目进行递归调用,以便生成所有可能性:

    function* permutations(n) {
        // Produce array [0, ..., n-1]
        const arr = [...Array(n).keys()];

        function* generate(n) {
            if (n < 0) {
                yield [...arr]; // a copy of arr
            } else {
                for (let i = 0; i < arr.length; i++) {
                    arr[n] = i;
                    yield* generate(n - 1);
                }
            }
        }

        yield* generate(n - 1);
    }

// Sample call for n=4
for (let arr of permutations(4)) {
    // output permuted arrays as x-y pairs:
    console.log(JSON.stringify(arr.map( (x, y) => ({ x, y }))));
    // For normal permutation view (without y), replace above line with following:
    // console.log(JSON.stringify(arr));
}
.as-console-wrapper { max-height: 100% !important; top: 0; }

上述实现使用了一些ES6功能,例如扩展语法(获取可迭代的数组副本),解构(用于交换)和生成器。生成器(function*)返回一个迭代器,它在它们被生成时提供排列。

在我的第一个版本的答案中,我了解到一个篮子里不可能有重复的供应商。这导致了纯粹的排列问题:

所有 n!排列不重复

如果必须在每个篮子中代表所有供应商,那么这就成了一个简单的排列问题。

为了生成排列,有几种有效的算法,例如Heap's Algorithm

function* permutations(n) {
    // Produce array [0, ..., n-1]
    const arr = [...Array(n).keys()];
    
    function swap(i, j) {
        [arr[i], arr[j]] = [arr[j], arr[i]];
    }

    function* generate(n) {
        if (n < 2) {
            yield [...arr]; // a copy of arr
        } else {
            for (let i = 0; i < n - 1; i++) {
                yield* generate(n - 1);
                swap(n%2 ? 0 : i, n - 1);
            }
            yield* generate(n - 1);
        }
    }

    yield* generate(n);
}

// Sample call for n=4
for (let arr of permutations(4)) {
    // output permuted arrays as x-y pairs:
    console.log(JSON.stringify(arr.map( (x, y) => ({ x, y }))));
    // For normal permutation view (without y), replace above line with following:
    // console.log(JSON.stringify(arr));
}
.as-console-wrapper { max-height: 100% !important; top: 0; }

Heap的算法非常模糊,但他找到了一种模式来选择一个元素与最后一个元素交换,以通过递归产生下一组排列:

  1. 使用递归生成第一个 n-1 元素的(n-1)!排列,将最后一个元素追加到每个 n- 1 - 大小排列。这将生成以最后一个元素结尾的所有 n -size排列。
  2. 如果 n 是奇数,则取第一个元素,然后取 i th 元素,并将其与最后一个元素交换。< / LI>
  3. 重复 i 的前两个步骤,范围介于0和 n-2 之间,并再次执行步骤1。
  4. 在第2步中,似乎总是相同的两个值来回交换(因为 n 在循环中不会改变),但是请注意第一步改变了顺序数组,所以第一个元素的值,不再是(通常)步骤1之后。

    实际上,在步骤1的每次执行中,最后一个元素将是不同的,因此在完成循环之后,将生成所有排列。

    当数组中只考虑一个元素时,递归结束。在这种情况下,不需要交换,并且数组以其当前顺序输出(通过yield)。

    请注意,此算法存在多种错误变体,以及使用基于1的索引的变体,因此在比较这些变体时会变得非常混乱。