从具有非唯一身份的列表中按顺序获取唯一组合

时间:2018-03-23 07:40:51

标签: javascript arrays algorithm

我有一个问题,我无法解决这个问题,我希望有人可以帮助我。

想象一下以下情况:

有一个人名单都有名字。所以每个人都是一个名字""属性。从该列表中,随机顺序选择随机数量的人(因此并非所有人总是在选择中),并且每个人被选择不超过一次。但是,名称可能不是唯一的:列表中可能有多个人名为" Smith"。对于这个例子,让我们想象每个名字只是字母表中的一个字母。所以我可能会收到以下选择:

[V,C,R,C,F,X,R,C]

下一次选择可能完全不同。该选择中的每个元素都是不同的人,但有些名称不止一次出现。

让我们添加数字以澄清:

[V, C1, R1, C2, F, X, R2, C3]

现在我需要所有可能的人员组合,其中每个名称只出现一次,但尊重他们在选择中列出的顺序。选择中的每个唯一名称都应该在组合中 例如,在这种情况下,我需要:

[V, C1, R1, F, X],
[V, R1, C2, F, X],
[V, R1, F, X, C3],
[V, C1, F, X, R2],
[V, C2, F, X, R2]
...

等等。人们的职位不应该改变(即[C1,V,...]不会好,因为" V"不应该在" C1")之后。

我假设我需要递归和某种方式来跟踪名称,但这就是我的大脑开始融化的地方。 ;-)我发现脚本可以按任何顺序获得所有可能的排列,但不是这样的。

有人可以帮帮我吗?

谢谢!

2 个答案:

答案 0 :(得分:0)

您可以采用一个组合算法,该算法在结果集中取一个项目(在检查相同的第一个字母不在临时结果集中之后),然后继续下一个索引,直到找到所需的长度或不有更多项目可供选择。

function getCombinations(array, size) {

    function fork(i, t) {
        if (t.length === size) {
            result.push(t);
            return;
        }
        if (i === array.length) {
            return;
        }
        if (!t.some(([c]) => c === array[i][0])) {
            fork(i + 1, t.concat([array[i]]));
        }
        fork(i + 1, t);
    }

    var result = [];
    fork(0, []);
    return result;
}

var data = ['V', 'C1', 'R1', 'C2', 'F', 'X', 'R2', 'C3'],
    result = getCombinations(data, 5);
	
console.log(result.map(a => a.join(' ')));

答案 1 :(得分:0)

这是一个简单的非递归算法。

list = ['V', 'C1', 'R1', 'C2', 'F', 'X', 'R2', 'C3']; //

// build an index item => its positions
// (in your case item[0] == item.name)

let index = new Map;

for (let [i, item] of list.entries())
    index.set(item[0], (index.get(item[0]) || new Set).add(i));

// start with a single combination which is the initial list

let combs = [list];

// for each item and its positions...

for (let [item, is] of index) {

    // generate a new set of combinations

    let combs2 = [];

    // for each position...

    for (let i of is) {

        // all positions of this item, except of the current one

        let toRemove = new Set([...is]);
        toRemove.delete(i);

        // for all combinations... 

        for (let c of combs) {

            // insert null at these positions

            combs2.push(c.map((x, j) => toRemove.has(j) ? null : x));

        }
    }

    // continue with the new set of combinations

    combs = combs2;
}

// once we're ready remove all null values

combs = combs.map(c => c.filter(x => x !== null))

// done

for (let c of combs)
    console.log(c.join())