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

org.jgrapht.generate.RandomRegularGraphGenerator Maven / Gradle / Ivy

The newest version!
/*
 * (C) Copyright 2018-2018, by Emilio Cruciani and Contributors.
 *
 * JGraphT : a free Java graph-theory library
 *
 * This program and the accompanying materials are dual-licensed under
 * either
 *
 * (a) the terms of the GNU Lesser General Public License version 2.1
 * as published by the Free Software Foundation, or (at your option) any
 * later version.
 *
 * or (per the licensee's choosing)
 *
 * (b) the terms of the Eclipse Public License v1.0 as published by
 * the Eclipse Foundation.
 */

package org.jgrapht.generate;

import org.jgrapht.*;

import java.util.*;

/**
 * Generate a random $d$-regular undirected graph with $n$ vertices. A regular graph is a graph
 * where each vertex has the same degree, i.e. the same number of neighbors.
 *
 * 

* The algorithm for the simple case, proposed in [SW99] and extending the one for the non-simple * case [W99], runs in expected $\mathcal{O}(nd^2)$ time. It has been proved in [KV03] to sample * from the space of random d-regular graphs in a way which is asymptotically uniform at random when * $d = \mathcal{O}(n^{1/3 - \epsilon})$. * *

* [KV03] Kim, Jeong Han, and Van H. Vu. "Generating random regular graphs." Proceedings of the * thirty-fifth annual ACM symposium on Theory of computing. ACM, 2003. * * [SW99] Steger, Angelika, and Nicholas C. Wormald. "Generating random regular graphs quickly." * Combinatorics, Probability and Computing 8.4 (1999): 377-396. * * [W99] Wormald, Nicholas C. "Models of random regular graphs." London Mathematical Society Lecture * Note Series (1999): 239-298. * * @author Emilio Cruciani * @since March 2018 * * @param graph node type * @param graph edge type */ public class RandomRegularGraphGenerator implements GraphGenerator { private final int n; private final int d; private final Random rng; /** * Construct a new RandomRegularGraphGenerator. * * @param n number of nodes * @param d degree of nodes * @throws IllegalArgumentException if number of nodes is negative * @throws IllegalArgumentException if degree is negative * @throws IllegalArgumentException if degree is greater than number of nodes * @throws IllegalArgumentException if the value "n * d" is odd */ public RandomRegularGraphGenerator(int n, int d) { this(n, d, new Random()); } /** * Construct a new RandomRegularGraphGenerator. * * @param n number of nodes * @param d degree of nodes * @param seed seed for the random number generator * @throws IllegalArgumentException if number of nodes is negative * @throws IllegalArgumentException if degree is negative * @throws IllegalArgumentException if degree is greater than number of nodes * @throws IllegalArgumentException if the value "n * d" is odd */ public RandomRegularGraphGenerator(int n, int d, long seed) { this(n, d, new Random(seed)); } /** * Construct a new RandomRegularGraphGenerator. * * @param n number of nodes * @param d degree of nodes * @param rng the random number generator to use * @throws IllegalArgumentException if number of nodes is negative * @throws IllegalArgumentException if degree is negative * @throws IllegalArgumentException if degree is greater than number of nodes * @throws IllegalArgumentException if the value "n * d" is odd */ public RandomRegularGraphGenerator(int n, int d, Random rng) { if (n < 0) { throw new IllegalArgumentException("number of nodes must be non-negative"); } if (d < 0) { throw new IllegalArgumentException("degree of nodes must be non-negative"); } if (d > n) { throw new IllegalArgumentException( "degree of nodes must be smaller than or equal to number of nodes"); } if ((n * d) % 2 != 0) { throw new IllegalArgumentException("value 'n * d' must be even"); } this.n = n; this.d = d; this.rng = rng; } /** * Generate a random regular graph. * * @param target the target graph * @param resultMap the result map * @throws IllegalArgumentException if target is not an undirected graph * @throws IllegalArgumentException if "n == d" and the graph is simple */ @Override public void generateGraph(Graph target, Map resultMap) { // directed/mixed case if (!target.getType().isUndirected()) { throw new IllegalArgumentException("target graph must be undirected"); } if (target.getType().isSimple()) { // simple case if (this.n == 0 || this.d == 0) { // no nodes or zero degree case EmptyGraphGenerator emptyGraphGenerator = new EmptyGraphGenerator<>(this.n); emptyGraphGenerator.generateGraph(target); } else if (this.d == this.n) { throw new IllegalArgumentException("target graph must be simple if 'n == d'"); } else if (this.d == (this.n - 1)) { // complete case CompleteGraphGenerator completeGraphGenerator = new CompleteGraphGenerator<>(this.n); completeGraphGenerator.generateGraph(target); } else { // general case generateSimpleRegularGraph(target); } } else { // non-simple case generateNonSimpleRegularGraph(target); } } // auxiliary method to check if there are remaining suitable edges // used in generateSimpleRegularGraph(Graph target, VertexFactory vertexFactory) private boolean suitable( Set> edges, Map potentialEdges) { if (potentialEdges.isEmpty()) { return true; } Integer[] keys = potentialEdges.keySet().toArray(new Integer[0]); Arrays.sort(keys); for (int i = 0; i < keys.length; i++) { int s2 = keys[i]; for (int j = 0; j < i; j++) { int s1 = keys[j]; Map.Entry e = new AbstractMap.SimpleImmutableEntry<>(s1, s2); if (!edges.contains(e)) { return true; } } } return false; } // auxiliary method to manage simple case private void generateSimpleRegularGraph(Graph target) { // integers to vertices List vertices = new ArrayList<>(this.n); for (int i = 0; i < this.n; i++) { V v = target.addVertex(); if (v == null) { throw new IllegalArgumentException("Invalid vertex supplier"); } vertices.add(v); } // set of final edges to add to target graph Set> edges = new HashSet<>(this.n * this.d); do { List stubs = new ArrayList<>(this.n * this.d); for (int i = 0; i < this.n * this.d; i++) { stubs.add(i % this.n); } while (!stubs.isEmpty()) { Map potentialEdges = new HashMap<>(); Collections.shuffle(stubs, this.rng); for (int i = 0; i < stubs.size() - 1; i += 2) { int s1 = stubs.get(i); int s2 = stubs.get(i + 1); // s1 < s2 has to be true if (s1 > s2) { int temp = s1; s1 = s2; s2 = temp; } Map.Entry edge = new AbstractMap.SimpleImmutableEntry<>(s1, s2); if (s1 != s2 && !edges.contains(edge)) { edges.add(edge); } else { potentialEdges.put(s1, potentialEdges.getOrDefault(s1, 0) + 1); potentialEdges.put(s2, potentialEdges.getOrDefault(s2, 0) + 1); } } if (!suitable(edges, potentialEdges)) { edges.clear(); break; } stubs.clear(); for (Map.Entry e : potentialEdges.entrySet()) { int node = e.getKey(); int potential = e.getValue(); for (int i = 0; i < potential; i++) { stubs.add(node); } } } } while (edges.isEmpty()); // add edges to target for (Map.Entry e : edges) { target.addEdge(vertices.get(e.getKey()), vertices.get(e.getValue())); } } // auxiliary method to manage non-simple case private void generateNonSimpleRegularGraph(Graph target) { List vertices = new ArrayList<>(this.n * this.d); for (int i = 0; i < this.n; i++) { V vertex = target.addVertex(); if (vertex == null) { throw new IllegalArgumentException("Invalid vertex supplier"); } for (int j = 0; j < this.d; j++) { vertices.add(vertex); } } Collections.shuffle(vertices, this.rng); for (int i = 0; i < (this.n * this.d) / 2; i++) { V u = vertices.get(2 * i); V v = vertices.get(2 * i + 1); target.addEdge(u, v); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy