Related
I had an interesting interview question the other day that sort of stumped me. I couldn't find a really good answer for it. The problem stated:
Suppose you are given a number B and an array A of length n. The number B is a natural number, and all numbers in array A are distinct, natural numbers. Design an algorithm that would find the shortest sequence of numbers in array A that would sum up to the number B. Duplicates can be used.
So, as an example, let us say I have a number B = 19, and A = [9, 6, 3, 1]. I could say a solution is 6+6+6+1, or 3+3+3+3+3+3+1, but the solution they are looking for is 9+9+1, because that is the shortest sequence of numbers.
The algorithm that I designed would sort the array and reach into the largest number and subtract it from the original number. It would keep doing this until it could no longer subtract the largest number. It would then go through the array and see if it could keep finding any numbers that it could subtract from B. It actually looked a lot like this:
def domath(b, a):
a.sort()
x = []
n = 0
idx = -1
while b != 0:
n = a[idx]
if(b >= n):
b -= n
x.append(n)
else:
idx -= 1
return x
But this solution would not always work. It would only work if you were lucky enough to have, say, a 2 or a 1 in the array, or the numbers that you kept subtracting from b magically worked. Consider if B=21 and A=[7,8,9]. If it kept subtracting 9, it would not be able to find a solution.
So I was thinking "Okay, then maybe I need to backtrack a bit.".
If I reached into the x array, which keeps track of all the number we kept subtracting, I could add the latest number we subtracted from b, then try to move the idx to the next largest number. So, instead of doing 21 - 9, then 12 -9, it would do 21 - 9, then 12 - 8. It still wouldnt find anything, so then it would try 21 - 9, then 12 - 7. It still wouldnt find anything, so it would try 21 - 8, then 13 - 8, and it wouldnt find anything, so it would do 21 -8, then 13 -7, and it still wouldn't find anything, so it would try 21 -7, and continue on that, and determine if it could do it. If it cant (in this case, it should), it would just return "False" or something.
Is that... a good solution? I feel like there must be a better one, because the interviewers were kind of iffy about this solution.
Tricky. The linked wikipedia page suggests an approach that will take I think O (B * length (A)) which would take quite long if we had B = 1,000,000,000,000 instead of B = 21 with A = [9, 8, 7]. Your backtracking algorithm would handle this reasonably quickly if you start with a division:
111,111,111,111 nines leaves one, no way.
111,111,111,110 nines leaves ten, no way (trying 1 or 0 eights)
111,111,111,109 nines leaves 19, no way (trying 2, 1 or 0 eights)
111,111,111,108 nines leaves 28 = 4x7 (trying 3 .. 0 eights). Best so far.
111,111,111,107 nines leaves 37. 4x8 < 37, no solution can beat what we have.
In your example, B = 21, backtracking would also work quite well. If we just denote the numbers of nines, eights, and sevens, then you would just try the following: 2,0,0; 1,1,0; 1,0,1; 0,2,0; 0,1,1; 0,0,3.
You'd want to stop search branches when you have a solution and can prove that no further solution can be better. That's what I did: When you have 37 left and the highest number available is 8 then you need at least 5 numbers. And for every nine that you remove that number is going up at least by one, so the best solution so far cannot be beaten.
This question already has answers here:
What is the fastest (known) algorithm to find the n-th Catalan number mod m?
(2 answers)
Closed 8 years ago.
in how many ways you can sum the numbers less or equal with N to be equal with n. What is the algorithm to solve that?
Example:
lets say that we have
n =10;
so there are a lot of combinations but for example we can do:
1+1+1+1+1+1+1+1+1+1 = 10
1+2+1+1+1+1+1+1+1=10
1+1+2+1+1+1+1+1+1=10
.....
1+9=10
10=10
8+2=10
and so on.
If you think is the Catalan questions, the answer is: the problem seems to be Catalan problem but is not. If you take a look to the results you will see that lets say for N=5 In Catalan algorithm you have 14 possibilities. But in right answer you have 2^4=16 possibilities if you count all, or the Fibonacci array if you keep only the unique combinations. Eg N=5 we have 8 possibilities, so the Catalan algorithm doesn't verify.
This was a question received by me in a quiz done for fun, at that time i thought that the solution is a well known formula, so i lost a lot of time trying to remember it :)
I found 2 solutions for this problem and 1 more if you are considering only the unique combinations. Eg 2+8 is the same as 8+2, you are considering only 1 of them.
So what is the algorithm to solve it?
This is an interesting problem. I do not have the solution (yet), but I think this can be done in a divide-and-conquer way. If you think of the problem space as a binary tree, you can generate it like this:
The root is the whole number n
Its children are floor(n/2) and ceil(n/2)
Example:
n=5
5
/ \
2 3
/ \ / \
1 1 1 2
/ \
1 1
If you do this recursively, you get a binary tree. If can then traverse the tree in this manner to get all the possible combinations of summing up to n:
get_combinations(root_node)
{
combinations=[]
combine(combinations, root_node.child_left, root_node.child_right)
}
combine(combinations, nodeA, nodeB)
{
new_combi = "nodeA" + "+nodeB"
combinations.add(new_combi)
if nodeA.has_children(): combinations.add( combine(combinations, nodeA.child_left, nodeA.child_right) + "+nodeB" )
if nodeB.has_children(): combinations.add( "nodeA+" + combine(combinations, nodeB.child_left, nodeB.child_right) )
return new_combi
}
This is just a draft. Of yourse you don't have to explicitly generate the tree beforehand, but you can do that along the way. Maybe I can come up with a nicer algorithm if I find the time.
EDIT:
OK, I didn't quite answer OPs question to the point, but I don't like to leave stuff unfinished, so here I present my solution as a working python program:
import math
def print_combinations(n):
for calc in combine(n):
line = ""
count = 0
for op in calc:
line += str(int(op))
count += 1
if count < len(calc):
line += "+"
print line
def combine(n):
p_comb = []
if n >= 1: p_comb.append([n])
if n >1:
comb_left = combine(math.floor(n/float(2)))
comb_right = combine(math.ceil(n/float(2)))
for l in comb_left:
for r in comb_right:
lr_merge = []
lr_merge.extend(l)
lr_merge.extend(r)
p_comb.append(lr_merge)
return p_comb
You can now generate all possible ways of summing up n with numbers <= n. For example if you want to do that for n=5 you call this: print_combinations(5)
Have fun, be aware though that you run into memory issues pretty fast (dynamic programming to the rescue!) and that you can have equivalent calculations (e.g. 1+2 and 2+1).
All the 3 solutions that I fount use Math induction:
solution 1:
if n =0 comb =1
if n =1 comb = 1
if n=2 there are 1+1, 2 comb =2 = comb(0)+comb(1)
if n=3 there are 1+1+1, 1+2, 2+1, 3 comb = 4 = comb(0)+comb(1)+comb(2)
if n=4 there are 1+1+1+1, 1+2+1,1+1+2,2+1+1,2+2,1+3,3+1,4 comb = 8 =comb(0)+comb(1)+comb(2)+comb(3)
Now we see a pattern here that says that:
at k value we have comb(k)= sum(comb(i)) where i between 0 and k-1
using math induction we can prove it for k+1 that:
comb(k+1)= sum(comb(i)) where is is between 0 and k
Solution number 2:
If we pay a little more attention to the solution 1 we can say that:
comb(0)=2^0
comb(1)=2^0
comb(2)=2^1
comb(3)=2^2
comb(4)=2^3
comb(k)=2^(k-1)
again using the math induction we can prove that
comb(k+1)=2^k
Solution number 3 (if we keep only the unique combinations) we can see that:
comb(0)=1
comb(1)=1
comb(2)= 1+1,2=2
comb(3)= 1+1+1, 1+2, 2+1, 3 we take out 1+2 because we have 2+1 and its the same comb(3)=3
comb(4) = 1+1+1+1, 1+2+1,1+1+2,2+1+1,2+2,1+3,3+1,4, here we take out the 1+2+1,,2+1+1 and 1+3 because we have them but in different order comb(4)= 5.
If we continue we can see that:
comb(5) = 8
comb(6)=13
we now can see the pattern that:
comb (k) = comb (k-1) + comb(k-2) the Fibonacci array
again using Math induction we can prove that for k+1
comb(k+1) = comb(k)+comb(k-1)
now it's easy to implement those solutions in a language using recursion for 2 of the solutions or just the non recursive method for the solution with 2^k.
And by the way this has serious connections with graph theory (how many sub-graphs you can build starting from a bigger graph - our number N, and sub-graphs being the ways to count )
Amazing isn't it?
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
how to get uniformed random between a, b by a known uniformed random function RANDOM(0,1)
In the book of Introduction to algorithms, there is an excise:
Describe an implementation of the procedure Random(a, b) that only makes calls to Random(0,1). What is the expected running time of your procedure, as a function of a and b? The probability of the result of Random(a,b) should be pure uniformly distributed, as Random(0,1)
For the Random function, the results are integers between a and b, inclusively. For e.g., Random(0,1) generates either 0 or 1; Random(a, b) generates a, a+1, a+2, ..., b
My solution is like this:
for i = 1 to b-a
r = a + Random(0,1)
return r
the running time is T=b-a
Is this correct? Are the results of my solutions uniformly distributed?
Thanks
What if my new solution is like this:
r = a
for i = 1 to b - a //including b-a
r += Random(0,1)
return r
If it is not correct, why r += Random(0,1) makes r not uniformly distributed?
Others have explained why your solution doesn't work. Here's the correct solution:
1) Find the smallest number, p, such that 2^p > b-a.
2) Perform the following algorithm:
r=0
for i = 1 to p
r = 2*r + Random(0,1)
3) If r is greater than b-a, go to step 2.
4) Your result is r+a
So let's try Random(1,3).
So b-a is 2.
2^1 = 2, so p will have to be 2 so that 2^p is greater than 2.
So we'll loop two times. Let's try all possible outputs:
00 -> r=0, 0 is not > 2, so we output 0+1 or 1.
01 -> r=1, 1 is not > 2, so we output 1+1 or 2.
10 -> r=2, 2 is not > 2, so we output 2+1 or 3.
11 -> r=3, 3 is > 2, so we repeat.
So 1/4 of the time, we output 1. 1/4 of the time we output 2. 1/4 of the time we output 3. And 1/4 of the time we have to repeat the algorithm a second time. Looks good.
Note that if you have to do this a lot, two optimizations are handy:
1) If you use the same range a lot, have a class that computes p once so you don't have to compute it each time.
2) Many CPUs have fast ways to perform step 1 that aren't exposed in high-level languages. For example, x86 CPUs have the BSR instruction.
No, it's not correct, that method will concentrate around (a+b)/2. It's a binomial distribution.
Are you sure that Random(0,1) produces integers? it would make more sense if it produced floating point values between 0 and 1. Then the solution would be an affine transformation, running time independent of a and b.
An idea I just had, in case it's about integer values: use bisection. At each step, you have a range low-high. If Random(0,1) returns 0, the next range is low-(low+high)/2, else (low+high)/2-high.
Details and complexity left to you, since it's homework.
That should create (approximately) a uniform distribution.
Edit: approximately is the important word there. Uniform if b-a+1 is a power of 2, not too far off if it's close, but not good enough generally. Ah, well it was a spontaneous idea, can't get them all right.
No, your solution isn't correct. This sum'll have binomial distribution.
However, you can generate a pure random sequence of 0, 1 and treat it as a binary number.
repeat
result = a
steps = ceiling(log(b - a))
for i = 0 to steps
result += (2 ^ i) * Random(0, 1)
until result <= b
KennyTM: my bad.
I read the other answers. For fun, here is another way to find the random number:
Allocate an array with b-a elements.
Set all the values to 1.
Iterate through the array. For each nonzero element, flip the coin, as it were. If it is came up 0, set the element to 0.
Whenever, after a complete iteration, you only have 1 element remaining, you have your random number: a+i where i is the index of the nonzero element (assuming we start indexing on 0). All numbers are then equally likely. (You would have to deal with the case where it's a tie, but I leave that as an exercise for you.)
This would have O(infinity) ... :)
On average, though, half the numbers would be eliminated, so it would have an average case running time of log_2 (b-a).
First of all I assume you are actually accumulating the result, not adding 0 or 1 to a on each step.
Using some probabilites you can prove that your solution is not uniformly distibuted. The chance that the resulting value r is (a+b)/2 is greatest. For instance if a is 0 and b is 7, the chance that you get a value 4 is (combination 4 of 7) divided by 2 raised to the power 7. The reason for that is that no matter which 4 out of the 7 values are 1 the result will still be 4.
The running time you estimate is correct.
Your solution's pseudocode should look like:
r=a
for i = 0 to b-a
r+=Random(0,1)
return r
As for uniform distribution, assuming that the random implementation this random number generator is based on is perfectly uniform the odds of getting 0 or 1 are 50%. Therefore getting the number you want is the result of that choice made over and over again.
So for a=1, b=5, there are 5 choices made.
The odds of getting 1 involves 5 decisions, all 0, the odds of that are 0.5^5 = 3.125%
The odds of getting 5 involves 5 decisions, all 1, the odds of that are 0.5^5 = 3.125%
As you can see from this, the distribution is not uniform -- the odds of any number should be 20%.
In the algorithm you created, it is really not equally distributed.
The result "r" will always be either "a" or "a+1". It will never go beyond that.
It should look something like this:
r=0;
for i=0 to b-a
r = a + r + Random(0,1)
return r;
By including "r" into your computation, you are including the "randomness" of all the previous "for" loop runs.
Say S = 5 and N = 3 the solutions would look like - <0,0,5> <0,1,4> <0,2,3> <0,3,2> <5,0,0> <2,3,0> <3,2,0> <1,2,2> etc etc.
In the general case, N nested loops can be used to solve the problem. Run N nested loop, inside them check if the loop variables add upto S.
If we do not know N ahead of time, we can use a recursive solution. In each level, run a loop starting from 0 to N, and then call the function itself again. When we reach a depth of N, see if the numbers obtained add up to S.
Any other dynamic programming solution?
Try this recursive function:
f(s, n) = 1 if s = 0
= 0 if s != 0 and n = 0
= sum f(s - i, n - 1) over i in [0, s] otherwise
To use dynamic programming you can cache the value of f after evaluating it, and check if the value already exists in the cache before evaluating it.
There is a closed form formula : binomial(s + n - 1, s) or binomial(s+n-1,n-1)
Those numbers are the simplex numbers.
If you want to compute them, use the log gamma function or arbitrary precision arithmetic.
See https://math.stackexchange.com/questions/2455/geometric-proof-of-the-formula-for-simplex-numbers
I have my own formula for this. We, together with my friend Gio made an investigative report concerning this. The formula that we got is [2 raised to (n-1) - 1], where n is the number we are looking for how many addends it has.
Let's try.
If n is 1: its addends are o. There's no two or more numbers that we can add to get a sum of 1 (excluding 0). Let's try a higher number.
Let's try 4. 4 has addends: 1+1+1+1, 1+2+1, 1+1+2, 2+1+1, 1+3, 2+2, 3+1. Its total is 7.
Let's check with the formula. 2 raised to (4-1) - 1 = 2 raised to (3) - 1 = 8-1 =7.
Let's try 15. 2 raised to (15-1) - 1 = 2 raised to (14) - 1 = 16384 - 1 = 16383. Therefore, there are 16383 ways to add numbers that will equal to 15.
(Note: Addends are positive numbers only.)
(You can try other numbers, to check whether our formula is correct or not.)
This can be calculated in O(s+n) (or O(1) if you don't mind an approximation) in the following way:
Imagine we have a string with n-1 X's in it and s o's. So for your example of s=5, n=3, one example string would be
oXooXoo
Notice that the X's divide the o's into three distinct groupings: one of length 1, length 2, and length 2. This corresponds to your solution of <1,2,2>. Every possible string gives us a different solution, by counting the number of o's in a row (a 0 is possible: for example, XoooooX would correspond to <0,5,0>). So by counting the number of possible strings of this form, we get the answer to your question.
There are s+(n-1) positions to choose for s o's, so the answer is Choose(s+n-1, s).
There is a fixed formula to find the answer. If you want to find the number of ways to get N as the sum of R elements. The answer is always:
(N+R-1)!/((R-1)!*(N)!)
or in other words:
(N+R-1) C (R-1)
This actually looks a lot like a Towers of Hanoi problem, without the constraint of stacking disks only on larger disks. You have S disks that can be in any combination on N towers. So that's what got me thinking about it.
What I suspect is that there is a formula we can deduce that doesn't require the recursive programming. I'll need a bit more time though.
From ProjectEuler.net:
Prob 76: How many different ways can one hundred be written as a sum of at least two positive integers?
I have no idea how to start this...any points in the right direction or help? I'm not looking for how to do it but some hints on how to do it.
For example 5 can be written like:
4 + 1
3 + 2
3 + 1 + 1
2 + 2 + 1
2 + 1 + 1 + 1
1 + 1 + 1 + 1 + 1
So 6 possibilities total.
Partition Numbers (or Partition Functions) are the key to this one.
Problems like these are usually easier if you start at the bottom and work your way up to see if you can detect any patterns.
P(1) = 1 = {1}
P(2) = 2 = {[2], [1 + 1]}
P(3) = 3 = {[3], [2 + 1], [1 + 1 + 1]}
P(4) = 5 = {[4], [3 + 1], [2 + 2], [2 + 1 + 1], [1 + 1 + 1 + 1]}
P(5) = 7 ...
P(6) = 11 ...
P(7) = 15 ...
P(8) = 22 ...
P(9) = 30 ...
Hint: See if you can build P(N) up from some combination of the results prior to P(N).
The solution can be found using a chopping algorithm.
Use for example the 6. Then we have:
6
5+1
4+2
3+3
but we are not finished yet.
If we take the 5+1, and consider the +1 part as finished, because all other ending combinations are handled by the 4+2 and 3+3. So we need to apply the same trick to the 5.
4+1+1
3+2+1
And we can continue. But not mindlessly. Because for example 4+2 produces 3+1+2 and 2+2+2. But we don't want the 3+1+2 because we will have a 3+2+1. So we only use all productions of 4 where the lowest number is greater or equal than 2.
6
5+1
4+1+1
3+1+1+1
2+1+1+1+1
1+1+1+1+1+1
2+2+1+1
3+2+1
4+2
2+2+2
3+3
Next step is to put this in an algorithm.
Ok we need a recursive function that takes two parameters. The number to be chopped and the minimal value:
func CountCombinations(Number, Minimal)
temp = 1
if Number<=1 then return 1
for i = 1 to Floor(Number/2)
if i>=Minimal then
temp := temp + CountCombinations(Number-i, i)
end for
return temp
end func
To check the algorithm:
C(6,1) = 1 + C(5,1) + C(4,2) + C(3,3) = 11, which is correct.
C(5,1) = 1 + C(4,1) + C(3,2) = 7
C(4,1) = 1 + C(3,1) + C(2,2) = 5
C(3,1) = 1 + C(2,1) = 3
C(2,1) = 1 + C(1,1) = 2
C(1,1) = 1
C(2,2) = 1
C(3,2) = 1
C(4,2) = 1 + C(2,2) = 2
C(3,3) = 1
By the way, the number of combinations of 100:
CC(100) = 190569292
CC(100) = 190569291 (if we don't take into account 100 + 0)
A good way to approach these is not get fixated on the '100' but try to consider what the difference between totalling for a sum n and n+1 would be, by looking for patterns as n increases 1,2,3....
I'd have a go now but I have work to do :)
Like most problems in Project Euler with big numbers, the best way to think about them is not to get stumped with that huge upper bound, and think of the problem in smaller terms, and gradually work your way up. Maybe, on the way you'll recognize a pattern, or learn enough to get you to the answer easily.
The only other hint I think I can give you without spoiling your epiphany is the word 'partition'.
Once you've figured that out, you'll have it in no time :)
one approach is to think recursive function: find permutations of a numeric series drawn from the positive integers (duplicates allowed) that add up to 100
the zero is 1, i.e. for the number 1 there are zero solutions
the unit is 2, i.e for the number 2 there is only one solution
another approach is to think generative function: start with the zero and find permutation series up to the target, keeping a map/hash or the intermediate values and counts
you can iterate up from 1, or recurse down from 100; you'll get the same answer either way. At each point you could (for a naive solution) generate all permutations of the series of positive integers counting up to the target number minus 1, and count only those that add up to the target number
good luck!
Notice: My maths is a bit rusty but hopefully this will help...
You are going well with your break down of the problem.
Think Generally:
A number n can be written as (n-1)+1 or (n-2)+2
You generalise this to (n-m)+m
Remember that the above also applies to all numbers (including m)
So the idea is to find the first set (lets say 5 = (5-1)+1) and then treat (5-1) as a new n...5 = 4 +1...5 = ((4-1)+1)+1. The once that is exhausted begin again on 5 = 3 + 2....which becomes 5 = ((3-1)+1)+2 ....= 2+1+2....breaking down each one as you go along.
Many math problems can be solved by induction.
You know the answer for a specific value, and you can find the answer for every value, if you find something that link n with n+1.
For example in your case you know that the answer for How many different ways can one be written as a sum of at least two positive integers? is just 1.
What do I mean with the link between n and n+1? Well I mean exactly that you must find a formula that, provide you know the answer for n, you will find the answer for n+1. Then, calling recursively that formula, you'll know the answer and you've done (note: this is just the mathematical part of it, in real life you might find that this approach would gave you something too slow to be practical, so you've not done yet - in this case I think you will be done).
Now, suppose that you know n can be written as a sum of at least two positive integers? in k different ways, one of which would be:
n=a1+a2+a3+...am (this sum has m terms)
What can you say about n+1? Since you would like just hints I'm not writing the solution here, but just what follows. Surely you have the same k different ways, but in each of them there will be the +1 term, one of which would be:
n+1=a1+a2+a3+...am+1 (this sum has m+1 terms)
Then, of course, you will have other k possibilities, such those in which the last term of each sum is not the same, but it will be increased by one, like:
n+1=a1+a2+a3+...(am+1) (this sum has m terms)
Thus there are at least 2k ways of writing n+1 as a sum of at least two positive integers. Well, there are other ones. Find it out, if you can :-)
And enjoy :-))