生成所有唯一的三胞胎

时间:2018-09-26 08:33:38

标签: python random combinatorics

有一系列的三胞胎:

[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12],[13, 14, 15]]

(可以更长)。它们都是独一无二的。

问:,生成这些三胞胎的所有可能组合,以使之前没有遇到的所有物品再次“相遇”的有效方法是什么?

例如,按此顺序,三元组中没有一个包含之前遇到的任何项目:

[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12],[13, 14, 15]]
[[1, 5, 9], [4, 8, 12], [7, 11, 15], [10, 14, 3],[2, 6, 13]]
[[1, 4, 7], [5, 8, 11], [9, 12, 15], [10, 13, 2],[14, 3, 6]]

但此方法无效:

[[1, 5, 9], [4, 6, 12], [7, 11, 15], [10, 14, 3],[2, 8, 13]]

因为第二个三元组中的46之前曾经在同一个三元组中,特别是在第一条记录的[4, 5, 6]

我认为可以做到这一点,方法是使用random.sample(l, 3)从初始序列中随机选择三元组,然后检查以前是否未使用过该三元组,但是它看起来效率很低,我想知道是否有更好的方法。

更新:::

我意识到发布一个丑陋且效率低下的代码仍然有意义,只是为了说明我在说什么:

import random
import itertools

z = list(range(1, 10))
group_size = 3
superset = list()



def check_not_met(x):
    for i in superset:
        if set(x).issubset(set(i)):
            return False
    return True


def check_not_anyone_met(x):
    for i in itertools.combinations(x, 2):
        if not check_not_met(i):
            return False
    return True


subsession_matrices = list()


def generating_subsession(seq):
    subglobal = list()
    while seq:
        x = a[-group_size:]
        if check_not_anyone_met(x):
            subglobal.append(x)
        else:
            return False
        del seq[-group_size:]
    return subglobal

for j in range(10000):
    a = z.copy()
    random.shuffle(a)
    subsession_matrix = generating_subsession(a)
    if not subsession_matrix:
        continue
    else:
        subsession_matrices.append(subsession_matrix)
        superset.extend(subsession_matrix)

print(subsession_matrices)

,输出为:

[[3, 7, 1], [8, 2, 4], [6, 5, 9]]
[[8, 1, 9], [3, 5, 2], [7, 6, 4]]
[[3, 8, 6], [1, 4, 5], [7, 2, 9]]
[[8, 5, 7], [1, 2, 6], [3, 9, 4]]

3 个答案:

答案 0 :(得分:1)

好的,这是从评论开始的,但是太大而无法使用。

首先,根据您的定义,没有唯一的组合。让我解释一下:

从那时起,您就不想重复已经出现在三联体中的任何两个数字,它们出现的顺序很重要,并且不会更改组合。

一个清楚的例子:

首先,您应该:

[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12], [13, 14, 15]]

一个可能的序列(不同于您的序列)可能是:

[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12], [13, 14, 15]]
[[1, 5, 10], [4, 8, 12], [7, 11, 15], [9, 14, 3],[ 2, 6, 13]]

(只需在第二个组合中将9交换为10)。不过,这会使15在同一三元组中再次不可用,因此按此顺序,您的第二个组合

[[1, 5, 9], [4, 8, 12], [7, 11, 15], [10, 14, 3],[2, 6, 13]]

无法出现在我的序列中。

那么,您如何定义唯一性?我认为您的定义存在问题。我什至不确定顺序是否会影响序列的长度。

  

检查此三联体以前是否未使用

如果您对唯一序列不感兴趣,但希望将限制应用于序列并获得尽可能多的组合,则上述方法将无效。您应该检查三元组中是否包含2个数字,而不是以前是否出现过三元组。您的条件将无法识别该组合

[[1, 5, 9], [4, 7, 13], [8, 11, 15], [10, 14, 3], [2, 6, 12]]
尽管所有三胞胎以前都没有出现,但

是不可接受的。

希望这会有所帮助。无论如何,如果我误解了一些内容,请进行编辑。

答案 1 :(得分:1)

您可以将每个数字递归地填写到三胞胎列表中,同时跟踪每个数字在集合字典中看到的数字,并在设法填写所有数字的情况下返回组合。使用while循环继续执行此操作,直到找不到更多组合为止:

from copy import deepcopy

def unique(items, size=3):
    def find_unique(items, size, seen, filled):
        filled_set = set(item for group in filled for item in group)
        if len(filled_set) == len(items):
            return filled, seen
        candidates = items - filled_set
        if not filled or len(filled[-1]) == size:
            filled.append([])
        for incumbent in filled[-1]:
            candidates -= seen[incumbent]
        new_seen = deepcopy(seen)
        new_filled = deepcopy(filled)
        for candidate in candidates:
            for incumbent in new_filled[-1]:
                new_seen[incumbent].add(candidate)
            new_seen[candidate].update(filled[-1])
            new_filled[-1].append(candidate)
            combinations, real_seen = find_unique(items, size, new_seen, new_filled)
            if combinations:
                return combinations, real_seen
            del new_filled[len(filled):]
            del new_filled[-1][len(filled[-1]):]
            for incumbent in new_filled[-1]:
                new_seen[candidate].remove(incumbent)
                new_seen[incumbent].remove(candidate)
        return None, None

    combinations = [items]
    seen = {}
    for group in items:
        for item in group:
            seen.setdefault(item, set()).update(group)
    while True:
        combination, seen = find_unique(seen.keys(), size, seen, [])
        if not combination:
            break
        combinations.append(combination)
    return combinations

这样:

unique([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12], [13, 14, 15]])

将返回:

[[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12], [13, 14, 15]],
 [[1, 4, 7], [2, 5, 8], [3, 10, 13], [6, 11, 14], [9, 12, 15]],
 [[1, 5, 9], [2, 4, 10], [3, 6, 15], [7, 11, 13], [8, 12, 14]],
 [[1, 6, 8], [2, 7, 14], [3, 9, 11], [4, 12, 13], [5, 10, 15]],
 [[1, 10, 14], [2, 11, 15], [3, 4, 8], [5, 7, 12], [6, 9, 13]]]

答案 2 :(得分:1)

您可以创建一个递归函数:

d = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12],[13, 14, 15]]
def create_triplet(nums, original, current = []):
  if len(current) == 3:
    yield sorted(current)
  else:
    for i in nums:
      yield from create_triplet(nums, original, current+[i])

_original = [d]
def triplets(source, current=[]):
  if len(current) == len(d):
     _original.append(current)
     yield current
  else:
     _flattened, seen = [i for b in current for i in b], []
     _options = list(create_triplet([i for i in source if i not in current], _original))
     for i in _options:
       if i not in seen and all(all(c not in b for c in i) for b in current) and all(i not in c for c in _original):
          _test = current+[i]
          if all(all(sum(h in c for h in i) < 2 for i in _test for c in b) for b in _original):
            yield from triplets(source, current=_test)
            seen.append(i)

_ = list(triplets([i for b in d for i in b]))
print(_original)

输出:

[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12], [13, 14, 15]]
[[1, 4, 7], [2, 5, 8], [3, 10, 13], [6, 11, 14], [9, 12, 15]]
[[1, 5, 9], [2, 4, 10], [3, 6, 15], [7, 11, 13], [8, 12, 14]]
[[1, 6, 8], [2, 7, 14], [3, 9, 11], [4, 12, 13], [5, 10, 15]]
[[1, 10, 14], [2, 11, 15], [3, 4, 8], [5, 7, 12], [6, 9, 13]]