Turán's brick factory problem
As one of the first edge crossing problems in graph theory, Turán’s brick factory problem asks a fairly simple question: Given a complete bipartite graph arrange the vertices on the plane so as to minimized the number of edge crossings.
We assume our edges are straight line segments and that no vertex lies on an edge unless it is an endpoint of that edge. It is conjectured that a positioning strategy of Zarankiewicz achieves the minimum. K4,7 is shown in the picture below.
I thought it would be interesting to write a small bit of python code to search for a counterexample to this conjecture. We know that if a counterexample exists than it first occurs when both n and m are odd for Kn,m, and that n, m > 7.
import sys import numpy as np import random import math import matplotlib.pyplot as plt A = 11 B = 11 edges = [] for i in range(A): for j in range(B): edges.append([i,j]) def assignRand(A,B): allTuples = [] Atuples = [] Btuples = [] while len(Atuples) < A: for i in range(A): rand = (random.randint(0,1000),random.randint(0,1000)) if rand not in allTuples: Atuples.append(rand) allTuples.append(rand) while len(Btuples) < B: for i in range(B): rand = (random.randint(0,1000), random.randint(0,1000)) if rand not in allTuples: Btuples.append(rand) allTuples.append(rand) return(Atuples,Btuples) def ccw(A,B,C): return (C[1]-A[1]) * (B[0]-A[0]) > (B[1]-A[1]) * (C[0]-A[0]) # Return true if line segments AB and CD intersect def intersect(A,B,C,D): return ccw(A,C,D) != ccw(B,C,D) and ccw(A,B,C) != ccw(A,B,D) def checkCrossings(Atuples,Btuples,edges): crossings = 0 for i in range(0,len(edges)): for j in range(i+1,len(edges)): if edges[i][0] == edges[j][0]: continue if edges[i][1] == edges[j][1]: continue Ax1 = Atuples[edges[i][0]][0] Ay1 = Atuples[edges[i][0]][1] Ax2 = Atuples[edges[j][0]][0] Ay2 = Atuples[edges[j][0]][1] Bx1 = Btuples[edges[i][1]][0] By1 = Btuples[edges[i][1]][1] Bx2 = Btuples[edges[j][1]][0] By2 = Btuples[edges[j][1]][1] if Bx1 - Ax1 != 0: slope1 = (By1 - Ay1)/(Bx1 - Ax1) else: slope1 = None if Bx2 - Ax2 != 0: slope2 = (By2 - Ay2)/(Bx2 - Ax2) else: slope2 = None if (Bx1 - Ax1) == 0 and (Bx2 - Ax2) == 0: continue if slope1 is not None and slope2 is not None: if slope1 == slope2: continue crossings += intersect([Ax1,Ay1],[Bx1,By1],[Ax2,Ay2],[Bx2,By2]) return crossings target = math.floor(A/2)*math.floor((A-1)/2)*math.floor(B/2)*math.floor((B-1)/2) test = assignRand(A,B) print("Target: " + str(target)) iterations = 5000 currBestTuples = [] count = 0 test = assignRand(A,B) currBest = checkCrossings(test[0],test[1],edges) while count < iterations: group = random.randint(0,1) choice = random.randint(0,8) newCoord = (random.randint(0,1000),random.randint(0,1000)) if newCoord in test[0]: continue if newCoord in test[1]: continue oldCoord = test[group][choice] test[group][choice] = newCoord count += 1 crossings = checkCrossings(test[0],test[1],edges) if crossings < currBest: print(crossings) currBest = crossings currBestTuples = [] currBestTuples.append(test[0]) currBestTuples.append(test[1]) else: test[group][choice] = oldCoord xa = [i[0] for i in test[0]] ya = [i[1] for i in test[0]] xb = [i[0] for i in test[1]] yb = [i[1] for i in test[1]] x = xa+xb y = ya+yb colors1 = ["blue"]*A colors2 = ["red"]*B colors = colors1 + colors2 fig, ax = plt.subplots() ax.scatter(x,y,c=colors,vmin=0,vmax=1000) plt.show() print(currBestTuples)
Running the code we see that our positioning that minimizes the crossings tends towards the Zarankiewicz pattern. In the future I plan to implement a machine learning algorithm schema as well.