Graph 解析依赖关系和冲突的图(可能包含循环)

Graph 解析依赖关系和冲突的图(可能包含循环),graph,dependencies,conflict,boolean-logic,boolean-operations,Graph,Dependencies,Conflict,Boolean Logic,Boolean Operations,给定一个图,其中每个节点可能包含与其他节点的任意依赖关系或冲突,从而导致任何可能的排列,包括循环引用和矛盾引用 我试图计算一个稳定的结果列表,其中最大限度地包含一个能够尊重所有约束的节点列表。我只需要找到一个可能的解决方案,如果有一个 在下面的示例中,“A依赖于B”表示A为真B必须为真。“B与A冲突”表示B为真A不得为真。没有优先级,依赖项和冲突都具有同等的权重并同时应用 在第三个示例中,“A”没有出现,因为它取决于与B冲突的D。因为A也需要B。。A不能作为D的冲突而存在,A的依赖关系禁止它 A

给定一个图,其中每个节点可能包含与其他节点的任意依赖关系或冲突,从而导致任何可能的排列,包括循环引用和矛盾引用

我试图计算一个稳定的结果列表,其中最大限度地包含一个能够尊重所有约束的节点列表。我只需要找到一个可能的解决方案,如果有一个

在下面的示例中,“A依赖于B”表示A为真B必须为真。“B与A冲突”表示B为真A不得为真。没有优先级,依赖项和冲突都具有同等的权重并同时应用

在第三个示例中,“A”没有出现,因为它取决于与B冲突的D。因为A也需要B。。A不能作为D的冲突而存在,A的依赖关系禁止它

A depends on B
B conflicts with A
= B

A depends on B
B depends on A
= A and B

A depends on B
A depends on D
B depends on C
D conflicts with B
D conflicts with C
= B and C or D

A conflicts with B
B conflicts with C
C conflicts with D
D conflicts with A
= A and C or B and D

我一直在尝试提出一个有效的算法,但到目前为止,我的努力类似于启发式GobblyBook,在非平凡图上失败得惊人。如果您有任何见解、阅读材料的指针或算法名称,我们将不胜感激。

将问题中使用的语言转换为布尔表达式,我们有:

“A取决于B”=>(A和B)或(非A)”

B与A“=>(B而非A)或(非B)冲突

因此,一种简单的编写方法(在Python3中)是生成笛卡尔积(给出所有可能的替代方案),然后只选择满足约束的情况。为简单起见,我使用索引而不是字母作为输入,因此
y[0]
相当于
A
,等等。不过,我已将输出转换为字母

对于n个节点,此方法将生成并测试所有2^n个可能的情况。有关更复杂但可能更有效的方法,请参见下文

import itertools

bbb = (True,False)

def resolve(n,test):
    return [x for x in itertools.product(bbb,repeat=n) if test(x)]

def dependsOn(a,b):
    return (a and b) or (not a)

def conflictsWith(b,a):
    return (b and not a) or (not b)

letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'

def pr(d):
    for dd in d:
        s = [letters[i] for i in range(len(dd)) if dd[i] ]
        if (len(s) > 0):
            print(s,end = " ")
    print()

pr(list(resolve(2,lambda y: 
    dependsOn(y[0],y[1]) and 
    conflictsWith(y[1],y[0])
    )))
pr(list(resolve(2,lambda y: 
    dependsOn(y[0],y[1]) and 
    dependsOn(y[1],y[0]) 
    )))
pr(list(resolve(4,lambda y: 
    dependsOn(y[0],y[1]) and 
    dependsOn(y[0],y[3]) and
    dependsOn(y[1],y[2]) and 
    conflictsWith(y[3],y[1]) and 
    conflictsWith(y[3],y[2])
    )))
pr(list(resolve(4,lambda y: 
    conflictsWith(y[0],y[1]) and
    conflictsWith(y[1],y[2]) and
    conflictsWith(y[2],y[3]) and
    conflictsWith(y[3],y[0])
    )))
结果如下:

