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

org.jgrapht.generate.PruferTreeGenerator 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.generate;

import org.jgrapht.*;

import java.util.*;

/**
 * Generates a random tree using Prüfer sequences.
 * 
 * 

* A Prüfer sequence of length $n$ is randomly generated and converted into the corresponding tree. *

* *

* This implementation is inspired by "X. Wang, L. Wang and Y. Wu, "An Optimal Algorithm for Prufer * Codes," Journal of Software Engineering and Applications, Vol. 2 No. 2, 2009, pp. 111-115. doi: * 10.4236/jsea.2009.22016." and has a running time of $O(n)$. *

* * @param the graph vertex type * @param the graph edge type * * @author Alexandru Valeanu */ public class PruferTreeGenerator implements GraphGenerator { // number of vertices private final int n; // random number generator private final Random rng; // input Prufer sequence private final int[] inputPruferSeq; /** * Construct a new PruferTreeGenerator from an input Prüfer sequence. Note that the size of the * generated tree will be $l+2$ where $l$ is the length of the input sequence. The Prüfer * sequence must contain integers between $0$ and $l+1$ (inclusive). * * Note: In this case, the same tree will be generated every time. * * @param pruferSequence the input Prüfer sequence * @throws IllegalArgumentException if {@code n} is ≤ 0 * @throws IllegalArgumentException if {@code pruferSequence} is {@code null} * @throws IllegalArgumentException if {@code pruferSequence} is invalid. */ public PruferTreeGenerator(int[] pruferSequence) { if (Objects.isNull(pruferSequence)) { throw new IllegalArgumentException("pruferSequence cannot be null"); } this.n = pruferSequence.length + 2; this.rng = null; this.inputPruferSeq = pruferSequence.clone(); if (n <= 0) { throw new IllegalArgumentException("n must be greater than 0"); } for (int i = 0; i < n - 2; i++) { if (pruferSequence[i] < 0 || pruferSequence[i] >= n) { throw new IllegalArgumentException("invalid pruferSequence"); } } } /** * Construct a new PruferTreeGenerator. * * @param n number of vertices to be generated * @throws IllegalArgumentException if {@code n} is ≤ 0 */ public PruferTreeGenerator(int n) { this(n, new Random()); } /** * Construct a new PruferTreeGenerator. * * @param n number of vertices to be generated * @param seed seed for the random number generator * @throws IllegalArgumentException if {@code n} is ≤ 0 */ public PruferTreeGenerator(int n, long seed) { this(n, new Random(seed)); } /** * Construct a new PruferTreeGenerator * * @param n number of vertices to be generated * @param rng the random number generator to use * @throws IllegalArgumentException if {@code n} is ≤ 0 * @throws NullPointerException if {@code rng} is {@code null} */ public PruferTreeGenerator(int n, Random rng) { if (n <= 0) { throw new IllegalArgumentException("n must be greater than 0"); } this.n = n; this.rng = Objects.requireNonNull(rng, "Random number generator cannot be null"); this.inputPruferSeq = null; } /** * Generates a tree. * *

* Note: An exception will be thrown if the target graph is not empty (i.e. contains at least * one vertex) *

* * @param target the target graph * @param resultMap not used by this generator, can be null * @throws NullPointerException if {@code target} is {@code null} * @throws IllegalArgumentException if {@code target} is not undirected * @throws IllegalArgumentException if {@code target} is not empty */ @Override public void generateGraph(Graph target, Map resultMap) { GraphTests.requireUndirected(target); if (!target.vertexSet().isEmpty()) { throw new IllegalArgumentException("target graph is not empty"); } List vertexList = new ArrayList<>(n); // add vertices for (int i = 0; i < n; i++) { vertexList.add(target.addVertex()); } // base case if (n == 1) { return; } // degree stores the remaining degree (plus one) for each node. The // degree of a node in the decoded tree is one more than the number // of times it appears in the code. int[] degree = new int[n]; Arrays.fill(degree, 1); int[] pruferSeq; if (inputPruferSeq == null) { pruferSeq = new int[n - 2]; for (int i = 0; i < n - 2; i++) { pruferSeq[i] = rng.nextInt(n); ++degree[pruferSeq[i]]; } } else { pruferSeq = inputPruferSeq; } int index = -1; for (int k = 0; k < n; k++) { if (degree[k] == 1) { index = k; break; } } assert index != -1; int x = index; // set of nodes without a parent Set orphans = new HashSet<>(target.vertexSet()); for (int i = 0; i < n - 2; i++) { int y = pruferSeq[i]; orphans.remove(vertexList.get(x)); target.addEdge(vertexList.get(x), vertexList.get(y)); --degree[y]; if (y < index && degree[y] == 1) { x = y; } else { for (int k = index + 1; k < n; k++) { if (degree[k] == 1) { index = x = k; break; } } } } assert orphans.size() == 2; Iterator iterator = orphans.iterator(); V u = iterator.next(); V v = iterator.next(); target.addEdge(u, v); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy