We've been tasked with finding out how many randomly distributed discs with radius = 1 it takes to form a chain of discs from the left side to the other of a square with side length = sqrt(n), and then how many discs are required to cover the whole square.
We have code that does the job. The problem is that it takes way too long for large values of n. This is due to the function checking conditions for each disc before adding another disc over and over. When we get to, say 5000 discs, this becomes incredibly slow since it checks over 5000 discs every time it adds a new one.
Are there ways of making this code more efficient?
Here is the code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
#generates randomly spaced disks
def generate_disc(size):
return np.random.uniform(0, size), np.random.uniform(0, size), 1.0 # Generate a disk with radius 1
#finds out of two disks touch each other
def touch_disc(disc1, disc2):
x1, y1, r1 = disc1
x2, y2, r2 = disc2
distance = np.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
if distance <= (r1 + r2):
return True
return False
#find clusters of disks and returns the chains that exist
def find_chains(discs):
clusters = []
copy = list(discs)
while copy != []:
current_disc = copy[0]
connected_discs = [current_disc]
copy.remove(current_disc)
i = 0
while i < len(connected_discs):
current_disc = connected_discs[i]
for disc in copy[:]:
if touch_disc(current_disc, disc):
connected_discs.append(disc)
copy.remove(disc)
i += 1
clusters.append(connected_discs)
chains = []
for cluster in clusters:
if len(cluster) > 1:
chains.append(cluster)
return chains
#find out if one chain touches both the left and the right side
def complete_chain(chains):
x = []
for i, chain in enumerate(chains):
x_chain = []
if x != []:
break
for k in range(len(chain)):
current_x = chain[k][0]
x_chain.append(current_x)
if (min(x_chain)-1) <= 0 and (max(x_chain)+1)>=size:
x = x_chain
return True
else:
return False
def plot_discs(discs, size):
plt.figure(figsize=(8, 8)) # Set a square figure size
plt.xlim(0, size) # Set x-axis limit to [0, size]
plt.ylim(0, size) # Set y-axis limit to [0, size]
plt.gca().set_aspect('equal') # Set aspect ratio to 'equal' for square axes
for disc in discs:
x, y, r = disc
# Ensure that the disc is plotted within the range [0, size]
x = max(0, min(x, size))
y = max(0, min(y, size))
circle = plt.Circle((x, y), r, edgecolor='blue', fill=True, alpha = 0.5)
plt.gca().add_patch(circle)
plt.title('Randomly Plotted Discs')
plt.xlabel('X')
plt.ylabel('Y')
plt.show()
def place_discs_until_covered(size, max_discs):
"""Place discs with a radius of 1 until the entire square is covered.
size is the size of the square. max_discs is the maximum number of discs we are willing to create."""
# Initialize list to hold discs
discs = []
discs2 = []
# Convert size to an integer
size_int = int(size)
tol = 0
# Loop until the entire square is covered or maximum number of discs is reached
for _ in range(max_discs):
# Generate a new disc
new_disc = generate_disc(size_int)
# Add the new disc to the list
discs.append(new_disc)
discs2.append(new_disc)
# Check if all points in the square are covered
all_covered = True
for x in range(size_int + 1):
for y in range(size_int + 1):
if tol <1:
chains = find_chains(discs2)
if complete_chain(chains):
first_chain = len(discs)
print('First chain completed after',first_chain, 'discs.')
tol += 1
plot_discs(discs, size_int)
# Check distance to each disc
if not any(np.sqrt((x - disc[0]) ** 2 + (y - disc[1]) ** 2) <= 1 for disc in discs):
all_covered = False
break
if not all_covered:
break
# If all points in the square are covered, break the loop
if all_covered:
break
#Plot all the discs
plot_discs(discs, size_int)
# Calculate the number of discs needed
num_discs_needed = len(discs)
if num_discs_needed < max_discs:
print(f"{num_discs_needed} discs were needed to cover the entire square.")
else:
print(f"The maximum number of discs ({max_discs}) was reached, but the square couldn't be fully covered.")
return num_discs_needed, first_chain
n = 1000
size = np.sqrt(n)
place_discs_until_covered(size,10**3)
Any advice is appreciated.
The current state is a complete lack of ideas I'm afraid.