import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; class Simulation { // We don't need no steenkin command line arguments, we got constants static final int REDS = 1000, BLACKS = 1000, NODES = REDS + BLACKS; static final int DEGREE = 10, JOINS = 5, COLUMNS = 20, WALK_STEPS = 6; static final int ROUNDS = 10000, SWAPS_PER_ROUND = 1000 * 1000; static final int SWAP_LIMIT = SWAPS_PER_ROUND * WALK_STEPS / NODES; ArrayList reds, blacks, nodes; int[] loc = new int[COLUMNS]; int round; Simulation() { // Create two separate networks each covering the whole keyspace reds = new ArrayList (REDS); for (int i = 0; i < REDS; i++) reds.add (new Node()); makeKleinbergNetwork (reds); blacks = new ArrayList (BLACKS); for (int i = 0; i < BLACKS; i++) blacks.add (new Node()); makeKleinbergNetwork (blacks); // Join the networks at a few randomly chosen points for (int i = 0; i < JOINS; i++) { Node a = reds.get ((int) (Math.random() * REDS)); Node b = blacks.get ((int) (Math.random() * BLACKS)); a.neighbours.add (b); b.neighbours.add (a); } // Allow the networks to swap with each other nodes = new ArrayList (NODES); nodes.addAll (reds); nodes.addAll (blacks); // See whether the locations become segregated for (round = 1; round <= ROUNDS; round++) { printLocations(); swapLocations(); } } void printLocations() { // Print the location distribution of the reds for (Node r : reds) loc[(int) (r.location * COLUMNS)]++; for (int i = 0; i < COLUMNS; i++) { System.out.print (loc[i] + " "); loc[i] = 0; } } void swapLocations() { int swapsAccepted = 0; for (int i = 0; i < SWAPS_PER_ROUND; i++) { Node a = nodes.get ((int) (Math.random() * NODES)); if (a.neighbours.isEmpty()) continue; Node b = loopingRandomWalk (a, WALK_STEPS); if (b == null) continue; // Too much swap traffic swapsAccepted++; if (shouldSwap (a, b)) { double temp = a.location; a.location = b.location; b.location = temp; } } System.out.println ((double) swapsAccepted / SWAPS_PER_ROUND); } Node loopingRandomWalk (Node n, int steps) { if ((double) n.swapTraffic++ / round > SWAP_LIMIT) return null; if (steps == 0) return n; ArrayList neighbours = new ArrayList (n.neighbours); int random = (int) (Math.random() * neighbours.size()); return loopingRandomWalk (neighbours.get (random), steps - 1); } static double distance (Node a, Node b) { double x = a.location, y = b.location; if (x > y) return Math.min (x - y, y - x + 1.0); else return Math.min (y - x, x - y + 1.0); } static boolean shouldSwap (Node a, Node b) { double before = 1.0; for (Node c : a.neighbours) before *= distance (a, c); for (Node d : b.neighbours) before *= distance (b, d); double after = 1.0; for (Node c : a.neighbours) after *= distance (b, c); for (Node d : b.neighbours) after *= distance (a, d); if (after <= before) return true; else return Math.random() < before / after; } static void makeKleinbergNetwork (ArrayList nodes) { for (Node a : nodes) { // Normalise the probabilities double norm = 0.0; for (Node b : nodes) { if (a.location == b.location) continue; norm += 1.0 / distance (a, b); } // Create DEGREE/2 outgoing connections for (Node b : nodes) { if (a.location == b.location) continue; double p = 1.0 / distance (a, b) / norm; for (int i = 0; i < DEGREE / 2; i++) { if (Math.random() < p) { a.neighbours.add (b); b.neighbours.add (a); break; } } } } } public static void main (String[] args) { new Simulation(); } class Node { HashSet neighbours = new HashSet(); double location = Math.random(); int swapTraffic = 0; } }