I am trying to learn how to write proper code in python, and stumbled upon the following problem.
Bob is given a set of building blocks
b
, and a numbern
of towers to build. Since we are in communist land the towers should have as equal of an height as possible. The catch is that you are only given a maximum of 300 seconds to build the towers. The goal is to minimize thestd
of the tower height.
Say you are given the following vector
bricks = [8 8 8 6 6 4 4 4 4 3 3 2]
and asked to build 5 towers. One output to the problem can be
8 4 0
8 4 0
8 4 0
6 4 2
6 3 3
Which has an std of std = ([12-12]^2+[12-12]^2+...+[12-12]^2)^1/2=0
. for 3 towers the output should be
8 4 4 4
8 6 3 3
8 6 4 2
Which again is optimal. Note however I am not asking for an optimal solution.. So this is similar to the knapsack problem. But here the goal is not to fill each knapsack to the brim, but rather to have each knapsack have as close weight as possible.
I came up with two possible codes
def TowerGreed(bricks,nTower):
bricks.sort(reverse=True)
lengthBricks = len(bricks)
if lengthBricks > nTower:
Tower = [[] for i in range(0,nTower)]
for i in range(0,lengthBricks):
if i < nTower:
Tower[i].append(bricks[i])
elif i == nTower:
Height = bricks[0:nTower]
Tower[i-1].append(bricks[i])
Height[nTower-1] = bricks[i] + Height[nTower-1]
else:
lowest = Height.index(min(Height))
Tower[lowest].append(bricks[i])
Height[lowest] = bricks[i] + Height[lowest]
return Tower
This is a standard Greed algorithm. It first sort the elements, then it adds one element to each tower. Then it starts checking which tower is the smallest. The smallest tower is given the next piece.
A modified version of the code above, is given below
def towerGreedImproved(bricks,numberTowers):
# First we sort the number of bricks from greatest to smallest
bricks.sort(reverse = True)
# Only runs the algorithm if we have more bricks than towers to build
if len(bricks) >= numberTowers:
Towers = [[] for i in range(0,numberTowers)] #Empty vector [[],[],...]
Height = [0] * numberTowers # Zero vector [0,0,...]
avgHeight = sum(bricks)/numberTowers
""" Loops through each tower, adds elements until that tower is just
bellow the average tower height """
for j in range(0,numberTowers):
while Height[0] + bricks[0] < avgHeight:
Towers[j].append(bricks[0])
Height[j] = bricks[0] + Height[j]
del bricks[0]; #Deletes used bricks
for n in bricks:
lowest = Height.index(min(Height))
Towers[lowest].append(n)
Height[lowest] = n + Height[lowest]
print(Towers)
return Towers
Here we also start by sorting the bricks, but the difference is we build one tower at a time. We stop building before we reach the average towerheight. This average is given as the sum of the bricks divided by the number of towers being built.
and third option was to randomize the input. The code above is somewhat slow for large vectors. To test my code, I tried the following three vectors. 2 towers, 3 towers, and finnaly 23 towers. Being more familiar with MATLAB i wrote the following code snippet. However I am not quite sure how to impliment it into python, nor if it provides any improvement.
Being a beginner in writing proper code, I have a couple of questions
- Which improvements can I make to my coding style?
- People talked about dynamic solutions to this problem. Are there better algorithms for this problem?
- How can I determine which algorithm is best suited for a given dataset?