根据共存规则将列表分为多个组

时间:2019-11-15 09:27:57

标签: python python-3.x set set-theory

我想根据“排除”规则创建字符串(或对象)组-即哪个项目可以存在或与其他项目“对话”

例如,假设我有一个名字列表:

names = ['proxy', 's1', 's2', 'queue', 'w1']

然后我说'proxy'可以与's1''s2'交谈,但是's1'不能与's2'交谈(这应该是相互的)。 我可以将这些规则和名称表示为对象列表:

proxy = {'name': 'proxy', 'exclude': [q, 'w1']}
s1 = {'name': 's1', 'exclude': ['s2', 'w1']}
s2 = {'name': 's2', 'exclude': ['s1', 'w1']}
q = {'name': 'queue', 'exclude': ['proxy']}
w1 = {'name': 'w1', 'exclude': ['proxy', 's1', 's2']}

我希望在这里可以分为5个小组:

[
  ['proxy', 's1'],
  ['proxy', 's2'],
  ['s1', 'queue'],
  ['s2', 'queue'],
  ['queue', 'w1']
]

我尝试在名称的完整列表上为每个“排除”使用set.difference,然后删除所有相等的集合,但这还不够,例如,对于第一个项目-“ proxy”,我最终会得到一组['proxy','s1','s2'],但是ofc's1'和's2'不能在一起。

我很想解决这个问题,但我感觉它与常见的数学/集合论问题相同或相似?

其他信息: 正如@MrFuppes很好地提到的那样,连接是双向的(即,如果X排除Z,则Z应该排除X),并且应该假定并推断出这种可能性。这是为了使用户更简单,因此他们不必显式声明两个规则。 可能是我的“规则架构”不是从用户那里收集数据来解决问题的最佳方法,如果有更最佳的方法,我将全力以赴。

2 个答案:

答案 0 :(得分:3)

绝对不是有史以来最漂亮的Python,但是它将成功。我自由地将规则收集在规则字典中,并将其重命名为q以排队以保持一致性。如果有问题,我们会找到解决方法

rules = {'proxy':{'name': 'proxy', 'exclude': ['queue', 'w1']},
         's1': {'name': 's1', 'exclude': ['s2', 'w1']},
         's2':{'name': 's2', 'exclude': ['s1', 'w1']},
         'queue': {'name': 'queue', 'exclude': ['proxy']},
         'w1': {'name': 'w1', 'exclude': ['proxy', 's1', 's2']}}
names = ['proxy', 's1', 's2', 'queue', 'w1']

现在输入代码本身。我首先要深度复制规则字典,因为我将添加已列为排除项的连接,并且不想与原始规则集混淆。其余的内容很简单,所以我对此不做过多评论:通过删除连接器本身和所有阻止的连接来获取所有允许的名称,将每个连接添加到连接列表中,并将连接器作为阻止的元素添加到所有连接中为了避免重复输入而受影响

import copy
conns = []
working_rules = copy.deepcopy(rules)
for item in names:
    tmp_names = names.copy()
    tmp_names.remove(item)
    allowed = [el for el in tmp_names if el not in working_rules[item]['exclude']]
    for el in allowed:
        conns.append([item, el])
        working_rules[el]['exclude'].append(item)

答案 1 :(得分:3)

鉴于预期的输出,输入规则是“不完整的”,这意味着必须推断互斥性-如卢卡斯·塞勒(Lukas Thaler)在其answer中所展示的。然后,推断的排除规则将取决于输入names的顺序。使用sets的简化版本可能如下所示:

excl = {'proxy': ['queue', 'w1'],
        's1': ['s2', 'w1'],
        's2': ['s1', 'w1'],
        'queue': ['proxy'],
        'w1': ['proxy', 's1', 's2']}

names = ['proxy', 's1', 's2', 'queue', 'w1']

result = []
for n in names:
    for i in set(names)-set(excl[n] + [n]):
        result.append([n, i])
        excl[i].append(n)

print(result)

# [['proxy', 's1'],
#  ['proxy', 's2'],
#  ['s1', 'queue'],
#  ['s2', 'queue'],
#  ['queue', 'w1']]

更新后的排除规则现在将是

{'proxy': ['queue', 'w1'],
    's1': ['s2', 'w1', 'proxy'],
    's2': ['s1', 'w1', 'proxy'],
 'queue': ['proxy', 's1', 's2'],
    'w1': ['proxy', 's1', 's2', 'queue']}

编辑#1

如果我没记错的话,先验地假设双向通信会使事情变得更容易(并且首先要对规则进行清晰的定义)。示例:给定对象a, b, c,可能会说a应该与b通信,反之亦然。 a也应与c进行通信,但反之亦然。 b不应与c通信,反之亦然。那会

objs = ['a', 'b', 'c']
excl = {'a': [], 
        'b': ['c'],
        'c': ['a', 'b']}

connections = [[o, i] for o in objs for i in set(objs)-set(excl[o] + [o])]
print(connections)
# [['a', 'b'], ['a', 'c'], ['b', 'a']]

编辑#2

然后可以通过分类为双向和单向组件来简化连接列表。

conn_grouped = {'bidirectional': [], 'unidirectional': []}
for c in connections:
    rev_c = list(reversed(c))
    if rev_c not in connections:
        conn_grouped['unidirectional'].append(c)
    if rev_c in connections and not rev_c in conn_grouped['bidirectional']:
        conn_grouped['bidirectional'].append(c)

print(conn_grouped)
# {'bidirectional': [['a', 'b']], 'unidirectional': [['a', 'c']]}

如果通信节点的数量增加,则可能需要找到一种更有效的算法。