# square.py - Linear growth of squares. # Within the unit square bounded by (0,0), (0,1), (1,0) and (1,1), randomly # create a bunch of tiny squares. Then, allow each to grow until it touches # another. The squares will grow at at constant rate, 1% of original side length. # Objectives: # 1. Run the program repeatedly to observe the execution time as a function # of the input number of squares. What is the empirical time complexity? # 2. Create a second version of this program in which we allow the side # length of the squares to grow exponentially instead of linearly. # Observe what effect this has on the execution time trend. import sys import random import time import math # s and t are squares. # Here, we need to use x/y/radius, i.e. [0], [1], [2] indices. def overlap(s, t): sUpperLeftX = s[0] - s[2] sUpperLeftY = s[1] + s[2] tUpperLeftX = t[0] - t[2] tUpperLeftY = t[1] + t[2] sLowerRightX = s[0] + s[2] sLowerRightY = s[1] - s[2] tLowerRightX = t[0] + t[2] tLowerRightY = t[1] - t[2] # Given the top left and lower right corners of two # squares, how can we tell if they overlap at all? To overlap # means that there is some intersection between them. Returning # false would mean that they do not overlap at all - they are # completely disjoint regions. # # http://www.geeksforgeeks.org/find-two-rectangles-overlap/ # Two rectangles do not overlap if one of the following conditions is true. # 1) One rectangle is above top edge of other rectangle. # 2) One rectangle is on left side of left edge of other rectangle. if sUpperLeftX > tLowerRightX or tUpperLeftX > sLowerRightX or \ sUpperLeftY < tLowerRightY or tUpperLeftY < sLowerRightY: return False else: return True # ----------------------------------------------------------------- # Main program # It doesn't matter how big the board is, so let's assume it's (0,0)-(1,1). # But we can vary the number of squares, and how big they are. # I don't think we need the rate. If you double the rate, you simply # double the length of time to reach a steady state. # The time.clock() function returns a float representing some arbitrary # time, which is sufficient for subtracting. It doesn't matter what # time it is; we just want elapsed time. # Should the starting size of the little squares should be dependent how many # there are? Logically I would expect that that the INITIAL total area of the # little squares should be invariant. If we doubled the board size, # we could accommodate twice as many squares. But I'm not enlarging the # board, so make the squares (initially) smaller. #begin = time.clock() random.seed() # Feel free to change how we obtain the number of squares from the user. # Here, I have used a command line argument. You may instead ask for # the number interactively. numSquares = int(sys.argv[1]) totalInitialLittleSquareArea = .001 side = math.sqrt(totalInitialLittleSquareArea / numSquares) increment = .01 * side # Create a list of squares. square = [] for i in range(0, numSquares): # Pick a point on the board. It's possible that some of the little # square may lie outside the board, but center will not. # random.random() returns 0.0 <= x < 1.0 which is perfect for me. x = random.random() y = random.random() radius = side / 2.0 # We now have initial data for a square: center x and y, radius, # and whether it has stopped growing. square.append([x, y, radius, False]) # Allow each square to grow until it bumps into someone else. iters = 0 begin = time.clock() while True: # Count how many squares didn't overlap. If zero, we are done. iters += 1 numberNonoverlapping = 0 for s in square: # If already frozen, then it can't grow. if s[3] == True: continue # Do I overlap with anyone else? # If so, I'm frozen and can't grow further. foundOverlap = False for t in square: if s != t and overlap(s, t): foundOverlap = True break # Freeze me if overlap found. # Optimization idea: would it help to print out now? if foundOverlap: s[3] = True else: # We grow by increasing the side length by the "increment". # But note that s[2] is the radius, which is 1/2 of the side # length, so we must divide the increment by 2. s[2] += increment / 2.0 numberNonoverlapping += 1 #print(numberNonoverlapping) if numberNonoverlapping == 0: break # Output. If desired, also print out the final radii. end = time.clock() duration = end - begin #print("Final radii:") #for s in square: # print(s[2]) print("{0:4d} squares, {1:5d} iterations, {2:6.2f} seconds". \ format(numSquares, iters, duration))