Problem statement (For more detailed description (including pictures), please, visit the link):
A row measuring seven units in length has red blocks with a minimum length of three units placed on it, such that any two red blocks (which are allowed to be different lengths) are separated by at least one grey square. There are exactly seventeen ways of doing this.
How many ways can a row measuring fifty units in length be filled?
Code:
from scipy import special
import time
import itertools
def partitionfunc(n,k,l=3):
'''n is the integer to partition, k is the
length of partitions, l is the min partition element size'''
if k < 1:
return 0
if k == 1:
if n >= l:
yield (n,)
return 0
for i in range(l,n+1):
for result in partitionfunc(n-i,k-1,i):
yield (i,)+result
def valid_partitions(p):
total = p
count = 0 #Max. number of tiles that can be placed on a row.
while True:
count += 1
total -=3
if total <= 3:
break
total-=1
'''Find all the valid partitions with length [1,count] that can be placed on the row with length p'''
data = []
for k in range(1,count+1):
min_part = k*3
for n in range(min_part,p+1):
Allowed = []
LIST = list(partitionfunc(n,k))
for b in LIST:
if sum(b) + (len(b)-1) <= p:
Allowed.append(b)
data+= Allowed
return data
def count_permutations(array):
'''Counts how many possible permutations are there for the particular partition'''
get_unique_elements = set(array)
total_length = len(array)
lengths = [array.count(x) for x in get_unique_elements]
answer = 1
for b in lengths:
answer*= special.comb(total_length, b)
total_length-= b
return answer
def calculate_ways(m,n):
return special.comb(n-m+1,n-2*m+1)
def final(w):
total_variations = 0
data = valid_partitions(w)
for q in data:
m = len(q)
remain = w - sum(q) - m + 1
n = 2*m -1 + remain
total_variations+= calculate_ways(m,n)*count_permutations(q)
return int(total_variations+1)
if __name__ == '__main__':
start = time.time()
print('Answer: {}'.format(final(50)))
print(time.time()-start)
I'll explain the reasoning behind the code using example provided by the Euler:
Step 1
First, we find maximum number of tiles that can be placed on the row with length 7. At most only 2 tiles can be placed, hence number equals 2 (call this number count).
Then we iterate through [1,count] and and find all the valid partitions of the number seven:
In our case:
1 tile: (3), (4), (5), (6), (7) (The reason we omit (1) and (2) is because minimum length of red tile is 3)
2 tiles: (3, 3) (Note, that although (3,4) is a partition of 7 too, but it won't work in our case, because it is specified that there must be at least 1 tile gap between two red tiles.
Step 2.
For each partition obtained in step 1, we calculate number of ways the partition can be placed on the row.
For example:
(5) Represents 1 tile with length 5. There are 3 ways to place such tile.
(3,3) Represents 2 tiles both with length 3. There is 1 way to place them on the row.
When we add up the numbers, we end up with 16. We need to add 1, because (I'm curious why) the row that doesn't contain any red tiles is a valid case too. The final answer is 17.
I believe that there are a lot of things that can be improved. I'm glad to hear any suggestions!
P.S The problem I would like to specifically point out is the variable/function names. I believe there is a possibility to make them way more descriptive/clear then they are now.