求解器:"simple" 逻辑问题的 OR-Tools 或 clingo(ASP) 模型

Solver: OR-Tools or clingo(ASP) model for "simple" logical problem

我尝试使用 OR-Tools 或 clingo(ASP) 解决“简单”的逻辑问题。 它是这样的: 我有一组人,比如 Person(Tony, Bob, Ann, Carl, Amber, Peter) 我还有像 Group1(Bob, Ann, Carl, Amber, Peter)Group2(Bob, Amber) 和 [=22= 这样的组]组 3(琥珀色)。 现在我想 select Group1 中的两个人,Group2 中的一个人和 Group3 中的一个人 - 所以总共有四个人满足所有限制条件。 可能的解决方案是 Carl、Ann、Bob、AmberPeter、Ann、Bob、AmberCarl、Peter、Bob , 琥珀色。 我怎样才能做到这一点?

编辑: 我在 ASP clingo:

中尝试过这个
person(tony; bob; ann; carl; amber; peter).
group1(bob; ann; carl; amber; peter).
group2(bob; amber).
group3(amber).

% rules
{select(X): person(X), group1(X)} = 2.
{select(X): person(X), group2(X)} = 1.
{select(X): person(X), group3(X)} = 1.

#show select/1.

但我只得到:

clingo version 5.5.0
Reading from stdin
Solving...
Answer: 1
select(amber) select(carl)
Answer: 2
select(amber) select(peter)
Answer: 3
select(amber) select(ann)
SATISFIABLE

Models       : 3
Calls        : 1
Time         : 0.004s (Solving: 0.00s 1st Model: 0.00s Unsat: 0.00s)
CPU Time     : 0.000s

感谢和问候

快速破解:

代码

import numpy as np
from ortools.sat.python import cp_model

# DATA
# ----
names = ['Tony', 'Bob', 'Ann', 'Carl', 'Amber', 'Peter']
matrix = np.array([
  [0, 1, 1, 1, 1, 1],     # group 0 "members": NOT Tony, Bob, Ann, ...
  [0, 1, 0, 0, 1, 0],     # group 1 "members": Bob, Amber      
  [0, 0, 0, 0, 1, 0]      # group 2 "members": Amber
])
cardinalities = [2, 1, 1] # group 0, group 1, group 2

# MODEL
# -----
model = cp_model.CpModel()
# 2d boolean assignment-matrix: person X group picked?
var_assign = np.empty(matrix.shape, dtype=object)
for a in range(matrix.shape[0]):
  for b in range(matrix.shape[1]):
    var_assign[a, b] = model.NewBoolVar('')

# forbid to pick a person from a group without being a "member"
forbidden_picks = np.where(matrix == 0)
for ind, group in enumerate(forbidden_picks[0]):
  model.Add(var_assign[group, forbidden_picks[1][ind]] == 0)

# a person is picked from at most one group
for person in range(matrix.shape[1]):
  model.Add(var_assign[:, person].sum() <= 1)

# a group is picked exactly n times 
for group, group_card in enumerate(cardinalities):
  model.Add(var_assign[group, :].sum() == group_card)

# SOLVE
# -----
class VarArraySolutionPrinter(cp_model.CpSolverSolutionCallback):
    def __init__(self, variables):
        cp_model.CpSolverSolutionCallback.__init__(self)
        self.__variables = variables

    def on_solution_callback(self):
        selected = []
        for g in range(len(cardinalities)):
          for p_ind, p in enumerate(names):
            if self.Value(self.__variables[g][p_ind]) == 1:
              selected.append(p)
        print('Solution: ', selected)

solver = cp_model.CpSolver()
solution_printer = VarArraySolutionPrinter(var_assign.tolist())
status = solver.SearchForAllSolutions(model, solution_printer)

出来

Solution:  ['Ann', 'Carl', 'Bob', 'Amber']
Solution:  ['Ann', 'Peter', 'Bob', 'Amber']
Solution:  ['Carl', 'Peter', 'Bob', 'Amber']

方法

想想一些赋值矩阵:

Tony  Bob  Ann  Carl  Amber  Peter
 -     1    1    1      1      1   = group 0 = 2
 -     1    -    -      1      -   = group 1 = 1
 -     -    -    -      1      -   = group 2 = 1 

 <=1  <=1  <=1  <=1    <=1    <=1

您在 ASP 中完美地阐述了您描述的问题,并且显示了正确的结果。您忘记说明的是,您不想 select 来自两个不同组的同一个人。

我将您的 ASP 程序修改为以下内容:

person(tony; bob; ann; carl; amber; peter).
group1(bob; ann; carl; amber; peter).
group2(bob; amber).
group3(amber).

% select the amount for each group individually
{select(1,X): group1(X)} = 2.
{select(2,X): group2(X)} = 1.
{select(3,X): group3(X)} = 1.

% you are not allowed to select a person in two groups
:- select(G,X), select(G',X), G < G'.

#show .
#show select(X) : select(G,X).

这给出了您预期的答案。