-
Notifications
You must be signed in to change notification settings - Fork 3
[Model] CyclicOrdering #917
Description
Motivation
CYCLIC ORDERING (MS2) from Garey & Johnson, A1.2. Given a set A and a collection of ordered triples, determine whether there is a one-to-one mapping f: A → {1,...,|A|} that respects all cyclic order constraints. NP-complete (Galil and Megiddo 1977). The problem captures cyclic sequencing constraints arising in scheduling on circular resources, DNA physical mapping, and circular genome assembly.
Associated rules:
- [Rule] 3-Satisfiability to Cyclic Ordering #918: 3-Satisfiability → CyclicOrdering (this model is the target; original NP-completeness proof)
Definition
Name: CyclicOrdering
Reference: Garey & Johnson, Computers and Intractability, A1.2 MS2; Galil and Megiddo 1977
Mathematical definition:
INSTANCE: Finite set A, collection C of ordered triples (a, b, c) of distinct elements from A.
QUESTION: Is there a one-to-one function f: A → {1, 2, ..., |A|} such that, for each ordered triple (a, b, c) ∈ C, we have either f(a) < f(b) < f(c), or f(b) < f(c) < f(a), or f(c) < f(a) < f(b)? (That is, a, b, c appear in this cyclic order under f.)
Variables
- Count: |A| (one per element)
- Per-variable domain: {1, 2, ..., |A|} (a permutation)
- Meaning: The position assigned to each element in the cyclic sequence. The assignment must be a permutation, and every ordered triple constraint must be satisfied cyclically.
Schema (data type)
Type name: CyclicOrdering
Variants: none (unique input structure)
| Field | Type | Description |
|---|---|---|
num_elements |
usize |
Number of elements |
triples |
Vec<(usize, usize, usize)> |
Ordered triples (a, b, c) encoding cyclic order constraints |
Getter methods: num_elements(), num_triples() (= triples.len())
Complexity
- Best known exact algorithm: The problem is NP-complete [Galil and Megiddo, 1977]. For the general case, no algorithm faster than brute-force permutation enumeration is known: enumerate all |A|! permutations and check each triple in O(|C|) time, giving O(|A|! · |C|) total. For special cases (e.g., when the constraint structure forms an interval graph on the cyclic order), polynomial algorithms exist.
declare_variants!complexity string:"factorial(num_elements)"
Extra Remark
Full book text:
INSTANCE: Finite set A, collection C of ordered triples (a, b, c) of distinct elements from A.
QUESTION: Is there a one-to-one function f: A → {1, 2, ..., |A|} such that for each ordered triple (a, b, c) ∈ C, either f(a) < f(b) < f(c), or f(b) < f(c) < f(a), or f(c) < f(a) < f(b)?
Reference: [Galil and Megiddo, 1977]. Transformation from 3-SATISFIABILITY.
How to solve
- It can be solved by (existing) bruteforce. (Enumerate all |A|! permutations; check each triple constraint in O(|C|) time.)
- It can be solved by reducing to integer programming. (Binary ILP: x_{i,p} ∈ {0,1} indicating element i is at position p, with assignment and cyclic ordering constraints.)
- Other:
Example Instance
YES Instance:
A = {0, 1, 2, 3, 4} (5 elements, indexed 0..4).
Triples: C = {(0, 1, 2), (2, 3, 0), (1, 3, 4)}.
Expected Outcome: YES with f(0)=2, f(1)=4, f(2)=5, f(3)=1, f(4)=3.
Verification of each triple:
- (0, 1, 2): f(0)=2 < f(1)=4 < f(2)=5 [direct order] ✓
- (2, 3, 0): f(3)=1 < f(0)=2 < f(2)=5, i.e., f(b) < f(c) < f(a) [wrap-around] ✓
- (1, 3, 4): f(3)=1 < f(4)=3 < f(1)=4, i.e., f(b) < f(c) < f(a) [wrap-around] ✓
Note: The triple (2, 3, 0) forces a wrap-around condition — element 0 has a smaller position than element 2, so the cyclic order wraps past |A|. Out of 120 total permutations, exactly 10 satisfy all three constraints.
NO Instance:
A = {0, 1, 2, 3, 4} (5 elements).
Triples: C = {(0, 1, 2), (2, 1, 0), (1, 3, 4)}.
Expected Outcome: NO.
The triples (0, 1, 2) and (2, 1, 0) demand opposite cyclic orientations of elements 0, 1, 2: the first requires the cyclic order 0→1→2, while the second requires 2→1→0. These are contradictory regardless of the positions of elements 3 and 4. Brute-force confirms 0 out of 120 permutations satisfy all constraints.
Validation script
from itertools import permutations
def check_cyclic_order(f, a, b, c):
fa, fb, fc = f[a], f[b], f[c]
return (fa < fb < fc) or (fb < fc < fa) or (fc < fa < fb)
def solve(num_elements, triples):
solutions = []
for perm in permutations(range(num_elements)):
f = {i: perm[i] + 1 for i in range(num_elements)}
if all(check_cyclic_order(f, a, b, c) for a, b, c in triples):
solutions.append(tuple(perm[i] + 1 for i in range(num_elements)))
return solutions
# YES instance
sols = solve(5, [(0, 1, 2), (2, 3, 0), (1, 3, 4)])
assert len(sols) == 10
assert (2, 4, 5, 1, 3) in sols
# NO instance
sols_no = solve(5, [(0, 1, 2), (2, 1, 0), (1, 3, 4)])
assert len(sols_no) == 0Metadata
Metadata
Assignees
Labels
Type
Projects
Status