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

com.salesforce.jgrapht.alg.tour.ChristofidesThreeHalvesApproxMetricTSP Maven / Gradle / Ivy

Go to download

This project contains the apt processor that implements all the checks enumerated in @Verify. It is a self contained, and shaded jar.

The newest version!
/*
 * (C) Copyright 2018-2018, by Timofey Chudakov 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 com.salesforce.jgrapht.alg.tour;

import com.salesforce.jgrapht.*;
import com.salesforce.jgrapht.alg.cycle.*;
import com.salesforce.jgrapht.alg.interfaces.*;
import com.salesforce.jgrapht.alg.matching.blossom.v5.*;
import com.salesforce.jgrapht.alg.spanning.*;
import com.salesforce.jgrapht.graph.*;

import java.util.*;
import java.util.stream.*;

/**
 * A $3/2$-approximation algorithm for the metric TSP problem.
 * 

* The travelling salesman * problem (TSP) asks the following question: "Given a list of cities and the distances between * each pair of cities, what is the shortest possible route that visits each city exactly once and * returns to the origin city?". In the metric TSP, the intercity distances satisfy the triangle * inequality. *

* This is an implementation of the * Christofides algorithm. The algorithms is a $3/2$-approximation assuming that the input graph * satisfies triangle inequality and all edge weights are nonnegative. The implementation requires * the input graph to be undirected and complete. The worst case running time * complexity is $\mathcal{O}(V^3E)$. *

* The algorithm performs following steps to compute the resulting tour: *

    *
  1. Compute a minimum spanning tree in the input graph.
  2. *
  3. Find vertices with odd degree in the MST.
  4. *
  5. Compute minimum weight perfect matching in the induced subgraph on odd degree vertices.
  6. *
  7. Add edges from the minimum weight perfect matching to the MST (forming a pseudograph).
  8. *
  9. Compute an Eulerian cycle in the obtained pseudograph and form a closed tour in this * cycle.
  10. *
*

* The following two observations yield the $3/2$ approximation bound: *

    *
  • The cost of every minimum spanning tree is less than or equal to the cost of every * Hamiltonian cycle since after one edge removal every Hamiltonian cycle becomes a spanning * tree
  • *
  • Twice the cost of a perfect matching in a graph is less than or equal to the cost of every * Hamiltonian cycle. This follows from the fact that after forming a closed tour using the edges of * a perfect matching the cost of the edges not from the matching is greater than or equal to the * cost of the matching edges.
  • *
*

* For more details, see Christofides, N.: Worst-case analysis of a new heuristic for the * travelling salesman problem. Graduate School of Industrial Administration, Carnegie Mellon * University (1976). * * @param the graph vertex type * @param the graph edge type * @author Timofey Chudakov * @author Dimitrios Michail */ public class ChristofidesThreeHalvesApproxMetricTSP implements HamiltonianCycleAlgorithm { /** * Empty constructor */ public ChristofidesThreeHalvesApproxMetricTSP() { } /** * Computes a $3/2$-approximate tour. * * @param graph the input graph * @return a tour * @throws IllegalArgumentException if the graph is not undirected * @throws IllegalArgumentException if the graph is not complete * @throws IllegalArgumentException if the graph contains no vertices */ @Override public GraphPath getTour(Graph graph) { if (!graph.getType().isUndirected()) { throw new IllegalArgumentException("Graph must be undirected"); } if (!GraphTests.isComplete(graph)) { throw new IllegalArgumentException("Graph must be complete"); } if (graph.vertexSet().isEmpty()) { throw new IllegalArgumentException("Graph contains no vertices"); } /* * Special case singleton vertex */ if (graph.vertexSet().size() == 1) { V start = graph.vertexSet().iterator().next(); return new GraphWalk<>( graph, start, start, Collections.singletonList(start), Collections.emptyList(), 0d); } int n = graph.vertexSet().size(); // add all vertices of the graph to the auxiliary graph Graph mstAndMatching = new Pseudograph<>(DefaultEdge.class); graph.vertexSet().forEach(mstAndMatching::addVertex); // add all edges of a minimum spanning tree to the auxiliary graph SpanningTreeAlgorithm spanningTreeAlgorithm = new KruskalMinimumSpanningTree<>(graph); spanningTreeAlgorithm.getSpanningTree().getEdges().forEach( e -> mstAndMatching.addEdge(graph.getEdgeSource(e), graph.getEdgeTarget(e))); // find odd degree vertices Set oddDegreeVertices = mstAndMatching .vertexSet().stream().filter(v -> (mstAndMatching.edgesOf(v).size() & 1) == 1) .collect(Collectors.toSet()); /* * Form an induced subgraph on odd degree vertices, find minimum weight perfect matching and * add its edges to the auxiliary graph */ Graph subgraph = new AsSubgraph<>(graph, oddDegreeVertices); MatchingAlgorithm matchingAlgorithm = new KolmogorovWeightedPerfectMatching<>(subgraph); matchingAlgorithm.getMatching().getEdges().forEach( e -> mstAndMatching.addEdge(graph.getEdgeSource(e), graph.getEdgeTarget(e))); // find an Eulerian cycle in the auxiliary graph EulerianCycleAlgorithm eulerianCycleAlgorithm = new HierholzerEulerianCycle<>(); GraphPath eulerianCycle = eulerianCycleAlgorithm.getEulerianCycle(mstAndMatching); // form a closed tour from the Hamiltonian cycle Set visited = new HashSet<>(n); List tourVertices = eulerianCycle .getVertexList().stream().filter(visited::add).collect(Collectors.toList()); tourVertices.add(tourVertices.get(0)); // compute tour edges List tourEdges = new ArrayList<>(n); double tourWeight = 0; V prev; V next = tourVertices.get(0); for (int i = 1; i <= n; i++) { prev = next; next = tourVertices.get(i); E edge = graph.getEdge(prev, next); tourEdges.add(edge); tourWeight += graph.getEdgeWeight(edge); } return new GraphWalk<>( graph, tourVertices.get(0), tourVertices.get(0), tourVertices, tourEdges, tourWeight); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy