从n个元素生成所有4元组对

时间:2017-09-06 11:31:26

标签: python algorithm combinatorics

我想在给定大小为n的数组的情况下生成所有可能的 4元组对的列表。 n至少为8,因此始终可以找到至少1对。

作为一个有助于理解问题的示例,我使用较小版本的问题, 2元组对给出一个大小为5的数组。 2元组对的预期结果将导致15个项目(元组被排序,没有重复):

[(1,2), (3,4)], [(1,2), (3,5)], [(1,2), (4,5)], [(1,3), (2,4)], [(1,3), (2,5)], [(1,3), (4,5)], [(1,4), (2,3)], [(1,4), (2,5)], [(1,4), (3,5)], [(1,5), (2,3)], [(1,5), (2,4)], [(1,5), (3,4)], [(2,3), (4,5)], [(2,4), (3,5)], [(2,5), (3,4)]

我目前的做法是使用python中的itertools并浏览itertools.combinations返回的所有元素,执行2次循环并查找不共享单个元素的2对然后使用那个元素。

为了在python代码中表达这一点,我准备了一个小片段:

arr = list(range(30)) # example list 
comb = list(itertools.combinations(range(0, len(arr)), 4))

for c1 in comb:
    for c2 in comb:  # go through all possible pairs
        if len([val for val in c1 if val in c2]) == 0:  # intersection of both sets results in 0, so they don't share an element
            ... # do something and check for duplicates

此方法正在执行其工作,但由于2个循环而效率低,并且仅适用于给定时间范围内的小n。这可以更有效率吗?

更新:在得到一些答案后,我评估了这些建议。对于我的具体案例,最好的事情是MSeifert(现已删除)答案提供的(扩展)算法,该算法执行速度最快:

def generate_four_pairs(n):
    valids = range(0, n)
    for x00, x01, x02, x03, x10, x11, x12, x13 in itertools.combinations(valids, 8):
      yield [x00, x01, x02, x03], [x10, x11, x12, x13]
      yield [x00, x01, x02, x10], [x03, x11, x12, x13]
      yield [x00, x01, x02, x11], [x03, x10, x12, x13]
      yield [x00, x01, x02, x12], [x03, x10, x11, x13]
      yield [x00, x01, x02, x13], [x03, x10, x11, x12]
      yield [x00, x01, x03, x10], [x02, x11, x12, x13]
      yield [x00, x01, x03, x11], [x02, x10, x12, x13]
      yield [x00, x01, x03, x12], [x02, x10, x11, x13]
      yield [x00, x01, x03, x13], [x02, x10, x11, x12]
      yield [x00, x01, x10, x11], [x02, x03, x12, x13]
      yield [x00, x01, x10, x12], [x02, x03, x11, x13]
      yield [x00, x01, x10, x13], [x02, x03, x11, x12]
      yield [x00, x01, x11, x12], [x02, x03, x10, x13]
      yield [x00, x01, x11, x13], [x02, x03, x10, x12]
      yield [x00, x01, x12, x13], [x02, x03, x10, x11]
      yield [x00, x02, x03, x10], [x01, x11, x12, x13]
      yield [x00, x02, x03, x11], [x01, x10, x12, x13]
      yield [x00, x02, x03, x12], [x01, x10, x11, x13]
      yield [x00, x02, x03, x13], [x01, x10, x11, x12]
      yield [x00, x02, x10, x11], [x01, x03, x12, x13]
      yield [x00, x02, x10, x12], [x01, x03, x11, x13]
      yield [x00, x02, x10, x13], [x01, x03, x11, x12]
      yield [x00, x02, x11, x12], [x01, x03, x10, x13]
      yield [x00, x02, x11, x13], [x01, x03, x10, x12]
      yield [x00, x02, x12, x13], [x01, x03, x10, x11]
      yield [x00, x03, x10, x11], [x01, x02, x12, x13]
      yield [x00, x03, x10, x12], [x01, x02, x11, x13]
      yield [x00, x03, x10, x13], [x01, x02, x11, x12]
      yield [x00, x03, x11, x12], [x01, x02, x10, x13]
      yield [x00, x03, x11, x13], [x01, x02, x10, x12]
      yield [x00, x03, x12, x13], [x01, x02, x10, x11]
      yield [x00, x10, x11, x12], [x01, x02, x03, x13]
      yield [x00, x10, x11, x13], [x01, x02, x03, x12]
      yield [x00, x10, x12, x13], [x01, x02, x03, x11]
      yield [x00, x11, x12, x13], [x01, x02, x03, x10]
      yield [x01, x02, x03, x00], [x10, x11, x12, x13]
      yield [x01, x02, x03, x10], [x00, x11, x12, x13]
      yield [x01, x02, x03, x11], [x00, x10, x12, x13]
      yield [x01, x02, x03, x12], [x00, x10, x11, x13]
      yield [x01, x02, x03, x13], [x00, x10, x11, x12]
      yield [x01, x02, x10, x00], [x03, x11, x12, x13]
      yield [x01, x02, x10, x11], [x00, x03, x12, x13]
      yield [x01, x02, x10, x12], [x00, x03, x11, x13]
      yield [x01, x02, x10, x13], [x00, x03, x11, x12]
      yield [x01, x02, x11, x00], [x03, x10, x12, x13]
      yield [x01, x02, x11, x12], [x00, x03, x10, x13]
      yield [x01, x02, x11, x13], [x00, x03, x10, x12]
      yield [x01, x02, x12, x00], [x03, x10, x11, x13]
      yield [x01, x02, x12, x13], [x00, x03, x10, x11]
      yield [x01, x02, x13, x00], [x03, x10, x11, x12]
      yield [x01, x03, x10, x00], [x02, x11, x12, x13]
      yield [x01, x03, x10, x11], [x00, x02, x12, x13]
      yield [x01, x03, x10, x12], [x00, x02, x11, x13]
      yield [x01, x03, x10, x13], [x00, x02, x11, x12]
      yield [x01, x03, x11, x00], [x02, x10, x12, x13]
      yield [x01, x03, x11, x12], [x00, x02, x10, x13]
      yield [x01, x03, x11, x13], [x00, x02, x10, x12]
      yield [x01, x03, x12, x00], [x02, x10, x11, x13]
      yield [x01, x03, x12, x13], [x00, x02, x10, x11]
      yield [x01, x03, x13, x00], [x02, x10, x11, x12]
      yield [x01, x10, x11, x00], [x02, x03, x12, x13]
      yield [x01, x10, x11, x12], [x00, x02, x03, x13]
      yield [x01, x10, x11, x13], [x00, x02, x03, x12]
      yield [x01, x10, x12, x00], [x02, x03, x11, x13]
      yield [x01, x10, x12, x13], [x00, x02, x03, x11]
      yield [x01, x10, x13, x00], [x02, x03, x11, x12]
      yield [x01, x11, x12, x00], [x02, x03, x10, x13]
      yield [x01, x11, x12, x13], [x00, x02, x03, x10]
      yield [x01, x11, x13, x00], [x02, x03, x10, x12]
      yield [x01, x12, x13, x00], [x02, x03, x10, x11]

对于一般方法,我建议NPE提供答案,因为这是解决此问题的最简单,最简单的答案。

4 个答案:

答案 0 :(得分:3)

你通过生成所有组合对执行大量不必要的工作,然后丢弃几乎所有组合,因为它们包含共同元素。

以下通过首先获取四个数字的所有子集(在您的2元组示例中),然后将每个子集分成所有可能的对来解决此问题:

import itertools

def gen_pairs(n, m):
  for both_halves in itertools.combinations(xrange(1, n + 1), 2 * m):
    for first_half in itertools.combinations(both_halves, m):
      second_half = tuple(sorted(set(both_halves) - set(first_half)))
      yield [first_half, second_half]

print sorted(gen_pairs(5, 2))

请注意,这不会消除重复项(例如,[(4, 5) (2, 3)] vs [(2, 3), (4, 5)]),因此会产生两倍于您期望的元素。

然而,删除重复项是微不足道的。这留给读者练习。

答案 1 :(得分:0)

您可以使用更快的排列和拆分:

array = ...
size = 4
c = itertools.permutations(array)
for t in c:
    a = []
    for i in range(0, len(t), size):
        if i + size <= len(t):
            a.append(t[i:i+size])
    yield a

注意:如果数组的长度不是大小的倍数,则此解决方案可以工作,但会产生重复。

答案 2 :(得分:0)

我会这样做:

from itertools import combinations

sample = range(1,6)
x1 = [subset for subset in combinations(sample,2)] #getting the set of tuples
x2 = [list(subset) for subset in combinations(x1,2)] #getting the pair of tuples
x3 = [x for x in x2 if (set(x[0]) & set(x[1]) == set())] #finally filtering the tuples with no intersection

输出:

[[(1, 2), (3, 4)],
 [(1, 2), (3, 5)],
 [(1, 2), (4, 5)],
 [(1, 3), (2, 4)],
 [(1, 3), (2, 5)],
 [(1, 3), (4, 5)],
 [(1, 4), (2, 3)],
 [(1, 4), (2, 5)],
 [(1, 4), (3, 5)],
 [(1, 5), (2, 3)],
 [(1, 5), (2, 4)],
 [(1, 5), (3, 4)],
 [(2, 3), (4, 5)],
 [(2, 4), (3, 5)],
 [(2, 5), (3, 4)]]

答案 3 :(得分:0)

这里有代码来生成MSeifert的yield语句:)(并且它只产生35个,这意味着没有重复:)

static void Main(string[] args)
{
    var t1 = new ActionBlock<int>(async i =>
    {
        await Task.Delay(2000);
        Trace.TraceInformation($"target 1 | Thread {System.Threading.Thread.CurrentThread.ManagedThreadId} | message {i}");
    }, new ExecutionDataflowBlockOptions { BoundedCapacity = 5 });

    var t2 = new ActionBlock<int>(async i =>
    {
        await Task.Delay(1000);
        Trace.TraceInformation($"target 2 | Thread {System.Threading.Thread.CurrentThread.ManagedThreadId} | message {i}");
    }, new ExecutionDataflowBlockOptions { BoundedCapacity = 5 });

    var t3 = new ActionBlock<int>(async i =>
    {
        await Task.Delay(100);
        Trace.TraceInformation($"target 3 | Thread {System.Threading.Thread.CurrentThread.ManagedThreadId} | message {i}");
        if (i > 5)
            throw new Exception("Too big number");
    }, new ExecutionDataflowBlockOptions { BoundedCapacity = 5 });

    var targets = new [] { t1, t2, t3};

    var broadcaster = new ActionBlock<int>(
        async item =>
        {
            var processingTasks = targets.Select(async t =>
            {
                try
                {
                    await t.SendAsync(item);
                }
                catch
                {
                    Trace.TraceInformation("handled in select"); // never goes here
                }
            });

            try
            {
                await Task.WhenAll(processingTasks);
            }
            catch
            {
                Trace.TraceInformation("handled"); // never goes here
            }
        });

    for (var i = 1; i <= 10; i++)
        broadcaster.Post(i);
}