['B']
['A', 'B']
['B', 'C'] ['C'] ['D']
['A', 'C'] ['A'] ['B', 'D'] ['B'] ['C'] ['D']
。。。对于四个测试用例

有关更多信息,您可以查看

(编辑)

对于具有多个节点和多个约束的问题,一种更有效的方法是逐步构建节点列表,然后仅在该部分列表符合约束的情况下(至少到目前为止已填充的程度)从每个部分列表继续构建。我们可以用以下版本替换上面的
resolve
函数,并将
dependsOn
conflicts替换为匹配的
函数:

import queue

# The following constraint functions return True if the constraint is met
# or if one or more of the elements relating to the constraint is None

def dependsOn(a,b):
    if (a != None) and (b != None):
        return (a and b) or (not a)
    else:
        return True

def conflictsWith(b,a):
    if (a != None) and (b != None):
        return (b and not a) or (not b)
    else:
        return True

def resolve(n,test):
    result = []
    testCount = 0
    # initialise the list with all None
    lst = [None] * n
    q = queue.Queue()
    q.put(lst)
    while not q.empty():
        lst = list(q.get())
        testCount += 1
        if test(lst):
            # the list complies with the constraints, at least
            # as far as it is populated
            if lst[-1] != None:
                # we have a fully-populated list
                result.append(lst)
            else:
                i = lst.index(None)
                lst[i] = True
                q.put(list(lst))
                lst[i] = False
                q.put(list(lst))
    print ("testCount = %d" % testCount)
    return result
这为四个测试用例提供了相同的结果。但是,对于第三个和第四个测试用例,
testCount
的值分别为21和23。这超过了笛卡尔乘积解所需的测试总数(n=4为16),但是,对于节点和约束更多的情况,这种方法避免了测试不可能包含解决方案的节点子集,因此与笛卡尔乘积解决方案相比,很容易需要更少的测试。当然,在最坏的情况下,很少或没有约束,这种方法最多需要2^(n+1)-1个测试。事实上,对于前两个测试用例,使用此算法时,
testCount
为7

请注意,这里显示的实现是粗糙的,当然可以针对速度进行优化。

我假设

  • “A取决于B”意味着A意味着B
  • “B与A冲突”表示B不表示A
现在,您有A意味着B=不是A或BB意味着不是A=不是B或不是A。这意味着问题归结为寻找析取连词(也称为子句)的解决方案,其中每个子句都有两个参数(a、而不是a、B或非B)

这个问题称为2-可满足性。您可以在web中找到多项式时间算法,例如,开始于


据我所知,在现代SAT解算器中,没有必要编写自己的算法。SAT解算器应该能够在多项式时间内自动解算此类实例。

为了澄清预期输出,您能否1)指出运算符的优先级(我认为您假定
的优先级),以及2)解释为什么
A
没有出现在第三个示例的答案中?目前,我不确定为什么需要“依赖于”行,除了显示在评估冲突时应该考虑哪些节点。如果没有“依赖于”行,问题似乎解决为一组布尔语句,形式为“如果a那么不是B/如果B那么不是a”等。感谢您的澄清。嗨,西蒙,我更新了上面的问题以尝试回答您的问题。有人能验证这一点吗:“a依赖于B”=a意味着B。“B与a冲突”=B意味着不是A。如果是这种情况,那么就有一个简单的多项式时间算法。@Dan:在测试我的原始答案时,我发现你的解释对于给出OP提供的示例输出是必要的,所以我认为他/她(我,因为我有一个可能的未来项目,可能需要类似的东西)会发现一个指向多项式时间算法的指针很有用。谢谢。@Simon:我为这个问题添加了一个答案。@user1887190:很乐意帮忙。我注意到你接受了这个答案,然后又不接受。如果我能提供进一步帮助,请让我知道。指数时间要求使此方案无法使用。我希望有一种我不知道的图形漫游算法可以用来解决这类问题。我所能找到的一切只有在没有周期的情况下才能起作用。据我所知,这是唯一的解决方案,在这种情况下,我非常乐意接受您的答案。@user1887190:是的,对于非常大的情况,指数时间要求将是一个问题。我会考虑其他选择。例如,可能是t