这个问题是关于python包约束(参见http://labix.org/python-constraint),特别是内置的“ AllEqualConstraint ”。在一个问题 涉及4个变量,我想强制执行前两个和 第二个是平等的,即:
p = Problem()
p.addVariables([1,2,3,4], [0,1])
p.addConstraint(AllEqualConstraint(), [1,2])
p.addConstraint(AllEqualConstraint(), [3,4])
我只得到两个解决方案:
for sol in p.getSolutions():
print sol
> {1: 1, 2: 1, 3: 1, 4: 1}
> {1: 0, 2: 0, 3: 0, 4: 0}
我希望看到四个,即:
> {1: 1, 2: 1, 3: 1, 4: 1}
> {1: 1, 2: 1, 3: 0, 4: 0}
> {1: 0, 2: 0, 3: 1, 4: 1}
> {1: 0, 2: 0, 3: 0, 4: 0}
我的问题是:任何人都可以确认这是包打算计算的内容以及 背后的推理是?
Ps:我已经联系了这个软件包的作者,但还没有得到答复。我知道这个包是众所周知的,之前在StackOverflow上有过关于它的问题。
回答LVC : 约束 不总是将约束应用于所有变量:
p = Problem()
p.addVariables([1,2,3], [0,1])
p.addConstraint(AllEqualConstraint(), [1,2])
给出
> {1: 1, 2: 1, 3: 1}
> {1: 1, 2: 1, 3: 0}
> {1: 0, 2: 0, 3: 1}
> {1: 0, 2: 0, 3: 0}
正如所料。如果AllEqualConstraint
不尊重变量,那将非常有限。
答案 0 :(得分:2)
模块的source code包含了AllEqualConstraint
的内容:
def __call__(self, variables, domains, assignments, forwardcheck=False,
_unassigned=Unassigned):
singlevalue = _unassigned
for value in assignments.values():
if singlevalue is _unassigned:
singlevalue = value
elif value != singlevalue:
return False
在每个Solver
中调用此代码的代码执行此操作:
for constraint, variables in vconstraints[variable]:
if not constraint(variables, domains, assignments,
pushdomains):
# Value is not good.
break
assignments
包含所有变量的所有分配,而不仅仅是受约束影响的变量。这意味着AllEqualConstraint
并不关心放在addConstraint
中的变量 - 它总是测试潜在解决方案中的所有变量是否相等,这就是为什么你错过了两个解决方案的原因事实并非如此。
唯一一次AllEqualConstraint
查看variables
参数的时间是forwardcheck
调用它的时候。在这种情况下,它会这样做:
if forwardcheck and singlevalue is not _unassigned:
for variable in variables:
if variable not in assignments:
domain = domains[variable]
if singlevalue not in domain:
return False
for value in domain[:]:
if value != singlevalue:
domain.hideValue(value)
但是,所提供的求解器似乎都没有使用除默认值之外的任何forwardcheck
值来调用约束 - 在AllEqualConstraint
的情况下,False
为FunctionConstraint
。
所以,你必须指定你自己的手动约束 - 但这不是太难,因为它们只能是采用适当数量的变量的函数(它包含在Constraint.__call__
中,所以你不需要关心传递给p.addConstraint(operator.eq, [1, 2])
p.addConstraint(operator.eq, [3, 4])
的所有其他内容。
因此,您想要的约束是一个函数,它接受两个变量,并返回它们是否相等。 operator.eq
非常适合这个法案:
def all_equal(a, b, c):
return a == b == c
应该为您提供您正在寻找的结果。
因此将其缩放为两个以上的变量,您可以编写自己的函数:
def all_equal(*vars):
if not vars:
return True
return all(var == var[0] for var in vars)
或者,更一般地说:
{{1}}
答案 1 :(得分:1)
[评论太长了。]
您所做的编辑并未表明AllEqualConstraint
尊重变量限制,只是它并不总是设法完全忽略它们。 @lvc注意到的代码段做了他所说的。如果您对其进行检测,则可以看到(使用a
,b
,c
,d
作为变量名称,以便明确)
from constraint import *
p = Problem()
p.addVariables(list("abcd"), [0, 1])
p.addConstraint(AllEqualConstraint(), list("ab"))
p.addConstraint(AllEqualConstraint(), list("cd"))
print p.getSolutions()
最终产生
variables: ['c', 'd']
domains: {'a': [0, 1], 'c': [0, 1], 'b': [1], 'd': [1, 0]}
assignments: {'a': 1, 'c': 0, 'b': 1}
forwardcheck: [[1, 0]]
_unassigned: Unassigned
setting singlevalue to 1 from variable a
returning False because assigned variable c with value 0 != 1
事实上,正如@lvc所说,它强加了a
应该等于c
的约束,即使它不应该。我认为相关循环应该更像是
for key, value in assignments.items():
if key not in variables: continue
产生
[{'a': 1, 'c': 1, 'b': 1, 'd': 1}, {'a': 1, 'c': 0, 'b': 1, 'd': 0},
{'a': 0, 'c': 1, 'b': 0, 'd': 1}, {'a': 0, 'c': 0, 'b': 0, 'd': 0}]
虽然我不知道代码是如何工作的,所以很难确定。这对我来说就像一个错误。这似乎不太可能,但它看起来不像AllEqualConstraint
在代码库中的任何地方使用(在所有示例中都没有),除了它自己的docstring,其中示例测试每个变量参与的情况约束,因此错误不会出现。