All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jgrapht.alg.tour.PalmerHamiltonianCycle Maven / Gradle / Ivy

/*
 * (C) Copyright 2018-2021, by Alexandru Valeanu and Contributors.
 *
 * JGraphT : a free Java graph-theory library
 *
 * See the CONTRIBUTORS.md file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0, or the
 * GNU Lesser General Public License v2.1 or later
 * which is available at
 * http://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR LGPL-2.1-or-later
 */
package org.jgrapht.alg.tour;

import org.jgrapht.*;

import java.util.*;

import static org.jgrapht.util.ArrayUtil.*;

/**
 * Palmer's algorithm for computing Hamiltonian cycles in graphs that meet Ore's condition. Ore gave
 * a sufficient condition for a graph to be Hamiltonian, essentially stating that a graph with
 * sufficiently many edges must contain a Hamilton cycle.
 *
 * Specifically, Ore's theorem considers the sum of the degrees of pairs of non-adjacent vertices:
 * if every such pair has a sum that at least equals the total number of vertices in the graph, then
 * the graph is Hamiltonian.
 *
 * 

* A Hamiltonian cycle, also called a Hamiltonian circuit, Hamilton cycle, or Hamilton circuit, is a * graph cycle (i.e., closed loop) through a graph that visits each node exactly once (Skiena 1990, * p. 196). *

* *

* This is an implementation of the CRISS-CROSS algorithm described by E. M. Palmer in his paper. * The algorithm takes a simple graph that meets Ore's condition (see * {@link GraphTests#hasOreProperty(Graph)}) and returns a Hamiltonian cycle. The algorithm runs in * $O(|V|^3)$ time and uses $O(|V|)$ space. In contrast to the most other algorithms in this package * this algorithm does only attempt to find any Hamiltonian cycle in the graph and does not attempt * to find the shortest cycle. The advantage of this algorithm is that accepted graphs only need to * meet Ore's condition which is less strict than the completeness requirement of most of the other * algorithms. *

* *

* The original algorithm is described in: Palmer, E. M. (1997), "The hidden algorithm of Ore's * theorem on Hamiltonian cycles", Computers & Mathematics with Applications, 34 (11): 113–119, * doi:10.1016/S0898-1221(97)00225-3 * * See wikipedia for a short description * of Ore's theorem and Palmer's algorithm. *

* * @param the graph vertex type * @param the graph edge type * * @author Alexandru Valeanu * @author Hannes Wellmann */ public class PalmerHamiltonianCycle extends HamiltonianCycleAlgorithmBase { /** * Computes a Hamiltonian tour. * * @param graph the input graph * @return a Hamiltonian tour * * @throws IllegalArgumentException if the graph doesn't meet Ore's condition * @see GraphTests#hasOreProperty(Graph) */ @Override public GraphPath getTour(Graph graph) { if (!GraphTests.hasOreProperty(graph)) { // requires vertexSet().size() >= 3 throw new IllegalArgumentException("Graph doesn't have Ore's property"); } Set vertices = graph.vertexSet(); final int n = vertices.size(); // number of vertices @SuppressWarnings("unchecked") V[] tour = (V[]) vertices.toArray(new Object[n + 1]); while (searchAndCloseGap(tour, n, graph)) { // repeat until not gap exists anymore } tour[n] = tour[0]; // close tour manually. Arrays.asList does not support add return closedVertexListToTour(Arrays.asList(tour), graph); } private static boolean searchAndCloseGap(V[] tour, final int n, Graph graph) { // search for a gap, i.e.: two consecutive vertices v and vN that are not adjacent in the // graph (connected by an edge) V v = tour[n - 1]; // start with last so "last to first"-connection is checked first for (int i = 0; i < n; i++) { V vN = tour[i]; // vN - the successor of v, i - its index // check if we found a gap in our cycle if (!graph.containsEdge(v, vN)) { // Search for a node 'u' such that the four vertices v, vN, u, and uN are all // distinct and that the graph contains edges from v to u and from vN to uN // ("a pair of crossing chords from the vertices of the gap to some other pair of // consecutive vertices that may or may not be adjacent") V u = tour[n - 1]; // again, start with last vertex for (int j = 0; j < n; j++) { V uN = tour[j]; // uN - the successor of u, j - its index boolean distinct = v != u && vN != u && v != uN; // v != u implies vN != uN if (distinct && graph.containsEdge(v, u) && graph.containsEdge(vN, uN)) { // found "a pair of crossing chords" reverseInCircle(tour, i, j - 1); return true; } u = uN; } throw new IllegalStateException("Found a gap but no mean to close it"); } v = vN; } return false; } private static void reverseInCircle(V[] array, int start, int end) { if (start < end) { // interval to reverse is completely contained in the array bounds reverse(array, start, end); } else { // interval to reverse wraps around the array end // Happens if the first gap to swap is closer to the "end" than the second gap. // Since it is all relative, instead of swapping "over the end" the opposite segment // (within the array) is swapped. reverse(array, end + 1, start - 1); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy