Skip to content

[Model] CyclicOrdering #917

@isPANN

Description

@isPANN

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:

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) == 0

Metadata

Metadata

Assignees

No one assigned

    Labels

    GoodAn issue passed all checks.modelA model problem to be implemented.

    Type

    No type

    Projects

    Status

    Done

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions