将问题中使用的语言翻译成布尔表达式,我们有:
“A 取决于 B”=>“(a 和 b)或(非 a)”
和
"B 与 A 冲突" => "(b and not a) or (not b)"
因此,一种简单的编写方法(在 Python 3 中)是生成笛卡尔积(给出所有可能的选择),然后仅选择满足约束的情况。为简单起见,我使用索引而不是字母作为输入,因此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']
...对于四个测试用例。
有关更多信息,您可以查看关于真值表的 Wikipedia 条目。
(编辑)
对于具有许多节点和许多约束的问题,一种更有效的方法是逐步构建节点列表,然后仅在该部分列表符合约束的情况下从每个部分列表继续构建,至少在它已被填充的范围内远的。我们可以通过将resolve
上面的函数替换为以下版本并替换dependsOn
andconflictsWith
函数来匹配:
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。
请注意,此处显示的实现是粗略的,当然可以针对速度进行优化。