【问题标题】:Get all the possible unique permutations获取所有可能的唯一排列
【发布时间】:2016-10-26 14:01:50
【问题描述】:

有一个 small 数组,其中包含 ['^','^','>','>','+','<','<'] 之类的符号,我怎样才能获得所有不同的排列?我知道有人问过类似的问题(并且已经有了一些很好的答案),例如:

但是他们没有提供独特的结果。我怎样才能有效地只获得每个可能的结果一次?

【问题讨论】:

  • 您应该使用其中一种解决方案,但是将每个结果添加到生成的地图中,并且如果它还没有在地图中,则仅将其添加到 arr 中
  • 你应该澄清你是想要 permutations 还是(我怀疑)你实际上想要 combinations math.uiuc.edu/~wgreen4/Math124S07/PermuCombinations.pdf
  • @Brad Thomas。我怀疑正确的术语是“没有重复的组合”,但我不确定,我发现的例子是不同的数字/符号,而我的数组有重复。

标签: javascript algorithm permutation


【解决方案1】:

对于小型数组,您可以使用其中一种引用算法,将每个排列映射到一个字符串,然后将整个数组放入 Set 以丢弃重复项。比如:

let a = ['^','^','>','>','+','<','<'];
let ps = permutations(a);  // return value should be array of arrays.
let qs = ps.map(p => p.join(""));
let s = new Set(qs);

这应该适用于带有&lt; 10 符号的数组。

否则,请参阅 herehere 了解可以转换为 JavaScript 的各种方法。

一种流行的方法是Pandita algorithm,它使用继承规则按字典顺序枚举排列,有效地只生成“唯一”排列。 herehere 对此方法进行了简短说明。这是一个 JavaScript (ES6) 实现:

function swap(a, i, j) {
    const t = a[i];
    a[i] = a[j];
    a[j] = t;
}

function reverseSuffix(a, start) {
    if (start === 0) {
        a.reverse();
    }
    else {
        let left = start;
        let right = a.length - 1;

        while (left < right)
            swap(a, left++, right--);
    }
}

function nextPermutation(a) {
    // 1. find the largest index `i` such that a[i] < a[i + 1].
    // 2. find the largest `j` (> i) such that a[i] < a[j].
    // 3. swap a[i] with a[j].
    // 4. reverse the suffix of `a` starting at index (i + 1).
    //
    // For a more intuitive description of this algorithm, see:
    //   https://www.nayuki.io/page/next-lexicographical-permutation-algorithm
    const reversedIndices = [...Array(a.length).keys()].reverse();

    // Step #1; (note: `.slice(1)` maybe not necessary in JS?)
    const i = reversedIndices.slice(1).find(i => a[i] < a[i + 1]);

    if (i === undefined) {
        a.reverse();
        return false;
    } 

    // Steps #2-4
    const j = reversedIndices.find(j => a[i] < a[j]);
    swap(a, i, j);
    reverseSuffix(a, i + 1);
    return true;
}

function* uniquePermutations(a) {
    const b = a.slice().sort();

    do {
        yield b.slice();
    } while (nextPermutation(b));
}

let a = ['^','^','>','>','+','<','<'];
let ps = Array.from(uniquePermutations(a));
let qs = ps.map(p => p.join(""));

console.log(ps.length);
console.log(new Set(qs).size);

nextPermutation 函数将数组就地转换为字典序的后继数组,或者如果数组已经是字典序的最大值,则将其转换为字典序的最小值。在第一种情况下,它返回true,否则返回false。这允许您从最小(排序)数组开始循环遍历所有排列,直到nextPermutation 翻转并返回false

【讨论】:

    【解决方案2】:

    那么,独特的结果问题显然会成为效率杀手,因为您每次创建新排列时都必须检查结果列表。至于算法,它将以与其他排列算法基本相同的方式工作,但您的删除重复标准将涉及更多检查。如果数组的大小很小,效率应该不是一个大问题。如果已经找到的值不添加到数组中,只需遍历答案数组即可。加快此检查过程的一种方法是确定对 answers 数组进行排序的方法。例如 ^ 总是在 * 之后出现在 ( 然后你不必每次都检查整个数组。还有其他加快速度的方法,但归根结底,它仍然是一个相当昂贵的计算要求。因为你的数组很小,除非你打算做这个排列,否则根本不重要

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多