Considere que temos 3 gabinetes e queremos distribuir 4 pessoas (Ana=1, Nuno=2, Pedro=3 e Maria=4) por esses gabinetes.
Considere ainda que foram estipuladas as seguintes regras de ocupação dos gabinetes:
Pretende-se que escreva um programa Python que, usando o Z3 como SAT solver, faça a distribuição das pessoas pelos gabinetes.
Começe por instalar o Z3.
!pip install z3-solver
Para resolver o problema em Lógica Proposicional temos que
Declarar um conjunto de variáveis Boolenas 𝑥𝑝,𝑔 com a seguinte semântica:
𝑥𝑝,𝑔 é verdade sse a pessoa 𝑝 ocupa o gabinete 𝑔
De seguida, teremos que modelar cada uma das restrições, acrescentando as fórmulas lógicas correspondentes.
Finalmente testamos se o conjunto de restrições é satisfazível e extraimos a solução calculada.
from z3 import *
pessoas = ["Ana","Nuno","Pedro","Maria"]
gabs = [1,2,3]
x = {}
for p in pessoas:
x[p] = {}
for g in gabs:
x[p][g] = Bool("%s,%d" % (p,g))
s = Solver()
# A melhor maneira para se fazer, é dividir a restrição "Cada pessoa ocupa um único gabinete." em duas:
# 1) Cada pessoa ocupa pelo menos um gabinete.
for p in pessoas:
s.add(Or([x[p][g] for g in gabs]))
# 2) Cada pessoa ocupa no máximo um gabinete.
for p in pessoas:
s.add(Implies(x[p][1], And(Not(x[p][2]),Not(x[p][3])) ))
s.add(Implies(x[p][2]),Not(x[p][3]))
# OU DESTA MANEIRA
for p in pessoas:
for g in range[1,3]:
s.add(Implies(x[p][g], And(Not (x[p][h]) for h in range(g+1,4))))
# O Nuno e o Pedro não podem partilhar gabinete.
for g in gabs:
s.add(Implies(x["Pedro"][g], Not(x["Nuno"][g])))
# Se a Ana ficar sozinha num gabinete, então o Pedro também terá que ficar sozinho num gabinete.
anaSo = Or([ And(x["Ana"][g],Not(x["Pedro"][g]),
Not(x["Maria"][g]),Not(x["Nuno"][g])) for g in gabs])
pedroSo = Or([ And(x["Pedro"][g],Not(x["Ana"][g]),
Not(x["Maria"][g]),Not(x["Nuno"][g])) for g in gabs])
s.add(Implies(anaSo,pedroSo))
# Cada gabinete só pode acomodar, no máximo, 2 pessoas.
comb = [("Ana","Nuno","Pedro","Maria"),("Ana","Pedro","Nuno","Maria"),
("Ana","Maria","Nuno","Pedro"),("Nuno","Pedro","Ana","Maria"),
("Nuno","Maria","Ana","Pedro"),("Pedro","Maria","Nuno","Ana")]
for g in gabs:
s.add(And([Implies(And(x[p1][g],x[p2][g]), And(Not(x[p3][g]),Not(x[p4][g])))
for (p1,p2,p3,p4) in comb]))
s.push()
if s.check() == sat:
m = s.model()
for p in pessoas:
for g in gabs:
if is_true(m[x[p][g]]):
print("%s fica no gabinete %s" % (p,g))
else:
print("Não tem solução.")
Será que há várias alternativas para distribuir os gabinetes seguindo estas regras? Faça as alterações necessárias ao programa de modo a saber todas as distribuições possíveis.
s.push()
i=0
while s.check() == sat:
i+=1
m = s.model()
f=[]
for p in pessoas:
for g in gabs:
if is_true(m[x[p][g]]):
print("%s fica no gabinete %s" % (p,g))
f.append(Not(x[p][g]))
else:
f.append(x[p][g])
s.add(Or(f)) # Guardamos, para usa-lo como restrição para que não se volte a encontrar a mesma solução
else:
print("Não tem solução.")
Colab: