The solutions listed on wikipedia and other websites to the egg dropping puzzle calculate the maximum amount of drops, or the worse case scenario until we reach the critical floor where the egg breaks. But what if I want an algorithm that only returns the ideal floor to start from?
For example: 1 egg, 100 foors = 1:
Obvious because you need to check every floor until it breaks.
2 eggs, 100 floors = 14:
We start at floor k. If it breaks, we just need to check k-1 steps beforehand as it's a 1-egg problem.
If it doesn't break, we move k-1 steps, so that the maximum amount of steps still remains k. This leads to k + k -1 + k-2... = k(k+1) / 2 >= 100, k = ~14 rounded up.
How do I find the general best floor for e eggs and n floors?
The trick is that the dynamic programming data structure has the answer encoded in it. Namely that you figure out how many drops are needed, and then it is the maximum floors with 1 less drop and 1 less egg plus 1 (the test egg which, if it breaks, puts you in the previously solved solution.)
Here is a Python solution with generators that is slightly inefficient but demonstrates the ideas in a hopefully clear manner.
def floors_by_drops (eggs):
drops = 0
if eggs == 1:
while True:
drops = drops + 1
yield (drops, drops)
else:
floors = 1
drops = 1
yield (drops, floors)
prev_floors = floors_by_drops(eggs-1)
while True:
drops = drops + 1
(this_drops, this_floors) = prev_floors.next()
if drops <= this_drops:
# We are not able to use the last egg in our best strategy.
yield (drops, this_floors)
floors = this_floors
else:
# We drop an egg at this_floors+1
# If we fail, we can do this_floors with 1 less egg and one less drop.
# If we succeed, we can do floors with all eggs and one less drop.
floors = floors + this_floors + 1
yield (drops, floors)
def first_floor (eggs, floors):
if eggs == 1:
return 1 # always
else:
prev_eggs_iterator = floors_by_drops(eggs-1)
eggs_iterator = floors_by_drops(eggs)
prev_floors = 0
while True:
# eggs_iterator is always 1 more drop than prev_eggs_iterator
this_floors = eggs_iterator.next()[1]
if floors <= this_floors:
return prev_floors + 1
prev_floors = prev_eggs_iterator.next()[1]
print(first_floor(2, 100))
Related
I'm creating a macro for an RPG, in Lua, in it I need to get the most sets with a stack of dices. to form a group the data must add up to the minimum of each group, and may exceed this minimum.
ex: 1, 2, 4, 5, 5, 6, 7, 10 w/ min = 10 will be: 6+4, 5+5, 7+1+2, 10.
I grouped the result of each dice into an array, and pulled out the data that can form groups on their own:
for i=#dice, 1, -1 do
table.sort(dice);
minimo = tonumber(minimum)
if dice[i] >= minimum then
stack.Total = stack.total+1;
table.insert(stack.dice, 1, math.floor(dice[i]))
table.remove(dice, i);
end;
end;
it doesn't have to be in Lua, just some mathematical formula will be of great help
Here's an efficient recursive solution. It likely doesn't scale as well as solving a mixed integer program would, but it's simple and doesn't require an external library. You could probably make it even faster by memoizing it, at the expense of a lot of memory.
The core idea is: form all possible groups that meet the minimum; for each such group, make the max number of groups out of the remaining rolls; take the best solution. The rest is optimization.
The first optimization is to loop over only some groups. Since we might as well put every roll in a group, the largest roll is in some group. To avoid looping over all permutations of the groups, enumerate the possibilities for that group only.
The second optimization is to stop searching if we find a provable optimum. Obviously we can't make more groups than the floor of the sum over the minimum. If we make this many, we can't improve.
The third optimization is to avoid enumerating duplicate groups. When we decrease i, we're considering groups that did not include the element at that position. To avoid duplicates, we skip i over the elements identical to the one that we just rejected.
In Python 3:
def all_groups(minimum, rolls, j):
roll = rolls[j]
if minimum <= roll:
yield [roll], rolls[:j]
else:
i = j - 1
while i >= 0:
for group, rest in all_groups(minimum - roll, rolls, i):
group.append(roll)
rest.extend(rolls[i + 1 : j])
yield group, rest
while i > 0 and rolls[i - 1] == rolls[i]:
i -= 1
i -= 1
def max_groups_helper(minimum, rolls, lower_bound=0):
upper_bound = sum(min(roll, minimum) for roll in rolls) // minimum
if upper_bound < lower_bound:
return None
if upper_bound <= 0:
return []
best = []
for group, rest in sorted(
all_groups(minimum, rolls, len(rolls) - 1),
key=lambda group_rest: sum(group_rest[0]),
):
candidate = max_groups_helper(minimum, rest, max(lower_bound - 1, len(best)))
if candidate is None:
continue
candidate.append(group)
best = candidate
if len(best) >= upper_bound:
break
return best
def max_groups(minimum, rolls):
assert minimum > 0
rolls = list(rolls)
return max_groups_helper(minimum, rolls, 0)
https://projecteuler.net/problem=35
All problems on Project Euler are supposed to be solvable by a program in under 1 minute. My solution, however, has a runtime of almost 3 minutes. Other solutions I've seen online are similar to mine conceptually, but have runtimes that are exponentially faster. Can anyone help make my code more efficient/run faster?
Thanks!
#genPrimes takes an argument n and returns a list of all prime numbers less than n
def genPrimes(n):
primeList = [2]
number = 3
while(number < n):
isPrime = True
for element in primeList:
if element > number**0.5:
break
if number%element == 0 and element <= number**0.5:
isPrime = False
break
if isPrime == True:
primeList.append(number)
number += 2
return primeList
#isCircular takes a number as input and returns True if all rotations of that number are prime
def isCircular(prime):
original = prime
isCircular = True
prime = int(str(prime)[-1] + str(prime)[:len(str(prime)) - 1])
while(prime != original):
if prime not in primeList:
isCircular = False
break
prime = int(str(prime)[-1] + str(prime)[:len(str(prime)) - 1])
return isCircular
primeList = genPrimes(1000000)
circCount = 0
for prime in primeList:
if isCircular(prime):
circCount += 1
print circCount
Two modifications of your code yield a pretty fast solution (roughly 2 seconds on my machine):
Generating primes is a common problem with many solutions on the web. I replaced yours with rwh_primes1 from this article:
def genPrimes(n):
sieve = [True] * (n/2)
for i in xrange(3,int(n**0.5)+1,2):
if sieve[i/2]:
sieve[i*i/2::i] = [False] * ((n-i*i-1)/(2*i)+1)
return [2] + [2*i+1 for i in xrange(1,n/2) if sieve[i]]
It is about 65 times faster (0.04 seconds).
The most important step I'd suggest, however, is to filter the list of generated primes. Since each circularly shifted version of an integer has to be prime, the circular prime must not contain certain digits. The prime 23, e.g., can be easily spotted as an invalid candidate, because it contains a 2, which indicates divisibility by two when this is the last digit. Thus you might remove all such bad candidates by the following simple method:
def filterPrimes(primeList):
for i in primeList[3:]:
if '0' in str(i) or '2' in str(i) or '4' in str(i) \
or '5' in str(i) or '6' in str(i) or '8' in str(i):
primeList.remove(i)
return primeList
Note that the loop starts at the fourth prime number to avoid removing the number 2 or 5.
The filtering step takes most of the computing time (about 1.9 seconds), but reduces the number of circular prime candidates dramatically from 78498 to 1113 (= 98.5 % reduction)!
The last step, the circulation of each remaining candidate, can be done as you suggested. If you wish, you can simplify the code as follows:
circCount = sum(map(isCircular, primeList))
Due to the reduced candidate set this step is completed in only 0.03 seconds.
This is an interview problem I came across yesterday, I can think of a recursive solution but I wanna know if there's a non-recursive solution.
Given a number N, starting with number 1, you can only multiply the result by 5 or add 3 to the result. If there's no way to get N through this method, return "Can't generate it".
Ex:
Input: 23
Output: (1+3)*5+3
Input: 215
Output: ((1*5+3)*5+3)*5
Input: 12
Output: Can't generate it.
The recursive method can be obvious and intuitive, but are there any non-recursive methods?
I think the quickest, non recursive solution is (for N > 2):
if N mod 3 == 1, it can be generated as 1 + 3*k.
if N mod 3 == 2, it can be generated as 1*5 + 3*k
if N mod 3 == 0, it cannot be generated
The last statement comes from the fact that starting with 1 (= 1 mod 3) you can only reach numbers which are equals to 1 or 2 mod 3:
when you add 3, you don't change the value mod 3
a number equals to 1 mod 3 multiplied by 5 gives a number equals to 2 mod 3
a number equals to 2 mod 3 multiplied by 5 gives a number equals to 1 mod 3
The key here is to work backwards. Start with the number you want to reach and if it's divisible by 5 then divide by 5 because multiplication by 5 results in a shorter solution than addition by 3. The only exceptions are if the value equals 10, because dividing by 5 would yield 2 which is insolvable. If the number is not divisible by 5 or is equal to 10, subtract 3. This produces the shortest string
Repeat until you reach 1
Here is python code:
def f(x):
if x%3 == 0 or x==2:
return "Can't generate it"
l = []
while x!=1:
if x%5 != 0 or x==10:
l.append(3)
x -= 3
else:
l.append(5)
x /=5
l.reverse()
s = '1'
for v in l:
if v == 3:
s += ' + 3'
else:
s = '(' + s + ')*5'
return s
Credit to the previous solutions for determining whether a given number is possible
Model the problem as a graph:
Nodes are numbers
Your root node is 1
Links between nodes are *5 or +3.
Then run Dijkstra's algorithm to get the shortest path. If you exhaust all links from nodes <N without getting to N then you can't generate N. (Alternatively, use #obourgain's answer to decide in advance whether the problem can be solved, and only attempt to work out how to solve the problem if it can be solved.)
So essentially, you enqueue the node (1, null path). You need a dictionary storing {node(i.e. number) => best path found so far for that node}. Then, so long as the queue isn't empty, in each pass of the loop you
Dequeue the head (node,path) from the queue.
If the number of this node is >N, or you've already seen this node before with fewer steps in the path, then don't do any more on this pass.
Add (node => path) to the dictionary.
Enqueue nodes reachable from this node with *5 and +3 (together with the paths that get you to those nodes)
When the loop terminates, look up N in the dictionary to get the path, or output "Can't generate it".
Edit: note, this is really Breadth-first search rather than Dijkstra's algorithm, as the cost of traversing a link is fixed at 1.
You can use the following recursion (which is indeed intuitive):
f(input) = f(input/5) OR f(input -3)
base:
f(1) = true
f(x) = false x is not natural positive number
Note that it can be done using Dynamic Programming as well:
f[-2] = f[-1] = f[0] = false
f[1] = true
for i from 2 to n:
f[i] = f[i-3] or (i%5 == 0? f[i/5] : false)
To get the score, you need to get on the table after building it from f[n] and follow the valid true moves.
Time and space complexity of the DP solution is O(n) [pseudo-polynomial]
All recursive algorithms can also be implemented using a stack. So, something like this:
bool canProduce(int target){
Stack<int> numStack;
int current;
numStack.push(1);
while(!numStack.empty){
current=numStack.top();
numStack.pop();
if(current==target)
return true;
if(current+3 < target)
numStack.push(current+3);
if(current*5 < target)
numStack.push(current*5);
}
return false;
}
In Python:
The smart solution:
def f(n):
if n % 3 == 1:
print '1' + '+3' * (n // 3)
elif n % 3 == 2:
print '1*5' + '+3' * ((n - 5) // 3)
else:
print "Can't generate it."
A naive but still O(n) version:
def f(n):
d={1:'1'}
for i in range(n):
if i in d:
d[i*5] = '(' + d[i] + ')*5'
d[i+3] = d[i] + '+3'
if n in d:
print d[n]
else:
print "Can't generate it."
And of course, you could also use a stack to reproduce the behavior of the recursive calls.
Which gives:
>>> f(23)
(1)*5+3+3+3+3+3+3
>>> f(215)
(1)*5+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3
>>> f(12)
Can't generate it.
Let me start with an example -
I have a range of numbers from 1 to 9. And let's say the target number that I want is 29.
In this case the minimum number of operations that are required would be (9*3)+2 = 2 operations. Similarly for 18 the minimum number of operations is 1 (9*2=18).
I can use any of the 4 arithmetic operators - +, -, / and *.
How can I programmatically find out the minimum number of operations required?
Thanks in advance for any help provided.
clarification: integers only, no decimals allowed mid-calculation. i.e. the following is not valid (from comments below): ((9/2) + 1) * 4 == 22
I must admit I didn't think about this thoroughly, but for my purpose it doesn't matter if decimal numbers appear mid-calculation. ((9/2) + 1) * 4 == 22 is valid. Sorry for the confusion.
For the special case where set Y = [1..9] and n > 0:
n <= 9 : 0 operations
n <=18 : 1 operation (+)
otherwise : Remove any divisor found in Y. If this is not enough, do a recursion on the remainder for all offsets -9 .. +9. Offset 0 can be skipped as it has already been tried.
Notice how division is not needed in this case. For other Y this does not hold.
This algorithm is exponential in log(n). The exact analysis is a job for somebody with more knowledge about algebra than I.
For more speed, add pruning to eliminate some of the search for larger numbers.
Sample code:
def findop(n, maxlen=9999):
# Return a short postfix list of numbers and operations
# Simple solution to small numbers
if n<=9: return [n]
if n<=18: return [9,n-9,'+']
# Find direct multiply
x = divlist(n)
if len(x) > 1:
mults = len(x)-1
x[-1:] = findop(x[-1], maxlen-2*mults)
x.extend(['*'] * mults)
return x
shortest = 0
for o in range(1,10) + range(-1,-10,-1):
x = divlist(n-o)
if len(x) == 1: continue
mults = len(x)-1
# We spent len(divlist) + mults + 2 fields for offset.
# The last number is expanded by the recursion, so it doesn't count.
recursion_maxlen = maxlen - len(x) - mults - 2 + 1
if recursion_maxlen < 1: continue
x[-1:] = findop(x[-1], recursion_maxlen)
x.extend(['*'] * mults)
if o > 0:
x.extend([o, '+'])
else:
x.extend([-o, '-'])
if shortest == 0 or len(x) < shortest:
shortest = len(x)
maxlen = shortest - 1
solution = x[:]
if shortest == 0:
# Fake solution, it will be discarded
return '#' * (maxlen+1)
return solution
def divlist(n):
l = []
for d in range(9,1,-1):
while n%d == 0:
l.append(d)
n = n/d
if n>1: l.append(n)
return l
The basic idea is to test all possibilities with k operations, for k starting from 0. Imagine you create a tree of height k that branches for every possible new operation with operand (4*9 branches per level). You need to traverse and evaluate the leaves of the tree for each k before moving to the next k.
I didn't test this pseudo-code:
for every k from 0 to infinity
for every n from 1 to 9
if compute(n,0,k):
return k
boolean compute(n,j,k):
if (j == k):
return (n == target)
else:
for each operator in {+,-,*,/}:
for every i from 1 to 9:
if compute((n operator i),j+1,k):
return true
return false
It doesn't take into account arithmetic operators precedence and braces, that would require some rework.
Really cool question :)
Notice that you can start from the end! From your example (9*3)+2 = 29 is equivalent to saying (29-2)/3=9. That way we can avoid the double loop in cyborg's answer. This suggests the following algorithm for set Y and result r:
nextleaves = {r}
nops = 0
while(true):
nops = nops+1
leaves = nextleaves
nextleaves = {}
for leaf in leaves:
for y in Y:
if (leaf+y) or (leaf-y) or (leaf*y) or (leaf/y) is in X:
return(nops)
else:
add (leaf+y) and (leaf-y) and (leaf*y) and (leaf/y) to nextleaves
This is the basic idea, performance can be certainly be improved, for instance by avoiding "backtracks", such as r+a-a or r*a*b/a.
I guess my idea is similar to the one of Peer Sommerlund:
For big numbers, you advance fast, by multiplication with big ciphers.
Is Y=29 prime? If not, divide it by the maximum divider of (2 to 9).
Else you could subtract a number, to reach a dividable number. 27 is fine, since it is dividable by 9, so
(29-2)/9=3 =>
3*9+2 = 29
So maybe - I didn't think about this to the end: Search the next divisible by 9 number below Y. If you don't reach a number which is a digit, repeat.
The formula is the steps reversed.
(I'll try it for some numbers. :) )
I tried with 2551, which is
echo $((((3*9+4)*9+4)*9+4))
But I didn't test every intermediate result whether it is prime.
But
echo $((8*8*8*5-9))
is 2 operations less. Maybe I can investigate this later.
I'm taking my first steps into recursion and dynamic programming and have a question about forming subproblems to model the recursion.
Problem:
How many different ways are there to
flip a fair coin 5 times and not have
three or more heads in a row?
If some could put up some heavily commented code (Ruby preferred but not essential) to help me get there. I am not a student if that matters, this is a modification of a Project Euler problem to make it very simple for me to grasp. I just need to get the hang of writing recursion formulas.
If you would like to abstract the problem into how many different ways are there to flip a fair coin Y times and not have Z or more heads in a row, that may be beneficial as well. Thanks again, this website rocks.
You can simply create a formula for that:
The number of ways to flip a coin 5 times without having 3 heads in a row is equal to the number of combinations of 5 coin flips minus the combinations with at least three heads in a row. In this case:
HHH-- (4 combinations)
THHH- (2 combinations)
TTHHH (1 combination)
The total number of combinations = 2^5 = 32. And 32 - 7 = 25.
If we flip a coin N times without Q heads in a row, the total amount is 2^N and the amount with at least Q heads is 2^(N-Q+1)-1. So the general answer is:
Flip(N,Q) = 2^N - 2^(N-Q+1) +1
Of course you can use recursion to simulate the total amount:
flipme: N x N -> N
flipme(flipsleft, maxhead) = flip(flipsleft, maxhead, 0)
flip: N x N x N -> N
flip(flipsleft, maxhead, headcount) ==
if flipsleft <= 0 then 0
else if maxhead<=headcount then 0
else
flip(flipsleft - 1, maxhead, headcount+1) + // head
flip(flipsleft - 1, maxhead, maxhead) // tail
Here's my solution in Ruby
def combination(length=5)
return [[]] if length == 0
combination(length-1).collect {|c| [:h] + c if c[0..1]!= [:h,:h]}.compact +
combination(length-1).collect {|c| [:t] + c }
end
puts "There are #{combination.length} ways"
All recursive methods start with an early out for the end case.
return [[]] if length == 0
This returns an array of combinations, where the only combination of zero length is []
The next bit (simplified) is...
combination(length-1).collect {|c| [:h] + c } +
combination(length-1).collect {|c| [:t] + c }
So.. this says.. I want all combinations that are one shorter than the desired length with a :head added to each of them... plus all the combinations that are one shorter with a tail added to them.
The way to think about recursion is.. "assuming I had a method to do the n-1 case.. what would I have to add to make it cover the n case". To me it feels like proof by induction.
This code would generate all combinations of heads and tails up to the given length.
We don't want ones that have :h :h :h. That can only happen where we have :h :h and we are adding a :h. So... I put an if c[0..1] != [:h,:h] on the adding of the :h so it will return nil instead of an array when it was about to make an invalid combination.
I then had to compact the result to ignore all results that are just nil
Isn't this a matter of taking all possible 5 bit sequences and removing the cases where there are three sequential 1 bits (assuming 1 = heads, 0 = tails)?
Here's one way to do it in Python:
#This will hold all possible combinations of flipping the coins.
flips = [[]]
for i in range(5):
#Loop through the existing permutations, and add either 'h' or 't'
#to the end.
for j in range(len(flips)):
f = flips[j]
tails = list(f)
tails.append('t')
flips.append(tails)
f.append('h')
#Now count how many of the permutations match our criteria.
fewEnoughHeadsCount = 0
for flip in flips:
hCount = 0
hasTooManyHeads = False
for c in flip:
if c == 'h': hCount += 1
else: hCount = 0
if hCount >= 3: hasTooManyHeads = True
if not hasTooManyHeads: fewEnoughHeadsCount += 1
print 'There are %s ways.' % fewEnoughHeadsCount
This breaks down to:
How many ways are there to flip a fair coin four times when the first flip was heads + when the first flip was tails:
So in python:
HEADS = "1"
TAILS = "0"
def threeOrMoreHeadsInARow(bits):
return "111" in bits
def flip(n = 5, flips = ""):
if threeOrMoreHeadsInARow(flips):
return 0
if n == 0:
return 1
return flip(n - 1, flips + HEADS) + flip(n - 1, flips + TAILS)
Here's a recursive combination function using Ruby yield statements:
def combinations(values, n)
if n.zero?
yield []
else
combinations(values, n - 1) do |combo_tail|
values.each do |value|
yield [value] + combo_tail
end
end
end
end
And you could use regular expressions to parse out three heads in a row:
def three_heads_in_a_row(s)
([/hhh../, /.hhh./, /..hhh/].collect {|pat| pat.match(s)}).any?
end
Finally, you would get the answer using something like this:
total_count = 0
filter_count = 0
combinations(["h", "t"], 5) do |combo|
count += 1
unless three_heads_in_a_row(combo.join)
filter_count += 1
end
end
puts "TOTAL: #{ total_count }"
puts "FILTERED: #{ filter_count }"
So that's how I would do it :)