【问题标题】:How does one create a "at least n" constraint?如何创建“至少 n”约束?
【发布时间】:2019-08-13 20:08:06
【问题描述】:

我是约束编程的新手,并试图弄清楚如何做一个“至少 n”的约束。

例如,我的 int 变量 x、y 和 z 都在 0 到 5 的范围内。

现在我想要所有解决方案,其中至少有 2 个变量在 2 到 3 之间。

所以类似于“给定条件的总和 >= 2”

我将如何在 python 中执行此操作,最好使用 Google 的 OR-Tools?

谢谢

【问题讨论】:

  • 您可以为此使用 reified 约束:developers.google.com/optimization/cp/channeling
  • 谢谢@WillemVanOnsem - 有没有更可扩展的版本?我需要增加它以找到解决方案,其中 6 个变量的组合必须适合 10 个插槽的表。例如,我需要安排 6 名教师轮班 8 小时,每班有 30 分钟的休息时间,并确保在上午 8 点到下午 12 点之间房间里至少有 4 名教师,在下午 12 点到下午 6 点之间房间里至少有 5 名教师。
  • @Christine 我在这里看不到可扩展性问题。使用纯 SAT 求解时的 1000 var / 10000 槽问题会吓到我。但是具有这些数字的(懒惰的)CP-SAT Hybrid(尽管很难预见这种小范围的行为)......好吧......先试试。当然,模型的其余部分同样重要,如果没有更多,那么 CP-SAT 是否应该优于 MIP 方法恕我直言。
  • 首先,我根本不会使用整数变量。只是布尔变量。其次,在这类问题上,我的经验是CP-SAT比CBC/SCIP好,比Gurobi/CPLEX差。

标签: python algorithm constraints constraint-programming or-tools


【解决方案1】:
from ortools.sat.python import cp_model
import collections


class SolutionPrinter(cp_model.CpSolverSolutionCallback):
    """Print intermediate solutions."""

    def __init__(self, variables):
        cp_model.CpSolverSolutionCallback.__init__(self)
        self.__variables = variables
        self.__num_vars = len(variables)
        self.__num_values = len(variables[0])
        self.__solution_count = 0

    def on_solution_callback(self):
        self.__solution_count += 1
        for var in range(self.__num_vars):
            for value in range(self.__num_values):
                if self.BooleanValue(self.__variables[var][value]):
                    print('var[%i]=%i' % (var, value), end=' ')
                    break
        print()

    def solution_count(self):
        return self.__solution_count


num_vars = 3
max_values = 5

model = cp_model.CpModel()
vars = collections.defaultdict(list)

for var in range(num_vars):
    for value in range(max_values + 1):
        vars[var].append(model.NewBoolVar('x_%i_%i' % (var, value)))

# Exactly one value per variable
for var in range(num_vars):
    model.Add(sum(vars[var]) == 1)

# At least 2 between 2 and 3
model.Add(sum(vars[var][2] for var in range(num_vars)) +
          sum(vars[var][3] for var in range(num_vars)) >= 2)

# Enumerate all solutions
solver = cp_model.CpSolver()
solution_printer = SolutionPrinter(vars)
status = solver.SearchForAllSolutions(model, solution_printer)

print('Status = %s' % solver.StatusName(status))
print('Number of solutions found: %i' % solution_printer.solution_count())

输出

var[0]=3      var[1]=2      var[2]=0 
var[0]=3      var[1]=2      var[2]=5 
var[0]=3      var[1]=2      var[2]=4 
var[0]=3      var[1]=2      var[2]=1 
var[0]=3      var[1]=2      var[2]=3 
var[0]=3      var[1]=0      var[2]=3 
var[0]=3      var[1]=1      var[2]=3 
var[0]=3      var[1]=5      var[2]=3 
var[0]=3      var[1]=4      var[2]=3 
var[0]=3      var[1]=4      var[2]=2 
var[0]=3      var[1]=0      var[2]=2 
var[0]=3      var[1]=1      var[2]=2 
var[0]=3      var[1]=2      var[2]=2 
var[0]=3      var[1]=5      var[2]=2 
var[0]=3      var[1]=3      var[2]=2 
var[0]=3      var[1]=3      var[2]=4 
var[0]=3      var[1]=3      var[2]=5 
var[0]=3      var[1]=3      var[2]=1 
var[0]=3      var[1]=3      var[2]=0 
var[0]=3      var[1]=3      var[2]=3 
var[0]=5      var[1]=3      var[2]=3 
var[0]=1      var[1]=3      var[2]=3 
var[0]=4      var[1]=3      var[2]=3 
var[0]=2      var[1]=3      var[2]=3 
var[0]=0      var[1]=3      var[2]=3 
var[0]=2      var[1]=3      var[2]=0 
var[0]=2      var[1]=3      var[2]=5 
var[0]=2      var[1]=3      var[2]=4 
var[0]=2      var[1]=3      var[2]=1 
var[0]=2      var[1]=3      var[2]=2 
var[0]=5      var[1]=3      var[2]=2 
var[0]=1      var[1]=3      var[2]=2 
var[0]=4      var[1]=3      var[2]=2 
var[0]=0      var[1]=3      var[2]=2 
var[0]=0      var[1]=2      var[2]=2 
var[0]=5      var[1]=2      var[2]=2 
var[0]=4      var[1]=2      var[2]=2 
var[0]=1      var[1]=2      var[2]=2 
var[0]=2      var[1]=2      var[2]=2 
var[0]=2      var[1]=0      var[2]=2 
var[0]=2      var[1]=1      var[2]=2 
var[0]=2      var[1]=5      var[2]=2 
var[0]=2      var[1]=4      var[2]=2 
var[0]=2      var[1]=2      var[2]=1 
var[0]=2      var[1]=2      var[2]=0 
var[0]=2      var[1]=2      var[2]=5 
var[0]=2      var[1]=2      var[2]=4 
var[0]=2      var[1]=2      var[2]=3 
var[0]=2      var[1]=0      var[2]=3 
var[0]=2      var[1]=1      var[2]=3 
var[0]=5      var[1]=2      var[2]=3 
var[0]=4      var[1]=2      var[2]=3 
var[0]=1      var[1]=2      var[2]=3 
var[0]=2      var[1]=5      var[2]=3 
var[0]=2      var[1]=4      var[2]=3 
var[0]=0      var[1]=2      var[2]=3 
Status = OPTIMAL
Number of solutions found: 56

【讨论】:

    【解决方案2】:

    假设你有这些条件

    x = 2.5
    y = 4
    z = 2.9
    

    然后你可以建立一个值列表,然后应用lambdamapsum 来找出答案。例如:

    # Create the list of values
    values = [x, y, z]
    
    # Apply a lambda to each element of the list, checking if they
    # are within [2,3]. This would return a boolean, so I am translating
    # those to 1 and 0
    ones_or_zeroes = map(lambda x: 1 if 2<=x<=3 else 0, values)
    
    # Check condition of how many matched the condition:
    condition = sum(ones_or_zeroes)
    

    当然可以压缩为:

    if sum(map(lambda x: 1 if 2<=x<=3 else 0, values)) >= 2:
        # then at least 2 variables match the costraint
        pass
    

    【讨论】:

      猜你喜欢
      • 2021-01-28
      • 1970-01-01
      • 2018-11-16
      • 2020-04-21
      • 2015-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-08
      相关资源
      最近更新 更多