You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

211 lines
5.2 KiB
Java

import java.util.*;
import java.util.stream.Stream;
// https://en.wikipedia.org/wiki/Pr?fer_sequence
public class RandomGraph {
public static List<Integer>[] getRandomTree2(int n, Random rnd) {
List<Integer>[] t = Stream.generate(ArrayList::new).limit(n).toArray(List[]::new);
int[] p = new int[n];
for (int i = 0, j; i < n; j = rnd.nextInt(i + 1), p[i] = p[j], p[j] = i, i++) ; // random permutation
for (int i = 1; i < n; i++) {
int parent = p[rnd.nextInt(i)];
t[parent].add(p[i]);
t[p[i]].add(parent);
}
return t;
}
public static List<Integer>[] pruferCode2Tree(int[] pruferCode) {
int n = pruferCode.length + 2;
List<Integer>[] tree = Stream.generate(ArrayList::new).limit(n).toArray(List[]::new);
int[] degree = new int[n];
Arrays.fill(degree, 1);
for (int v : pruferCode)
++degree[v];
int ptr = 0;
while (degree[ptr] != 1)
++ptr;
int leaf = ptr;
for (int v : pruferCode) {
tree[leaf].add(v);
tree[v].add(leaf);
--degree[leaf];
--degree[v];
if (degree[v] == 1 && v < ptr) {
leaf = v;
} else {
for (++ptr; ptr < n && degree[ptr] != 1; ++ptr) ;
leaf = ptr;
}
}
for (int v = 0; v < n - 1; v++) {
if (degree[v] == 1) {
tree[v].add(n - 1);
tree[n - 1].add(v);
}
}
return tree;
}
public static int[] tree2PruferCode(List<Integer>[] tree) {
int n = tree.length;
int[] parent = new int[n];
parent[n - 1] = -1;
pruferDfs(tree, parent, n - 1);
int[] degree = new int[n];
int ptr = -1;
for (int i = 0; i < n; ++i) {
degree[i] = tree[i].size();
if (degree[i] == 1 && ptr == -1)
ptr = i;
}
int[] res = new int[n - 2];
int leaf = ptr;
for (int i = 0; i < n - 2; ++i) {
int next = parent[leaf];
res[i] = next;
--degree[next];
if (degree[next] == 1 && next < ptr) {
leaf = next;
} else {
++ptr;
while (ptr < n && degree[ptr] != 1)
++ptr;
leaf = ptr;
}
}
return res;
}
static void pruferDfs(List<Integer>[] tree, int[] parent, int v) {
for (int i = 0; i < tree[v].size(); ++i) {
int to = tree[v].get(i);
if (to != parent[v]) {
parent[to] = v;
pruferDfs(tree, parent, to);
}
}
}
// precondition: n >= 2
public static List<Integer>[] getRandomTree(int V, Random rnd) {
int[] a = new int[V - 2];
for (int i = 0; i < a.length; i++) {
a[i] = rnd.nextInt(V);
}
return pruferCode2Tree(a);
}
// precondition: V >= 2, V-1 <= E <= V*(V-1)/2
public static List<Integer>[] getRandomUndirectedConnectedGraph(int V, int E, Random rnd) {
List<Integer>[] g = getRandomTree(V, rnd);
Set<Long> edgeSet = new LinkedHashSet<>();
for (int i = 0; i < V; i++) {
for (int j = i + 1; j < V; j++) {
edgeSet.add(((long) i << 32) + j);
}
}
for (int i = 0; i < V; i++) {
for (int j : g[i]) {
edgeSet.remove(((long) i << 32) + j);
}
}
List<Long> edges = new ArrayList<>(edgeSet);
for (int x : getRandomArrangement(edges.size(), E - (V - 1), rnd)) {
long e = edges.get(x);
int u = (int) (e >>> 32);
int v = (int) e;
g[u].add(v);
g[v].add(u);
}
for (int i = 0; i < V; i++)
Collections.sort(g[i]);
return g;
}
// precondition: V >= 2, V-1 <= E <= V*(V-1)/2
public static List<Integer>[] getRandomUndirectedConnectedGraph2(int V, int E, Random rnd) {
List<Integer>[] g = getRandomTree(V, rnd);
Set<Long> edgeSet = new LinkedHashSet<>();
for (int i = 0; i < V; i++) {
for (int j : g[i]) {
edgeSet.add(((long) i << 32) + j);
}
}
for (int i = 0; i < E - (V - 1); i++) {
int u;
int v;
long edge;
while (true) {
u = rnd.nextInt(V);
v = rnd.nextInt(V);
edge = ((long) u << 32) + v;
if (u < v && !edgeSet.contains(edge)) break;
}
edgeSet.add(edge);
g[u].add(v);
g[v].add(u);
}
for (int i = 0; i < V; i++)
Collections.sort(g[i]);
return g;
}
static int[] getRandomArrangement(int n, int m, Random rnd) {
int[] res = new int[n];
for (int i = 0; i < n; i++) {
res[i] = i;
}
for (int i = 0; i < m; i++) {
int j = n - 1 - rnd.nextInt(n - i);
int t = res[i];
res[i] = res[j];
res[j] = t;
}
return Arrays.copyOf(res, m);
}
// Usage example
public static void main(String[] args) {
List<Integer>[] tree = pruferCode2Tree(new int[]{3, 3, 3, 4});
System.out.println(Arrays.toString(tree));
System.out.println(Arrays.toString(tree2PruferCode(tree)));
System.out.println(Arrays.toString(pruferCode2Tree(new int[]{0, 0})));
Random rnd = new Random(1);
for (int step = 0; step < 1000; step++) {
int V = rnd.nextInt(50) + 2;
checkGraph(V, V - 1, rnd);
checkGraph(V, V * (V - 1) / 2, rnd);
checkGraph(V, rnd.nextInt(V * (V - 1) / 2 - (V - 1) + 1) + V - 1, rnd);
}
}
static void checkGraph(int V, int E, Random rnd) {
List<Integer>[] g = getRandomUndirectedConnectedGraph(V, E, rnd);
int n = g.length;
int[][] a = new int[n][n];
int edges = 0;
for (int i = 0; i < n; i++) {
for (int j : g[i]) {
++a[i][j];
++edges;
}
}
if (edges != 2 * E) {
throw new RuntimeException();
}
for (int i = 0; i < n; i++) {
if (a[i][i] != 0) {
throw new RuntimeException();
}
for (int j = 0; j < n; j++) {
if (a[i][j] != a[j][i] || a[i][j] != 0 && a[i][j] != 1) {
throw new RuntimeException();
}
}
}
}
}