package edu.hawaii.ics.yucheng; import java.util.ArrayList; import java.util.Collection; /** * A class that generates random graphs. */ class GraphRandomizer { /** * Builds a random number of subtrees into the graph recursively. This method * works on subtrees at a depth of one or greater in the tree. * * @param indices The vertex indices. * * @param root The root index of the tree. * * @param end The end index (not assigned in this method) * * @param edges The collection of edges. */ private static void buildDeepSubtrees(final int[] indices, final int root, final int end, final Collection<Edge> edges) { // The next vertex is always a child if there are any children. int child = root + 1; // Loop until all children or ancestors are connected. while (child < end) { // Connect this child vertex. edges.add(new Edge(indices[root], indices[child])); // Determine the next child. final int nextChild = random(child + 1, end); // Recursively build deeper trees for the ancestors, if any. buildDeepSubtrees(indices, child, nextChild, edges); // Loop until all children or ancestors are connected. child = nextChild; } } /** * Builds a random number of loops into the graph. * * @param edges The collection of edges. * * @param numVertices The number of vertices. */ @SuppressWarnings("boxing") private static void buildLoops(final Collection<Edge> edges, final int numVertices) { // Use a matrix to keep track of the connections. final Matrix<Boolean> matrix = new Matrix<Boolean>(numVertices, numVertices, false); // Initialize the matrix of connections. for (final Edge e : edges) { matrix.set(e.first(), e.second(), true); matrix.set(e.second(), e.first(), true); } // Loop over each possible edge, and connect the edge to form a loop // arbitrarily 25% of the time. for (int i = 0; i < numVertices; i++) for (int j = i + 1; j < numVertices; j++) if (!matrix.get(i, j)) if (random(0, 4) == 0) { matrix.set(i, j, true); matrix.set(j, i, true); edges.add(new Edge(i, j)); } } /** * Builds a random number of subtrees into the graph. * * @param indices The vertex indices. * * @return The collection of edges. */ private static Collection<Edge> buildSubtrees(final int[] indices) { // Create a new collection of edges that will be built and returned. final Collection<Edge> edges = new ArrayList<Edge>(); // Loop over each vertex at depth 0. int from = 0; while (from + 1 < indices.length) { // Select a random number of children for its tree. final int to = random(from + 1, indices.length + 1); // Connect this root vertex to the next root vertex if there is one. if (to < indices.length) edges.add(new Edge(indices[from], indices[to])); // Recursively build deeper subtrees. buildDeepSubtrees(indices, from, to, edges); // Loop until all vertices are connected. from = to; } // Return the collection of edges created above. return edges; } /** * Creates a random, valid graph with a number of vertices between min and * (max - 1). * * @param min The minimum number of vertices (inclusive). * * @param max The maximum number of vertices (exclusive). * * @return A new graph. */ public static Graph create(final int min, final int max) { if (min <= 0) throw new IllegalArgumentException("min"); if (max <= min) throw new IllegalArgumentException("max"); // Randomly create a number of vertices, weighted between 0..99. final float[] vertices = new float[random(min, max)]; for (int i = 0; i < vertices.length; i++) vertices[i] = random(0, 100); // Create an random array of edges that will connect each vertex. final int[] indices = new int[vertices.length]; for (int i = 0; i < indices.length; i++) indices[i] = i; for (int i = 0; i < indices.length; i++) swap(indices, i, random(0, indices.length)); // Create subtrees for some of the vertices. final Collection<Edge> edges = buildSubtrees(indices); // Create a series of random loops. buildLoops(edges, vertices.length); // Store the final result. return new Graph(vertices, edges); } /** * Returns a random number between low and high. * * @param low The low value (inclusive). * * @param high The high value (exclusive). * * @return A pseudo-random number. */ private static int random(final int low, final int high) { return (int) (Math.random() * (high - low) + low); } /** * Swaps two values in an array. * * @param array The array. * * @param a The first index. * * @param b The second index. */ private static void swap(final int[] array, final int a, final int b) { final int temp = array[a]; array[a] = array[b]; array[b] = temp; } }