Solve google foobar staircase problem with python
Question:
Given an n
number of bricks i find out how many possible different staircases can be built: each staircase must have at least 2 steps. No two steps are allowed to be at the same height – each step must be lower than the previous one. All steps must contain at least one brick. A step’s height is classified as the total amount of bricks that make up that step.
For example, when N = 5, you have 2 choice of how to build the staircase (# indicates a brick):
# 1
#### 4
or
## 2
### 3
I have actually managed to solve the problem: the more obvious way was to use itertools
to find all the possible combinations and then select the possible ones. However i finally figured out something like this:
def _solution(bricks, struct):
result = 0
for used_bricks in range(1, bricks if not struct else struct[-1]):
elapsed_bricks = bricks - used_bricks
result += _solution(elapsed_bricks, [*struct, used_bricks])
if not bricks:
# print(struct)
result += 1
return result
def solution(n):
if not n:
return 0
return _solution(n, [])
This works, great! If i call solution(6)
it returns 3 as expected, however this function-recursion method works greatly for low values of n
, it took an hour to calculate n = 100
, and however what i am doing is still calculating all the possible permutations. Is there a fastest way to do this?
Answers:
it took an hour to calculate n = 100
because your solution is exponential time and for a standard machine this is expected.
each staircase must have at least 2 steps. No two steps are allowed to be at
the same height - each step must be lower than the previous one. All steps must
contain at least one brick
From this we can infer that any solution staircase for some number of bricks N will be an ascending ordering of natural numbers from 1 to N-1 such that their sum is N and each number is unique.
This problem can be reduced to partition problem in which we need to find total number of partitions of N such that each partition size is unique.
Let SkN be the number of partitions of N such that each of the summands is from 1 to k.
This follows the recurrence,
SkN = Sk-1N-k + Sk-1N
Clearly answer to our problem is given by, SN-1N
Here is the solution using bottom up dynamic programming, O(N2),
def _solution(bricks):
S = [1, *([0]*(bricks))]
for i in range(1, bricks): # k for our Sk(N)
for j in range(bricks, i-1, -1): # N for our Sk(N)
S[j] += S[j-i]
return S[bricks]
def solution(n):
if not n or n < 3: #2 steps min. are required which requires (1+2)=3 bricks
return 0
return _solution(n)
Here’s a recursive approach with memoization, taking i
bricks from n
for each recursive attempt, while making sure that i
is less than the previous height
by passing on i
as the new height
for the recursive call:
def solution(n, height=None, memo={}):
height = height or n
key = n, height
if key not in memo:
memo[key] = 3 > n < height or sum(
solution(n - i, i) for i in range(2, min(n + 1, height)))
return memo[key]
Given an n
number of bricks i find out how many possible different staircases can be built: each staircase must have at least 2 steps. No two steps are allowed to be at the same height – each step must be lower than the previous one. All steps must contain at least one brick. A step’s height is classified as the total amount of bricks that make up that step.
For example, when N = 5, you have 2 choice of how to build the staircase (# indicates a brick):
# 1
#### 4
or
## 2
### 3
I have actually managed to solve the problem: the more obvious way was to use itertools
to find all the possible combinations and then select the possible ones. However i finally figured out something like this:
def _solution(bricks, struct):
result = 0
for used_bricks in range(1, bricks if not struct else struct[-1]):
elapsed_bricks = bricks - used_bricks
result += _solution(elapsed_bricks, [*struct, used_bricks])
if not bricks:
# print(struct)
result += 1
return result
def solution(n):
if not n:
return 0
return _solution(n, [])
This works, great! If i call solution(6)
it returns 3 as expected, however this function-recursion method works greatly for low values of n
, it took an hour to calculate n = 100
, and however what i am doing is still calculating all the possible permutations. Is there a fastest way to do this?
it took an hour to calculate n = 100
because your solution is exponential time and for a standard machine this is expected.
each staircase must have at least 2 steps. No two steps are allowed to be at
the same height - each step must be lower than the previous one. All steps must
contain at least one brick
From this we can infer that any solution staircase for some number of bricks N will be an ascending ordering of natural numbers from 1 to N-1 such that their sum is N and each number is unique.
This problem can be reduced to partition problem in which we need to find total number of partitions of N such that each partition size is unique.
Let SkN be the number of partitions of N such that each of the summands is from 1 to k.
This follows the recurrence,
SkN = Sk-1N-k + Sk-1N
Clearly answer to our problem is given by, SN-1N
Here is the solution using bottom up dynamic programming, O(N2),
def _solution(bricks):
S = [1, *([0]*(bricks))]
for i in range(1, bricks): # k for our Sk(N)
for j in range(bricks, i-1, -1): # N for our Sk(N)
S[j] += S[j-i]
return S[bricks]
def solution(n):
if not n or n < 3: #2 steps min. are required which requires (1+2)=3 bricks
return 0
return _solution(n)
Here’s a recursive approach with memoization, taking i
bricks from n
for each recursive attempt, while making sure that i
is less than the previous height
by passing on i
as the new height
for the recursive call:
def solution(n, height=None, memo={}):
height = height or n
key = n, height
if key not in memo:
memo[key] = 3 > n < height or sum(
solution(n - i, i) for i in range(2, min(n + 1, height)))
return memo[key]