"""Một ca cố định cho mỗi lớp, xung đột xét trên tuần xuất hiện thực tế.""" from itertools import product import clingo PROGRAM = r""" #const k=2. week(1..4). course(a;b;c). slot(1..k). standard(a,W) :- week(W). standard(b,W) :- week(W), W\2 = 1. standard(c,W) :- week(W), W\2 = 0. occurs(L,W) :- standard(L,W), not cancelled(L,W). occurs(L,W) :- extra(L,W), course(L), week(W). 1 { at(L,S) : slot(S) } 1 :- course(L). :- at(L,S), at(M,S), L < M, occurs(L,W), occurs(M,W). #show at/2. """ def schedules(extra="", slots=2): ctl = clingo.Control(["0", "--warn=none", "-c", f"k={slots}"]) ctl.add("base", [], PROGRAM + extra) ctl.ground([("base", [])]) result = set() def collect(model): mapping = {str(a.arguments[0]): a.arguments[1].number for a in model.symbols(shown=True)} result.add(tuple(mapping[l] for l in ("a", "b", "c"))) state = ctl.solve(on_model=collect) assert state.exhausted and not state.interrupted return result def oracle(weeks, slots): return {x for x in product(range(1, slots+1), repeat=3) if all(x[i] != x[j] or not (weeks[i] & weeks[j]) for i in range(3) for j in range(i+1, 3))} normal = ({1, 2, 3, 4}, {1, 3}, {2, 4}) changed = ({1, 2, 3, 4}, {1, 2}, {2, 4}) exception = "cancelled(b,3). extra(b,2)." assert schedules() == oracle(normal, 2) == {(1, 2, 2), (2, 1, 1)} assert schedules(exception) == oracle(changed, 2) == set() assert schedules(exception, 3) == oracle(changed, 3) assert len(schedules(exception, 3)) == 6 print("Lịch thường: 2 phương án.") print("Chuyển buổi B từ tuần 3 sang tuần 2: vô nghiệm với 2 ca.") print("Cho 3 ca: 6 phương án.")