Algorithm help :Given a num, can it finally be 1 - algorithm

Given a number, you can divide it or its contiguous part by 2 , or multiply it or its continuous part by 2. Can the number finally be 1?
For example : 13
3 is a part of 13, first we take 3 * 2 = 6, the num turn to be 16,
second we can operate the whole num 16, 16 / 2 = 8, the num is 8 now,
8/2 = 4, num is 4 now,
4/2 = 2, num is 2 now,
2/2 = 1, num is 1 now.
finally we can say 13 can turn into 1, and the path is 13->16->8->4->2->1, we can use a List to store the path.
Example :27
first we operate the whole num 27, 27 * 2 = 54;
then we take 4 as the part of 54, 4 / 2 = 2 , so the 4 is 2 now, num becomes 52;
operate 52, 52 / 2 = 26, num is 26 now;
operate 26, 26 / 2 = 13, num is 13 now;
we just analyzed 13, so 27 can turn into 1 finally.
How to analyze such problem? What's the main idea of solving such type problem?
Sorry about the confusing description, let's take a more complex example: 316
16 is a contiguous part, let 16 / 2 = 8 , so the num is 38 now,
then take 8 / 2 = 4 , the num is 34,
take 4 / 2 = 2, the num is 32,
now take the whole num 32 / 2 = 16,
16 / 2 = 8, num is 8,
8 / 2 = 4, num is 4,
4 / 2 = 2, num is 2,
finally 2 / 2 = 1.
We say, original num 316 can turn into 1 finally after above conversion.
And the contiguous part means, if the input num is 12345,
then 123, 234,345,12,2345 and so on, they are all contiguous parts.
Any continuous subset of num is fine,including head or tail is NOT necessary.
The question is :
How to judge such a num? And if the num can turn into 1, print the path.
Can you find the shortest way?
I got some hints from interviewer (The interview is over):
Most of numbers are eligible, that means nums which are NOT eligible, these characteristics are obvious.
Brute fore way's time complexity is too high, we should pruning timely. (Slide window + pruning ?)

Here is a simple and unoptimized breadth-first search.
def shortest_digit_path (n):
path_from = {n: None}
queue = [n]
count = 0
while True:
m = queue.pop(0)
count += 1
if 0 == count %1000:
print((count, m))
if m == 1:
break
x = str(m)
for i in range(len(x)):
for j in range(i+1, len(x) + 1):
y = x[0:i]
z = x[i:j]
w = x[j:]
if z[0] == '0':
continue # The continuous section is not a proper number.
# Try half of z
if z[-1] in ['2', '4', '6', '8']:
next_m = int(y + str(int(z)//2) + w)
if next_m not in path_from:
path_from[next_m] = m
queue.append(next_m)
# Try doubling z
next_m = int(y + str(int(z)*2) + w)
if next_m not in path_from:
path_from[next_m] = m
queue.append(next_m)
path = []
while m is not None:
path.append(m)
m = path_from[m]
return list(reversed(path))
After playing around with this for a bit, I came up with the following observations.
If the number ends in 0 or 5, there is no path to having any other digit at the end, and therefore you can't get to 1. (The above function will just run forever.
For anything else we can find a path just dealing with 1-2 digits at a time.
Here are the special cases for observation #2. Our first goal is to get to just 0, 1, and 5 as digits.
0: 0
1: 1
2: 2 -> 1
3: 3 -> 6 -> 12 -> 24 -> 28 -> 56 -> 112 -> 16 -> 8 -> 4 -> 2 -> 1
4: 4 -> 2 -> 1
5: 5
6: 6 -> 12 -> 24 -> 28 -> 56 -> 112 -> 16 -> 8 -> 4 -> 2 -> 1
7: 7 -> 14 -> 28 -> 56 -> 112 -> 16 -> 8 -> 4 -> 2 -> 1
8: 8 -> 4 -> 2 -> 1
9: 9 -> 18 -> 28 -> 56 -> 112 -> 16 -> 8 -> 4 -> 2 -> 1
And now from the start of the number we have to deal with the following cases that reduce the number of digits and get back to our desired form.
10: 10 -> 5
11: 11 -> 22 -> 24 -> 28 -> 56 -> 112 -> 16 -> 8 -> 4 -> 2 -> 1
15: 15 -> 110 -> 220 -> 240 -> 280 -> 560 -> 1120 -> 160 -> 80 -> 40 -> 20 -> 10 -> 5
50: 50 -> 25 -> 15 -> 110 -> 220 -> 240 -> 280 -> 560 -> 1120 -> 160 -> 80 -> 40 -> 20 -> 10 -> 5
51: 51 -> 52 -> 26 -> 16 -> 8 -> 4 -> 2 -> 1
55: 55 -> 510 -> 520 -> 260 -> 160 -> 80 -> 40 -> 20 -> 10 -> 5
With this set of rules we can first normalize the number to a standard form, then we can shorten it one digit at a time. This lets us essentially instantly come up with a path. Almost certainly not the shortest one, but definitely a path.
Writing that function is left as an exercise to the reader.
Now back to the shortest path. The algorithm for the breadth-first search can be made much faster if we start with a breadth-first search from both ends and meet in the middle. For this you'd need to also have a path_to that is initialized with {1: None}, a queue containing elements of the form (m, is_rising) and initialize it with [(1, True), (n: False)]. You'd then have to branch on is_rising and before entering values into path_from/path_to check for whether it is in path_to/path_from. If it is, you've met in the middle. Now work out both halves of the path and join them together.
The approach is tricker. But it will let you find the shortest path in the square root of the number of steps that the current approach takes.

Related

Counting number of key comparisons in hash table

I have a hash table that look like this:
0
1 -> 1101 -> 1222 -> 1343 \\ 3 key comparison
2
3 -> 2973 -> 2588 \\ 2 key comparison
4
How many key comparisons are there?
The given answer is 1 + 2 + 1 = 4 but shouldn't it be 3 + 2 = 5?
The given answer is correct. One possible sequence:
At first, you have an empty list -> then add 1101 -> no comparison needed.
Add 1222 -> go to the 1 list, compared it with 1101 -> add it to the end of the list -> 1 comparison.
Add 1343 -> go to the 1 list, compared it with 1101, 1222 -> add it to the end of the list -> 2 comparisons.
Add 2973 -> no comparison,
Add 2588 -> go to 3 list, compared it with 2973 -> 1 comparison.
So, in total, the number of comparison is 0 + 1 + 2 + 0 + 1
Don't know where do you get the 3 + 2 = 5 from? total number of elements?

How to generate combinations in chunks

I have algorithm which performs calculations for every combination of input elements, e.g. for every 5-element subset out of 100-element set. I am porting it to GPU, and now I have initial version of it ready. In order to speed it up I would like to load data from local memory, which is limited (e.g. 32KB) and can hold something like 20 input elements out of 100. So I have to somehow partition my work and generate combinations in chunks. And now this is the hard part, how to do this. Most probably I have to first load data for 20 elements and perform calculations for 5-elements subsets of these 20 elements. After this I would have to replace some (or all) of them with new ones and perform calculations for them, then rinse and repeat. Could you tell me how should I choose replaced elements in local memory, to avoid duplicate work? So far I came to conclusion that I would have to replace at least 16 of them at once to avoid duplicated work problem.
Edit: here is example for generating of 2-element combinations out of 5 elements. Here is full list of all possible cases:
1, 2
1, 3
1, 4
1, 5
2, 3
2, 4
2, 5
3, 4
3, 5
4, 5
Local memory on GPU is limited - lets assume it can hold 3 elements only. So I have to somehow divide my problem into generating combinations of 2 elements out of 3. I have to repeat this multiple times, until I get all combinations from above list. As a first step I can load elements 1, 2, 3 into local memory, so I will get following combinations:
1, 2
1, 3
2, 3
Now I have to load another set of elements and calculate combinations for them. It can be 1, 4, 5. It will produce following combinations:
1, 4
1, 5
4, 5
On the other hand set 1, 2, 4 is invalid - it would result in duplicated combination:
1, 2 // duplicate
1, 4 // ok, new
2, 4 // ok, new
After this step there are 4 more combinations to be generated (list is below). Algorithm has to be able to generate another 2-element combination out of 3 elements, and to somehow handle last (10th) combination.
2, 4
2, 5
3, 4
3, 5
By splitting work in this way, I would be able to process all combinations of original input set using limited local memory which can hold only part of it.
Say e.g. you have 100 elements, you can hold 20 in memory, and you need all the 5-element combinations, of which there are C(100,5) = 75,287,520.
In order to be able to generate all the combinations, every combination of 5 elements has to be in memory at some point. This can be done by dividing the elements into groups of 20/5 = 4 elements; there are 25 of these groups in the input, and C(25,5) = 53,130 combinations of 5 groups.
For every combination of groups, we will start by generating the combinations with one element from each of the five groups; this gives us 53,130 x 45 = 54,405,120 unique combinations.
We now have the combinations where each element comes from a different group, which is the partition [1,1,1,1,1]. We still have to find the combinations for the partitions [2,1,1,1], [2,2,1], [3,1,1], [3,2] and [4,1]. The easiest way will be to do this in seperate stages, but the fastest way will of course be to incorporate these into the first stage for [1,1,1,1,1], because all the combinations of groups we'll ever need are loaded in memory at some point during the first stage.
For the partition [2,1,1,1], we'd load every group in turn as the group with 2 elements, and then load every combination of 3 groups from the remaining 24 groups and take an element from each of them. That would require 25 x C(24,3) = 50,600 steps, each resulting in C(4,2) x 43 = 384 combinations, or a total of 19,430,400.
A partition like [2,2,1] is a bit different, because we'd load every group in turn to be the first group with 2 elements, but only the groups that come after it as the second group with 2 elements, to avoid duplicates. Then for each of these, we'd load each of the other 23 groups to get the final element. That would require C(25,2)/2 x 23 = 6,900 steps, each resulting in C(4,2) x C(4,2) x C(4,1) = 144 combinations, for a total of 993,600.
The partition [3,1,1] requires 25 x C(24,2) = 25 x 276 = 6,900 steps, each resulting in C(4,3) x 42 = 64 combinations, for a total of 441,600.
The partition [3,2] requires 25 x 24 = 600 steps, each resulting in C(4,3) x C(4,2) = 24 combinations, for a total of 14,400.
The partition [4,1] requires 25 x 24 = 600 steps, each resulting in C(4,4) x C(4,1) = 4 combinations, for a total of 2,400.
So we have a total of:
[1,1,1,1,1] -> 54,405,120
[2,1,1,1] -> 19,430,400
[2,2,1] -> 993,600
[3,1,1] -> 441,600
[3,2] -> 14,400
[4,1] -> 2,400
----------
75,287,520 combinations
As you'll have noticed, the partitions [3,2] and [4,1] both require every combination of two groups, so they can be easily integrated into one stage. Of course, if you integrate them all into the first stage for [1,1,1,1,1], you'll only have to load 53,130 combinations of groups into memory, which is the absolute minimum.
(If it's faster to load only one new group of elements into memory at each step, instead of running through the combinations of groups in lexicographical order, check out this answer.)
Integration of different stages
The simplest way to run through all the group combinations for the partition [1,1,1,1,1] is to load groups 1 to 21 as group A, then all groups after A up to 22 as group B, all groups after B up to 23 as group C, all groups after C up to 24 as group D, and all groups after D up to 25 as group E.
A B C D E
1 2 3 4 5 <- ABCDE
1 2 3 4 6 <- ABCDE
...
1 2 3 4 25 <- ABCDE
1 2 3 5 6 <- ABCDE
...
1 2 3 24 25 <- ABCDE
1 2 4 5 6 <- ABCDE
...
1 2 23 24 25 <- ABCDE
1 3 4 5 6 <- ABCDE
...
1 22 23 24 25 <- ABCDE
2 3 4 5 6 <- ABCDE
...
21 22 23 24 25 <- ABCDE
The partitions with four parts can be integrated by taking [2,1,1,1], [1,2,1,1], [1,1,2,1] and [1,1,1,2] elements from these combinations of four groups:
A B C D E
1 2 3 4 5 <- ABCD ABCE ABDE ACDE BCDE
1 2 3 4 6 <- ABCE ABDE ACDE BCDE
...
1 2 3 4 25 <- ABCE ABDE ACDE BCDE
1 2 3 5 6 <- ABDE ACDE BCDE
...
1 2 3 24 25 <- ABDE ACDE BCDE
1 2 4 5 6 <- ACDE BCDE
...
1 2 23 24 25 <- ACDE BCDE
1 3 4 5 6 <- BCDE
...
1 22 23 24 25 <- BCDE
2 3 4 5 6 <- none
...
21 22 23 24 25 <- none
The partitions with three parts can be integrated by taking [2,2,1], [2,1,2], [1,2,2], [3,1,1], [1,3,1] and [1,1,3] elements from these combinations of three groups:
A B C D E
1 2 3 4 5 <- ABC ABD ACD BCD ABE ACE BCE ADE BDE CDE
1 2 3 4 6 <- ABE ACE BCE ADE BDE CDE
...
1 2 3 4 25 <- ABE ACE BCE ADE BDE CDE
1 2 3 5 6 <- ADE BDE CDE
...
1 2 3 24 25 <- ADE BDE CDE
1 2 4 5 6 <- CDE
...
1 2 23 24 25 <- CDE
1 3 4 5 6 <- none
...
21 22 23 24 25 <- none
The partitions with two parts can be integrated by taking [2,3], [3,2], [4,1] and [1,4] elements from these combinations of two groups:
A B C D E
1 2 3 4 5 <- AB AC BC AD BD CD AE BE CE DE
1 2 3 4 6 <- AE BE CE DE
...
1 2 3 4 25 <- AE BE CE DE
1 2 3 5 6 <- DE
...
1 2 3 24 25 <- DE
1 2 4 5 6 <- none
...
21 22 23 24 25 <- none
In general
There are e elements, m elements can be loaded in memory, and you want all combinations of k elements. Use k groups of size g = m/k.
Generate all partitions of k with parts limited to size g:
[1,1,1 ... 1] [2,1,1 ... 1] [2,2,1 ... 1] ... [k] (if k <= g)
[1,1,1 ... 1] [2,1,1 ... 1] [2,2,1 ... 1] ... [g,k-g] (if k > g)
For each of these, generate all unique permutations, e.g.:
[3,2,2,1] -> [3,2,2,1] [3,2,1,2] [3,1,2,2]
[2,3,2,1] [2,3,1,2] [1,3,2,2]
[2,2,3,1] [2,1,3,2] [1,2,3,2]
[2,2,1,3] [2,1,2,3] [1,2,2,3]
Sort the permutations per number of parts, e.g.:
k: [1,1,1 ... 1]
k-1: [2,1 ... 1] [1,2 ... 1] ... [1,1 ... 2]
...
2: [g,k-g] [k-g,g]
Load the first k groups into memory, e.g.:
A B C D E F
1 2 3 4 5 6
For every length of partition p, generate every set of groups of size p, e.g.:
p=k: ABCDEF C(k,k) sets
p=k-1: ABCDE ABCDF ABCEF ABDEF ACDEF BCDEF C(k,k-1) sets
p=k-2: ABCD ABCE ABCF ABDE ABDF ... CDEF C(k,k-2) sets
...
p=2: AB AC AD AE AF BC BD BE BF ... EF C(k,2) sets
For each of these sets, generate the combinations for the partitions with the corresponding number of parts, e.g.:
p=k-1: ABCDE [2,1,1,1,1] -> [a,a,b,c,d,e] C(g,2)*C(g,1)^4 combinations
[1,2,1,1,1] -> [a,b,b,c,d,e]
[1,1,2,1,1] -> [a,b,c,c,d,e]
[1,1,1,2,1] -> [a,b,c,d,d,e]
[1,1,1,1,2] -> [a,b,c,d,e,e]
ABCDE [2,1,1,1,1] -> [a,a,b,c,d,f]
[1,2,1,1,1] -> [a,b,b,c,d,f]
[1,1,2,1,1] -> [a,b,c,c,d,f]
[1,1,1,2,1] -> [a,b,c,d,d,f]
[1,1,1,1,2] -> [a,b,c,d,f,f]
...
BCDEF [2,1,1,1,1] -> [b,b,c,d,e,f]
[1,2,1,1,1] -> [b,c,c,d,e,f]
[1,1,2,1,1] -> [b,c,d,d,e,f]
[1,1,1,2,1] -> [b,c,d,e,e,f]
[1,1,1,1,2] -> [b,c,d,e,f,f]
From the list of sets, remove the sets which do not contain the last group (F):
p=k: ABCDEF
p=k-1: ABCDF ABCEF ABDEF ACDEF BCDEF
p=k-2: ABCF ABDF ABEF ACDF ACEF ADEF BCDF BCEF BDEF CDEF
...
p=2: AF BF CF DF EF
Load the next groups up to e/g into memory as group F, e.g.:
A B C D E F
1 2 3 4 5 7
...
1 2 3 4 5 e/g
Again, for each of these, and for each of the sets, generate the combinations for the partitions with the corresponding number of parts.
From the list of sets, remove the sets which do not contain the two last groups (EF):
p=k: ABCDEF
p=k-1: ABCEF ABDEF ACDEF BCDEF
p=k-2: ABEF ACEF ADEF BCEF BDEF CDEF
...
p=2: EF
Load the next groups up to e/g-1 into memory as group E, and for each of these, load the groups after E up to e/g into memory as group F, e.g.:
A B C D E F
1 2 3 4 6 7
...
1 2 3 4 e/g-1 e/g
Again, for each of these, and for each of the sets, generate the combinations for the partitions with the corresponding number of parts.
From the list of sets, remove the sets which do not contain the three last groups (DEF):
p=k: ABCDEF
p=k-1: ABDEF ACDEF BCDEF
p=k-2: ADEF BDEF CDEF
...
p=2: none
Load the next groups up to e/g-2 into memory as group D, and for each of these, load the groups after D up to e/g-1 into memory as group E, and for each of these, load the groups after E up to e/g into memory as group F, e.g.:
A B C D E F
1 2 3 5 6 7
...
1 2 3 e/g-2 e/g-1 e/g
Again, for each of these, and for each of the sets, generate the combinations for the partitions with the corresponding number of parts.
And so on, until you reach:
A B C D E F
e/g-5 e/g-4 e/g-3 e/g-2 e/g-1 e/g
with only:
p=k: ABCDEF
Run-through for 21,9,3
number of elements: e = 21
elements: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21]
size of combinations: k = 3 elements
size of memory: m = 9 elements
Preparations:
number of groups in memory: 3 (k)
group size: g = m/k = 3 elements
number of groups: e/g = 7
groups: 1:[1,2,3] 2:[4,5,6] 3:[7,8,9] 4:[10,11,12] 5:[13,14,15] 6:[16,17,18] 7:[19,20,21]
number of element sets loaded into memory: C(e/g,k) = C(7,3) = 35
partitions of k with max part g: [1,1,1] [2,1] [3]
permutations: 3:{[1,1,1]} 2:{[1,2],[2,1]} 1:{[3]}
group sets: 3:{[A,B,C]} 2:{[A,B],[A,C],[B,C]} 1:{[A],[B],[C]}
Phase 1:
group sets: 3:{[A,B,C]} 2:{[A,B],[A,C],[B,C]} 1:{[A],[B],[C]} (all)
A B C
1 2 3 -> elements in memory: [1,2,3] [4,5,6] [7,8,9] -> 84 combinations
3: [1,1,1]:[A,B,C] -> [a,b,c] -> [1,4,7] [1,4,8] [1,4,9] [1,5,7] [1,5,8] [1,5,9] [1,6,7] [1,6,8] [1,6,9]
[2,4,7] [2,4,8] [2,4,9] [2,5,7] [2,5,8] [2,5,9] [2,6,7] [2,6,8] [2,6,9]
[3,4,7] [3,4,8] [3,4,9] [3,5,7] [3,5,8] [3,5,9] [3,6,7] [3,6,8] [3,6,9]
2: [1,2]:[A,B] -> [a,b,b] -> [1,4,5] [1,4,6] [1,5,6] [2,4,5] [2,4,6] [2,5,6] [3,4,5] [3,4,6] [3,5,6]
[1,2]:[A,C] -> [a,c,c] -> [1,7,8] [1,7,9] [1,8,9] [2,7,8] [2,7,9] [2,8,9] [3,7,8] [3,7,9] [3,8,9]
[1,2]:[B,C] -> [b,c,c] -> [4,7,8] [4,7,9] [4,8,9] [5,7,8] [5,7,9] [5,8,9] [6,7,8] [6,7,9] [6,8,9]
[2,1]:[A,B] -> [a,a,b] -> [1,2,4] [1,3,4] [2,3,4] [1,2,5] [1,3,5] [2,3,5] [1,2,6] [1,3,6] [2,3,6]
[2,1]:[A,C] -> [a,a,c] -> [1,2,7] [1,3,7] [2,3,7] [1,2,8] [1,3,8] [2,3,8] [1,2,9] [1,3,9] [2,3,9]
[2,1]:[B,C] -> [b,b,c] -> [4,5,7] [4,6,7] [5,6,7] [4,5,8] [4,6,8] [5,6,8] [4,5,9] [4,6,9] [5,6,9]
1: [3]:[A] -> [a,a,a] -> [1,2,3]
[3]:[B] -> [b,b,b] -> [4,5,6]
[3]:[C] -> [c,c,c] -> [7,8,9]
Phase 2:
group sets: 3:{[A,B,C]} 2:{[A,C],[B,C]} 1:{[C]} (sets without C removed)
A B C
1 2 4 -> elements in memory: [1,2,3] [4,5,6] [10,11,12] -> 64 combinations
3: [1,1,1]:[A,B,C] -> [a,b,c] -> [1,4,10] [1,4,11] [1,4,12] [1,5,10] [1,5,11] [1,5,12] [1,6,10] [1,6,11] [1,6,12]
[2,4,10] [2,4,11] [2,4,12] [2,5,10] [2,5,11] [2,5,12] [2,6,10] [2,6,11] [2,6,12]
[3,4,10] [3,4,11] [3,4,12] [3,5,10] [3,5,11] [3,5,12] [3,6,10] [3,6,11] [3,6,12]
2: [1,2]:[A,C] -> [a,c,c] -> [1,10,11] [1,10,12] [1,11,12] [2,10,11] [2,10,12] [2,11,12] [3,10,11] [3,10,12] [3,11,12]
[1,2]:[B,C] -> [b,c,c] -> [4,10,11] [4,10,12] [4,11,12] [5,10,11] [5,10,12] [5,11,12] [6,10,11] [6,10,12] [6,11,12]
[2,1]:[A,C] -> [a,a,c] -> [1,2,10] [1,3,10] [2,3,10] [1,2,11] [1,3,11] [2,3,11] [1,2,12] [1,3,12] [2,3,12]
[2,1]:[B,C] -> [b,b,c] -> [4,5,10] [4,6,10] [5,6,10] [4,5,11] [4,6,11] [5,6,11] [4,5,12] [4,6,12] [5,6,12]
1: [3]:[C] -> [c,c,c] -> [10,11,12]
A B C
1 2 5 -> elements in memory: [1,2,3] [4,5,6] [13,14,15] -> 64 combinations
1 2 6 -> elements in memory: [1,2,3] [4,5,6] [16,17,18] -> 64 combinations
1 2 7 -> elements in memory: [1,2,3] [4,5,6] [19,20,21] -> 64 combinations
Phase 3:
group sets: 3:{[A,B,C]} 2:{[B,C]} (sets without B removed)
A B C
1 3 4 -> elements in memory: [1,2,3] [7,8,9] [10,11,12] -> 45 combinations
3: [1,1,1]:[A,B,C] -> [a,b,c] -> [1,7,10] [1,7,11] [1,7,12] [1,8,10] [1,8,11] [1,8,12] [1,9,10] [1,9,11] [1,9,12]
[2,7,10] [2,7,11] [2,7,12] [2,8,10] [2,8,11] [2,8,12] [2,9,10] [2,9,11] [2,9,12]
[3,7,10] [3,7,11] [3,7,12] [3,8,10] [3,8,11] [3,8,12] [3,9,10] [3,9,11] [3,9,12]
2: [1,2]:[B,C] -> [b,c,c] -> [7,10,11] [7,10,12] [7,11,12] [8,10,11] [8,10,12] [8,11,12] [9,10,11] [9,10,12] [9,11,12]
[2,1]:[B,C] -> [b,b,c] -> [7,8,10] [7,9,10] [8,9,10] [7,8,11] [7,9,11] [8,9,11] [7,8,12] [7,9,12] [8,9,12]
A B C
1 3 5 -> elements in memory: [1,2,3] [7,8,9] [13,14,15] -> 45 combinations
1 3 6 -> elements in memory: [1,2,3] [7,8,9] [16,17,18] -> 45 combinations
1 3 7 -> elements in memory: [1,2,3] [7,8,9] [19,20,21] -> 45 combinations
1 4 5 -> elements in memory: [1,2,3] [7,8,9] [13,14,15] -> 45 combinations
1 4 6 -> elements in memory: [1,2,3] [7,8,9] [16,17,18] -> 45 combinations
1 4 7 -> elements in memory: [1,2,3] [7,8,9] [19,20,21] -> 45 combinations
1 5 6 -> elements in memory: [1,2,3] [7,8,9] [16,17,18] -> 45 combinations
1 5 7 -> elements in memory: [1,2,3] [7,8,9] [19,20,21] -> 45 combinations
1 6 7 -> elements in memory: [1,2,3] [7,8,9] [19,20,21] -> 45 combinations
Phase 4:
group sets: 3:{[A,B,C]} (sets without A removed)
A B C
2 3 4 -> elements in memory: [4,5,6] [7,8,9] [10,11,12] -> 27 combinations
3: [1,1,1]:[A,B,C] -> [a,b,c] -> [4,7,10] [4,7,11] [4,7,12] [4,8,10] [4,8,11] [4,8,12] [4,9,10] [4,9,11] [4,9,12]
[5,7,10] [5,7,11] [5,7,12] [5,8,10] [5,8,11] [5,8,12] [5,9,10] [5,9,11] [5,9,12]
[6,7,10] [6,7,11] [6,7,12] [6,8,10] [6,8,11] [6,8,12] [6,9,10] [6,9,11] [6,9,12]
A B C
2 3 5 -> elements in memory: [4,5,6] [7,8,9] [13,14,15] -> 27 combinations
2 3 6 -> elements in memory: [4,5,6] [7,8,9] [16,17,18] -> 27 combinations
2 3 7 -> elements in memory: [4,5,6] [7,8,9] [19,20,21] -> 27 combinations
2 4 5 -> elements in memory: [4,5,6] [10,11,12] [13,14,15] -> 27 combinations
2 4 6 -> elements in memory: [4,5,6] [10,11,12] [16,17,18] -> 27 combinations
2 4 7 -> elements in memory: [4,5,6] [10,11,12] [19,20,21] -> 27 combinations
2 5 6 -> elements in memory: [4,5,6] [13,14,15] [16,17,18] -> 27 combinations
2 5 7 -> elements in memory: [4,5,6] [13,14,15] [19,20,21] -> 27 combinations
2 6 7 -> elements in memory: [4,5,6] [16,17,18] [19,20,21] -> 27 combinations
3 4 5 -> elements in memory: [7,8,9] [10,11,12] [13,14,15] -> 27 combinations
3 4 6 -> elements in memory: [7,8,9] [10,11,12] [16,17,18] -> 27 combinations
3 4 7 -> elements in memory: [7,8,9] [10,11,12] [19,20,21] -> 27 combinations
3 5 6 -> elements in memory: [7,8,9] [13,14,15] [16,17,18] -> 27 combinations
3 5 7 -> elements in memory: [7,8,9] [13,14,15] [19,20,21] -> 27 combinations
3 6 7 -> elements in memory: [7,8,9] [16,17,18] [19,20,21] -> 27 combinations
4 5 6 -> elements in memory: [10,11,12] [13,14,15] [16,17,18] -> 27 combinations
4 5 7 -> elements in memory: [10,11,12] [13,14,15] [19,20,21] -> 27 combinations
4 6 7 -> elements in memory: [10,11,12] [16,17,18] [19,20,21] -> 27 combinations
5 6 7 -> elements in memory: [13,14,15] [16,17,18] [19,20,21] -> 27 combinations
Results:
Phase 1: 84 = 84 combinations
Phase 2: 4 x 64 = 256 combinations
Phase 3: 10 x 45 = 450 combinations
Phase 4: 20 x 27 = 540 combinations
----
1330 combinations = C(21,3)
Depending on how heavy is the calculation you do for each combination, the fastest way might be to partition the range 0..C(n,k) into a bunch of subranges to be processed in parallel, and within each subrange generate the respective combinations directly on the GPU, using an unranking function to generate the first one and the classical algorithm for finding the next combination to generate subsequent ones.

Algorithm to calculate the price volatility of a commodity

I am trying to design an algorithm to calculate how volatile the price fluctuations of a commodity are.
The way I would like this to work is that if the price of a commodity constantly goes up and down, it should have a higher score than if the price of the commodity gradually increases and then falls in price rapidly.
Here is an example of what I mean:
Commodity A: 1 -> 2 -> 3 -> 2 -> 1 -> 3 -> 4 -> 2 -> 1
Commodity B: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 2
Commodity C: 1 -> 2 -> 3 -> 4 -> 5 -> 4 -> 3 -> 2-> 1
Commodity A has a 'wave' like pattern in that its price goes up and falls down on a regular basis.
Commodity B has a 'cliff' like pattern in that the price goes up gradually and then falls steeply.
Commodity C has a 'hill' like pattern in that the price rises gradually and then falls gradually.
A should receive the highest ranking, followed by C, followed by B. The more of a wave pattern the price of the commodity follows, the higher a ranking it should have.
Does have any suggestions for an algorithm that could do this?
Thanks!
My Approach looks something like this.
For my algorithm, I am considering the above example.
A: 1 -> 2 -> 3 -> 2 -> 1 -> 3 -> 4 -> 2 -> 1
B: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 2
C: 1 -> 2 -> 3 -> 4 -> 5 -> 4 -> 3 -> 2-> 1
Now I will squash these list, by squash i mean taking the start value and end value of an increasing or decreasing sequence.
So, after squashing the list will look something like this.
A: 1 -> 3 -> 1 -> 4 -> 1
B: 1 -> 8 -> 2
C: 1 -> 5 -> 1
Now once this it done, I take the difference between i and i+1 element and then take the average and based on the average, I give them the rank.
So the difference between i and i+1 element will look something like this
2 2 3 3
A: 1 --> 3 --> 1 --> 4 --> 1
7 6
B: 1 --> 8 --> 2
4 4
C: 1 --> 5 --> 1
Now let's sum this difference and take the average.
A: (2+2+3+3)/4 = 2.5
B: (7+6)/2 = 6.5
C: (4+4)/2 = 4
Now we can assign ranks based on this average value where
A < C < B
Hope this helps!

Minimum number of moves to empty all buckets, which all have same capacity but different amount of water initially. I can only move left to right

Problem definition
I have n buckets with the same capacity m, one next to the other. I can pour water from one bucket to the one to it's right.Tthe goal is to empty them all into another container but only the rightmost bucket can be emptied. They each have a certain initial amount of water w, where 0 <= w <= m and w is an integer. You can't do partial moves in the sense that if you have the following case: 6 6 -> 3 9 where you only pour 3, that would not be allowed. If you pour, you have to pour as much as you can, so a legal move would be 6 6 -> 2 10.
What is the minimum number of moves I have to make to empty all of the buckets? The maximum amount of buckets is 1000 and the maximum capacity is 100.
Examples
Example 1
4 buckets capacity 10 with the following amount of water: 4 0 6
The answer would be 4 0 6 -> 0 4 6 -> 0 0 10 -> 0 0 0 which is three moves.
Example 2
3 buckets capacity 10, 8 9 3
8 9 3 -> 8 2 10 -> 0 10 10 -> 0 10 0 -> 0 0 10 -> 0 0 0 = 5 moves total
I first tried doing it with different types of algorithms (greedy, dynamic, backtracking, etc) but none seemed to work. I thought I found a pretty intuitive solution but the program that checks these answers tells me it's wrong, so I might be wrong. Another thing is that this program has rejected correct answers previously so I'm not really sure.
Here is my solution:
Calculate the sum of all of the buckets before each bucket, and then take the ceiling of that number divided by the capacity of the buckets, and then add all of those numbers.
For example: 6 6 6 6 6 -> 6 12 18 24 30
ceil(6/10) ceil(12/10) ceil(18/10) ceil(24/10) ceil(30/10) = 1 + 2 + 2 + 3 + 3 = 11
that is the right answer: 6 6 6 6 6 -> 6 2 10 6 6 -> 0 8 10 6 6 -> 0 8 10 2 10 -> 0 8 2 10 10 -> 0 0 10 10 10 -> 0 0 10 10 0 -> 0 0 10 0 10 -> 0 0 10 0 0 -> 0 0 0 10 0 -> 0 0 0 0 10 -> 0 0 0 0 0 = 11 steps
The logic is that if there are L liters of water before a certain bucket, then there must be at least ceil(L/Capacity) moves that pass through that position. So far I have tried around 30 test cases and they have all worked. Every time I thought I found a counterexample, I realized I was wrong after trying it out a few times by hand. The problem is that although I am pretty sure this is the right answer, but I have no idea how to prove something like this or I might simply be wrong.
Can someone tell me if this answer is correct?
Here are my findings on the problem
First thing let's review the game rules:
Rule 1: You can only dispose all of the right-most bucket if it's filled
Rule 2: You can dispose as many liters as you want (up to 10-bucket[i+1]) from any bucket[i] to bucket[i+1]
Let's consider the following special cases:
Shifting: in case of 0, 10, 0, 0, 0 you shift the 2nd bucket 3 times before disposal
Merge: in case of 2 4 you can merge 1st bucket to 2nd to get 0 6
Perfect Merge: in case of 6 4 you merge to 0 10 getting 2nd bucket full, an optimal perfect merge will leave 0 in the current bucket
Conclusion: I suggest the following strategy, Right-to-Left Sequential Disposal which is from left to right, take a Perfect Merge and Shift it to disposal.
Note: this is not an optimal algorithm for this problem
void solution (int bucket [], const int size)
{
for (int i=size-1; i>=0; i--)
{
// find a non-empty bucket
if (bucket[i] > 0)
{
// bucket need to be filled
if (bucket[i] < 10)
for (int j=i-1; j>=0; j--)
{
// -------------------
// fill bucket[i] from previous
// buckets and count moves
// -------------------
if (bucket[i] == 10)
break;
}
bucket[i]=0; // (size-i) shift + 1 dispose
moves = moves + (size-i) + 1;
}
}
}
This is not a solution, but Python code that finds the minimum number of moves from a given configuration, by brute force. Every sequence of legal moves is tried and the shortest length is reported.
B= [6, 6, 6, 6, 6] # Buckets state
M= sum(B) * len(B) # Gross upper bound
B.append(- sum(B)) # Append the container
def Try(N):
global M
# Check if we have found a better solution
if B[-1] == 0 and N < M:
M= N
# Try every possible move from i to i+1
for i in range(len(B) - 1):
# Amount transferable
D= min(B[i], 10 - B[i + 1])
if D > 0:
# Transfer
B[i]-= D
B[i + 1]+= D
# Recurse
Try(N+1)
# Restore
B[i]+= D
B[i + 1]-= D
Try(0)
print M, "moves"
>>>
11 moves
Few points that can influence the design of right algorithm :-
rightmost full buckets are directly disposable. Just need to add n*(n+1)/2 for n rightmost buckets for total steps.
if rightmost bucket is not full then check the previous bucket if it is full or is able to fill rightmost then do else try to fill that
bucket do this recursively till either condition is met or first bucket
is reached. Then pour the bucket into next till it is full or other is
empty bottom up and count steps.
Do 1 to 2 till only 1 or no bucket is left
if one bucket is left then empty it.
Examples :-
given 4 0 6
1. fails
2. last bucket is not empty hence try to pour second last but it is also empty
pour 4 into bucket 1 and then bucket 1 into bucket 2 . Hence after this step
4 0 6 => 0 4 6 => 0 0 10
3. 1 bucket left so 4
4. empty bucket 0 0 10 => 0 0 0
given 8 9 3
iteration 1 : 8 9 3 => 8 2 10
iteration 2 : 8 2 10 => 8 2 0 and 8 2 0 => 0 10 0
iteration 3 : 0 10 0 => 0 0 10 => 0 0 0
given 6 6 6 6 6
iteration 1 : 6 6 6 6 6 => 6 6 6 2 10
iteration 2 : 6 6 6 2 10 => 6 6 6 2 0 && 6 6 6 2 0 => 6 2 10 2 0 => 6 2 2 10 0
iteration 3 : 6 2 2 10 0 => 6 2 2 0 10 => 6 2 2 0 0 &&
6 2 2 0 0 => 0 8 2 0 0 => 0 0 10 0 0
iteration 4 : 0 0 10 0 0 => 0 0 0 10 0 => 0 0 0 0 10 => 0 0 0 0 0
Time complexity :- Straight forward implementation would be O(n^2) because in 1 iteration atleast 1 rightmost bucket is emptied in O(n) computation.
There is a wonderfully simple solution.
The optimal number of moves is known to be Sum(0<=i<N: Pi\C), where \ denotes integer division by excess and the Pi form the prefix sum of B.
Example:
6 6 6 6 6
6 12 18 24 30
1 2 2 3 3 => 11
It suffices to always choose a move that decreases the optimal number by one unit. This move can be found in linear time with respect to N. (Hint: find some Pi\C that a move decreases.)
2>10 6 6 6
2 12 18 24 30
1 2 2 3 3 => 11
6 2>10 6 6
6 8 18 24 30
1 1* 2 3 3 => 10
6 6 2>10 6
6 12 14 24 30
1 2 2 3 3 => 11
6 6 6 2>10
6 12 18 20 30
1 2 2 2* 3 => 10
6 6 6 6 0>
6 12 18 24 24
1 2 2 3 3 => 11
The total complexity is O(N.M), where M is the optimal number of moves.
B= [6, 6, 6, 6, 6]
C= 10
# Show the current state
print B
# Append the container
B.append(- sum(B))
# Loop until all buckets are empty
while B[-1] != 0:
# Emulate the quotient by excess
Prefix= C - 1
# Try all buckets
for i in range(len(B) - 1):
# Amount transferable
A= min(B[i], C - B[i + 1])
# Evaluate the move from i to i+1
Prefix+= B[i]
if (Prefix - A) / C < Prefix / C:
# The total count will decrease, accept this move
B[i]-= A
B[i + 1]+= A
break
# Show the current state
print B[:-1]
>>>
[6, 6, 6, 6, 6]
[6, 2, 10, 6, 6]
[0, 8, 10, 6, 6]
[0, 8, 10, 2, 10]
[0, 8, 2, 10, 10]
[0, 0, 10, 10, 10]
[0, 0, 10, 10, 0]
[0, 0, 10, 0, 10]
[0, 0, 0, 10, 10]
[0, 0, 0, 10, 0]
[0, 0, 0, 0, 10]
[0, 0, 0, 0, 0]
The proof of termination of the algorithm rests on the validity of the optimal number formula, which remains to demonstrate.

Finding the root value of a binary tree?

I have an array which stores the relations of values, which makes several trees something like:
So, in this case, my array would be (root, linked to)
(8,3)
(8,10)
(3,1)
(3,6)
(6,4)
(6,7)
(10,14)
(14,13)
And i'd like to set all the root values in the array to the main root in the tree (in all trees):
(8,3)
(8,1)
(8,6)
(8,4)
(8,7)
(8,10)
(8,14)
(8,13)
What algorithm should i investigate?
1) Make a list of all the unique first elements of the tuples.
2) Remove any that also appear as the second element of a tuple.
3) You'll be left with the root (8 here). Replace the first elements of all tuples with this value.
EDIT:
A more complicated approach that will work with multiple trees would be as follows.
First, convert to a parent lookup table:
1 -> 3
3 -> 8
4 -> 6
6 -> 3
7 -> 6
10 -> 8
13 -> 14
14 -> 10
Next, run "find parent with path compression" on each element:
1)
1 -> 3 -> 8
gives
1 -> 8
3 -> 8
4 -> 6
...
3)
3 -> 8
4)
4 -> 6 -> 3 -> 8
gives
1 -> 8
3 -> 8
4 -> 8
6 -> 8
7 -> 6
...
6)
6 -> 8 (already done)
7)
7 -> 6 -> 8
etc.
Result:
1 -> 8
3 -> 8
4 -> 8
6 -> 8
7 -> 8
...
Then convert this back to the tuple list:
(8,1)(8,3)(8,4)...
The find parent with path compression algorithm is as find_set would be for disjoint set forests, e.g.
int find_set(int x) const
{
Element& element = get_element(x);
int& parent = element.m_parent;
if(parent != x)
{
parent = find_set(parent);
}
return parent;
}
The key point is that path compression helps you avoid a lot of work. In the above, for example, when you do the lookup for 4, you store 6 -> 8, which makes later lookups referencing 6 faster.
So assume you have a list of tuples representing the points:
def find_root(ls):
child, parent, root = [], [], []
for node in ls:
parent.append(node[0])
child.append(node[1])
for dis in parent:
if (!child.count(dis)):
root.append(dis)
if len(root) > 1 : return -1 # failure, the tree is not formed well
for nodeIndex in xrange(len(ls)):
ls[nodeIndex] = (root[0], ls[nodeIndex][1])
return ls

Resources