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.