简体   繁体   English

订购一组,维护多个定义的分组

[英]Order a set, maintaining multiple defined groupings

Since its hard for me to describe what I want in general, I try it with an example:由于我很难概括地描述我想要什么,所以我尝试举个例子:

Given a set {x,y,z,d} and subsets {x,z}, {d,y} and {x,y}, I would like to order the first set {x,y,z,d} so that the small sets dont get torn apart (the permutation in each set is not important, so {x,y} or {y,x} is the same}.给定一个集合 {x,y,z,d} 和子集 {x,z}、{d,y} 和 {x,y},我想订购第一个集合 {x,y,z,d} 所以小集合不会被撕裂(每个集合中的排列并不重要,所以 {x,y} 或 {y,x} 是相同的}。

The length of the example sets can be larger than what is given here.示例集的长度可以大于此处给出的长度。 The small sets are always real subsets of the largest set.小集合总是最大集合的实子集。

I think it would be nice to have a way to say ok this part of the set has to stay in this configuration (x has to be next to y), but this part is arbitrary.我认为如果有一种方法可以说 ok 集合的这一部分必须保留在这个配置中(x 必须在 y 旁边),但是这部分是任意的。 Any recommendation how to do it?任何建议如何去做? I tried to do it with a tree, but I am completely failing with this problem:(我试着用一棵树来做,但我完全没有解决这个问题:(

I don't think a brute force solution lacks elegance, but it will certainly try many options that aren't worth considering:我不认为蛮力解决方案缺乏优雅,但它肯定会尝试许多不值得考虑的选项:

from itertools import permutations


def find_ordering(main, subsets):
    for p in permutations(main):
        if all(any(set(p[i:i+len(s)]) == s 
                   for i in range(len(main) - len(s) + 1)) 
               for s in subsets):
            return p


print(find_ordering({1, 2, 3, 4}, [{1, 3}, {4, 2}, {1, 2}]))

Result:结果:

(3, 1, 2, 4)

Edit: OK, that was a tough nut to crack, but here's a smarter solution:编辑:好的,这是一个棘手的问题,但这里有一个更聪明的解决方案:

from functools import reduce
from itertools import permutations


def shove(xs: set, yss: list[set]) -> list[set]:
    # move n up to the first part of yss that's not or only partially contained in xs
    # this works because there's no duplicates members among any members of yss
    n = 0
    while n < len(yss) and yss[n] <= xs:
        xs = xs - yss[n]
        n += 1
    # if xs could be shoved into yss entirely
    if not xs:
        return yss
    # if xs covers yss entirely, and some is left over
    elif n >= len(yss):
        return [xs] + yss
    else:
        # h, i, t = xs - yss[n], xs & yss[n], yss[n] - xs
        h, t = xs - (i := yss[n] & xs), yss[n] - i
        # avoid returning empty sets as part of the solution
        return ([h] if h else []) + yss[:n] + ([i] if i else []) + ([t] if t else []) + yss[n+1:]


def find_ordering(main: set, subsets: list[set]) -> list | None:
    # ensure there are no elements in subsets that are not in main
    if not set(reduce(set.union, subsets, set())) <= main:
        return None
    for p in permutations(subsets):
        solution = []
        solution_set = set()
        # try to shove subsets into each other, in order p
        for subset in p:
            solution = shove(subset, solution)
            # if new solution[0] contains elements in the prev solution, there's now duplicates
            if solution[0] & solution_set:
                break
            solution_set = solution_set.union(solution[0])
        else:
            # if all subsets could be shoved together, it's a solution, stop looking and return
            return [x for xs in solution for x in xs]


print(find_ordering({1, 2, 3, 4}, [{1, 3}, {4, 2}, {1, 2}]))
print(find_ordering({1, 2, 3, 4, 5, 6, 7}, [{1, 3}, {4, 2}, {1, 2}, {4, 5, 6}, {3, 7}]))
print(find_ordering({1, 2, 3, 4, 5, 6, 7}, [{1, 6}, {2, 1, 4}, {7, 3}, {3, 4}, {5, 6}]))

Result:结果:

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

The approach hinges on the idea of shoving subsets into each other.该方法取决于将子集相互推入的想法。 If you shove {1, 2} into [{2, 3, 4}] , the outcome is [{1}, {2}, {3, 4}] - ie a fixed ordering of sets that still contains the groupings in the original sets.如果将{1, 2}推入[{2, 3, 4}] ,结果为[{1}, {2}, {3, 4}] - 即仍然包含分组的集合的固定顺序原始集。 3 and 4 can still change places and the ordering will hold, but none of the sets can change place or the ordering is broken. 34仍然可以改变位置,顺序将保持不变,但没有任何一组可以改变位置或顺序被破坏。 The shove() function performs this operation. shove() function 执行此操作。 When shoving, it never changes the order of the sets in the list, but it ignores the order of elements in the sets for matching (as expected).推入时,它永远不会改变列表中集合的顺序,但它会忽略匹配集合中元素的顺序(如预期的那样)。

The find_ordering() function uses shove to try and shove subsets into each other in every possible order (using permutations of the subsets). find_ordering() function 使用 shove 尝试以每个可能的顺序将子集推入彼此(使用子集的排列)。

If the first set in an expanded solution (after a shove) contains any elements that were already in the solution, it means there's now a duplicate in the solution and it won't work, so it moves on to the next permutation.如果扩展解决方案中的第一个集合(在推动之后)包含解决方案中已经存在的任何元素,则意味着解决方案中现在有一个重复项并且它不会起作用,因此它会继续进行下一个排列。

Before starting find_ordering() , it checks if the subsets actually solely consist of elements of main , because otherwise it would try them all while a solution is impossible.在开始find_ordering()之前,它会检查子集是否实际上仅由main的元素组成,否则它会尝试所有元素,而无法找到解决方案。 In that case, or if no permutation works, the function returns None .在这种情况下,或者如果排列无效,则 function 返回None

That was a fun question, thanks.这是一个有趣的问题,谢谢。 If you see any issue with this solution, do let me know.如果您发现此解决方案有任何问题,请告诉我。

Edit: you correctly identified a problem with the solution - turns out there were 3 problems, but I think this is better.编辑:您正确地确定了解决方案的问题 - 原来有 3 个问题,但我认为这更好。 Don't drink and code, kids.孩子们,不要边喝酒边写代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM