Related
I have a VERY simple question.
Honestly, I'm so sorry to ask you this stupid question but I really don't know how to do this.
I have a list like this.
p = [2, 5, 1, 2, 4, 1, 2, 5, 1, 2, 4, 1, 2, 4, 1, 2, 5, 1, 2, 4]
The elements in the list are usually the repetition of either 2, 4, 1 or 2, 5, 1.
Sometimes, there are no 1 at the end of 2, 4 or 2, 5.
I want to put 3 in the new list where the 3 consecutive elements of 2, 4, 1 or 2, 5, 1 are.
2, 4, 1 -> 3
2, 5, 1 -> 3
And I want to put 2 in the new list where the elements of 2, 4 or 2, 5 are.
2, 4 -> 2
2, 5 -> 2
Also, if there is either 2, 4 or 2, 5 at the end of the last 2, 4, 1 or 2, 5, 1, I need to put 3.
[2, 4, 1, 2, 5, 1, 2, 5] ==> [3, 3, 3]
However, if there are more than 2 of either 2, 4 or 2, 5 in a row after 2, 4, 1 or 2, 5, 1, I want to put 2 in the list like below.
[2, 4, 1, 2, 5, 2, 5] ==> [3, 2, 2]
My simple code below doesn't give me 2 at the end of the new list. Why is that?
Actually, I altered my code a lot and I am not getting what I want...
Any help will be GREATLY appreciated!
new_list = []
p = [2, 5, 1, 2, 4, 1, 2, 5, 1, 2, 4, 1, 2, 4, 1, 2, 5, 1, 2, 4]
#p = [2, 5, 1, 2, 4, 1, 2, 5, 1, 2, 4, 1, 2, 4, 1, 2, 5, 1, 2, 4, 2, 4]
#p = [2, 4]
for i in range(0, len(p)):
if i <= len(p) - 3:
if p[i] == 2 and p[i+1] >= 4:
if p[i+2] !=1:
new_list.append(2)
elif p[i+2] == 1:
new_list.append(3)
print(new_list)
Let's do it step by step :
new_list = []
p = [2, 5, 1, 2, 4, 1, 2, 5, 1, 2, 4, 1, 2, 4, 1, 2, 5, 1, 2, 4]
# p = [2, 4, 1, 2, 5, 1, 2, 5]
# p = [2, 4, 1, 2, 5, 2, 5]
sublists = []
# First we split the list on each 2
for i in p:
if i == 2:
sublists.append([2])
else:
sublists[-1].append(i)
# Then we add the size of each sublist to the new list
new_list = [len(x) for x in sublists]
# This is the same as:
# for i in sublists:
# new_list.append(len(i))
# Finally we check the particular case of a 2 at the end of the new list
if len(new_list) > 0 and new_list[-1] == 2:
new_list[-1] = 3
if len(new_list) > 1 and new_list[-2] == 2:
new_list[-1] = 2
print(new_list)
It's often a good idea when you are dealing with multiple elements in a list to split it into sub-lists for more clarity.
I hope you find that useful.
This is my method of doing it:
new_list = []
p = [2, 5, 1, 2, 4, 1, 2, 5, 1, 2, 4, 1, 2, 4, 1, 2, 5, 1, 2, 4]
while len(p) > 0:
if p[0] == 2:
if len(p) >= 3:
if p[1] == 4 and p[2] == 1:
new_list.append(3)
p.pop(0)
p.pop(0)
p.pop(0)
elif p[1] == 4 and p[2] != 1:
new_list.append(2)
p.pop(0)
p.pop(0)
elif p[1] == 5 and p[2] == 1:
new_list.append(3)
p.pop(0)
p.pop(0)
p.pop(0)
elif p[1] == 5 and p[2] != 1:
new_list.append(2)
p.pop(0)
p.pop(0)
else:
if p[1] == 4:
new_list.append(2)
p.pop(0)
p.pop(0)
elif p[1] == 5:
new_list.append(2)
p.pop(0)
p.pop(0)
print(new_list)
Firstly, we being a while loop that works until the list p has 0 elements.
Then, we check if the first element of the list starts with a 2. In this case it does. We then check if the second and third elements are 4 and 1. If they are, we add 3 to the new list, and then remove these 3 terms from the list so that their is no repetition.
We check this for the second and third elements 5 and 1 as well, and we also check if the third element is not 1. If it is not 1, we add a 2.
Then, the code is repeated in a if statement. This is because, when the list only has 2 elements left, the 2, 4 in this case, their would be an index error when we check if p[2] == 1, as their is no 3rd element. Therefore, when their are only 2 elements, we check to see if they are 2, 4 or 2, 5, and if so, add 2 to the list.
The code works as long as the first element of the list is a 2, which it seems like it always is.
I have a list named A:
A = [1, 2, 1, 2, 1, 2, 3, 4, 5]
and I want to count the number of list [1, 2] appears in A.
It will be 3 because:
A = [1, 2, 1, 2, 1, 2, 3, 4, 5]
[1, 2][1, 2][1, 2] <- appears 3 times
Here is my code:
result = 0
while len(A) > 0:
temp, tA = [1, 2], A
for i in temp:
if len(tA) == 0: break
if i in tA: tA.pop(0)
result += 1
return result
And I wonder know is there any faster way to accomplish the same function?
I'm trying to get a list of lists (or tuples) which follows a pattern something like this:
[1,1,1,2]
[1,1,2,2]
[1,2,2,2]
[1,2,2,3]
[1,2,3,3]
[1,2,3,4]
Using itertools.combinations_with_replacement I've gotten close, but I end up with lists which jump values for example:
[1,1,1,3]
or
[2,2,2,3]
I don't want this. I always want to start at 1, and increase until the list is filled, and then increase to the next value.
If I'm using itertools, then is there a way to remove the lists that I don't want?
Instead of using combinations, I would generate the pattern directly.
Create a list of 1's with the desired length and iterate backward, changing the list accordingly.
def generate_increment(n):
lst = [1] * n
result = []
for k in range(n-1):
lst[-1] += 1
result.append(lst[:])
for i in range(len(lst)-2, k, -1):
a, b = lst[i], lst[i+1]
if a != b:
lst[i] = b
result.append(lst[:])
return result
>>print(*generate_increment(4), sep='\n')
[1, 1, 1, 2]
[1, 1, 2, 2]
[1, 2, 2, 2]
[1, 2, 2, 3]
[1, 2, 3, 3]
[1, 2, 3, 4]
It feels like validating the results of combinations will be a bigger effort than simply creating those lists you need.
This can be done with a recursive function that adds with each step what value to add to the list until a defined size:
def gen_list(pre, size):
if size == 1:
return [pre]
res = gen_list(pre + [pre[-1]], size - 1)
res.extend(gen_list(pre + [pre[-1]+1], size-1))
return res
for l in gen_list([1], 4):
print(l)
Which prints:
[1, 1, 1, 1]
[1, 1, 1, 2]
[1, 1, 2, 2]
[1, 1, 2, 3]
[1, 2, 2, 2]
[1, 2, 2, 3]
[1, 2, 3, 3]
[1, 2, 3, 4]
Suppose that we are considering a sorted list of all non-decreasing sequences with values in the range (1, max_num) and num_slots elements in each sequence, how can I find the index of some given member sequence in O(1) time complexity? I am not actually given the entire list up-front, I just want to find the index of some member sequence were the list of all sequences to exist.
For a concrete example, suppose that max_num = 3 and num_slots = 4. Then there are 15 sequences (or generally, there are (max_num + num_slots - 1) choose (num_slots) sequences):
[[1, 1, 1, 1],
[1, 1, 1, 2],
[1, 1, 1, 3],
[1, 1, 2, 2],
[1, 1, 2, 3],
[1, 1, 3, 3],
[1, 2, 2, 2],
[1, 2, 2, 3],
[1, 2, 3, 3],
[1, 3, 3, 3],
[2, 2, 2, 2],
[2, 2, 2, 3],
[2, 2, 3, 3],
[2, 3, 3, 3],
[3, 3, 3, 3]]
So given inputs of a sequence like [1, 2, 2, 3] along with the information max_num = 3, I am trying to write a function that would return its correct index of 7. I do not actually have the list of all sequences to work with.
Background info
I have come up with an algorithm to generate all non-decreasing sequences I care about, but this doesn't seem completely relevant for generating the index of a particular member sequence without the whole list of sequences materialized.
def gen(max_num, num_slots, l = None):
if l is None:
l = [[1] * num_slots]
cur = l[-1].copy()
for i in reversed(range(num_slots)):
if cur[i] < max_num:
cur[i] += 1
for j in range(i+1, num_slots):
cur[j] = cur[i]
l.append(cur)
return gen(max_num, num_slots, l)
return l
This one is O(|seq| + max_num). Note that this is still much faster than the naive generate all and search approach, which is exponential in |seq|.
The idea is that you count the sequences before the input sequence. For example,
you want to know what's the index of [2, 4, 5, 6] when max_num = 6.
Count [1, *, *, *]
Count [2, 2, *, *]
Count [2, 3, *, *]
(Note: you cannot count [2, 4, *, *], because then you would include [2, 4, 6, 6] which comes after your input. You should always go until one less than your input at the given index)
Count [2, 4, 4, *]
Count [2, 4, 5, 5]
(for each row, you can use your formula, (max_num + num_slots - 1) choose (num_slots) and sum them up)
def combinations(slots, available):
return choose(slots + available - 1, slots)
def find_index(seq, max_num):
res = 0
for digit_index in xrange(len(seq)):
prev = seq[digit_index - 1] if digit_index > 0 else 1
for digit in xrange(prev, seq[digit_index]):
res += combinations(len(seq) - digit_index - 1, max_num - digit + 1)
return res
print find_index([1, 2, 2, 3], 3)
I'll elaborate on #DavidFrank's answer on why it is O(length+max_num) and give a more easily understand example (a bit more complex too).
To start with, observe:
Assume the total series possibility in F(length, max_num) = X
Then for all of possibilities in X that starts with 1, e.g. [1, ....], we have a count of F(length-1, max_num) within this group.
For all the possibility in X that does not start with 1, e.g. [2, ....] or [3, ....], we have a count of F(length, max_num-1).
Thus we can use recursion to get this in O(length*max_num) (can become O(length+max_num) if we use memoization) number of complexity:
# This calculate the total number of X of possible entry given (length, max_num)
def calc_sum(length, max_num):
if max_num == 1:
return 1
elif length == 1:
return max_num
else:
total = calc_sum(length-1, max_num) + calc_sum(length, max_num-1)
return total
Now we examine the result to see if we can make it O(1):
# This is clearly not going to make it O(1), so now we need some generalizations to NOT run this recursion.
import numpy as np
arr = np.zeros((6,6))
for i in range(6):
for j in range(6):
arr[i, j] = calc_sum(i+1, j+1)
print(arr)
The result is:
[[ 1. 2. 3. 4. 5. 6.]
[ 1. 3. 6. 10. 15. 21.]
[ 1. 4. 10. 20. 35. 56.]
[ 1. 5. 15. 35. 70. 126.]
[ 1. 6. 21. 56. 126. 252.]
[ 1. 7. 28. 84. 210. 462.]]
This is a pascal's triangle, if you look diagonally to the top right. The diagonals of pascal's triangle are defined by (x choose y)
This makes it clear that it cannot be O(1), and will at least be O(length+max_num) because this is the general complexity of (Choose) function.
We've went all the way to prove that an O(1) solution is impossible, unless we constrain (length + max_num) to be constant.
# We can expand by solving it now:
from scipy.special import comb # this is choose function.
def get_index(my_list, max_num):
my_list = np.array(my_list)
if len(my_list) == 1:
return my_list[0] - 1
elif my_list[0] == 1:
return get_index(my_list[1:], max_num)
elif my_list[0] != 1:
return get_index(my_list - 1, max_num - 1) + comb(len(my_list)-2+max_num, max_num-1)
get_index([1,2,2,3],3) # 7
The aggregated complexity of the final function with the comb() is still O(length + max_num) as the complexity of everything outside comb is O(length + max_num) as well.
There is bijection from the k-subsets of {1...n} (with repetition) to k-subsets of {1...n + k â 1} (without repetition) by mapping {c_0, c_1...c_(kâ1)} to {c_0, c_(1+1), c_(2+2)...c_(kâ1+kâ1)} (see here).
Once converted, just use your favourite combination ranking utility.
[3, 3, 3, 3] --> [3, 4, 5, 6]
[2, 3, 3, 3] --> [2, 4, 5, 6]
[2, 2, 3, 3] --> [2, 3, 5, 6]
[2, 2, 2, 3] --> [2, 3, 4, 6]
[2, 2, 2, 2] --> [2, 3, 4, 5]
[1, 3, 3, 3] --> [1, 4, 5, 6]
[1, 2, 3, 3] --> [1, 3, 5, 6]
[1, 2, 2, 3] --> [1, 3, 4, 6]
[1, 2, 2, 2] --> [1, 3, 4, 5]
[1, 1, 3, 3] --> [1, 2, 5, 6]
[1, 1, 2, 3] --> [1, 2, 4, 6]
[1, 1, 2, 2] --> [1, 2, 4, 5]
[1, 1, 1, 3] --> [1, 2, 3, 6]
[1, 1, 1, 2] --> [1, 2, 3, 5]
[1, 1, 1, 1] --> [1, 2, 3, 4]
import pyncomb
def convert(m, S):
return (m + len(S) - 1, [ x-1 + i for x,i in zip(S, list(xrange(len(S)))) ])
def rank(m, S):
k, s = convert(m, S)
return pyncomb.ksubsetcolex.rank(k, s)
print rank(3, [1,2,2,3])
# 7
For each digit, find the difference between that and the lowest digit. Add 1 for each changed position to the right of any altered digit
idx = 0;
for i in range(0,num_slots):
d = SEQ[i]
idx += d-min_num
if (d > min_num):
idx += num_slots-1 - i
For example:
[1,1,1,3] is 0 + 0 + 0 + (2+0) or 2
[1,2,3,3] is 0 + (1+2) + (2+1) + (2+0) or 8
[3,3,3,3] is (2+3) + (2+2) + (2+1) + (2+0) or 14
Given:
numma = [1,2,3,4], n = 7
I expected the following code to return:
[[1,2,3],[1,2,4]]
Instead, it returns a list with four copies of:
[1,2,4]
and six copies of:
[1,2,3,4]
The script is intended to take the elements of the list numbers = numma in order, and form a new list (this_chain) with them, as long as the sum of the elements in this_chain does not exceed n = 7.
When adding a new element would break that condition, the list this_chain is appended to another list (all_chains), and the process begins again. If the original list (numbers) runs out of elements, the current this_chain is appended to all_chains and the script finishes, returning the list (of lists) called all_chains.
When I run it step by step, it follows the expected behavior, but upon reaching the return statement (step 84), instead of returning all_chains and finish, the arrow moves half a step downwards and then jumps back to the first statement (for i in ...). Then it goes back down and appends another copy of the current this_chain to all_chains, and so on, taking 66 additional steps and returning the output I mentioned above.
It was suggested to me to iterate over a tuple, instead of over a list, but since I want to remove elements from the iteration sequence I do not see how it could be done.
I am pretty puzzled. The four questions I'd like to ask, in order of importance, are the following:
Why does the program not finish upon reaching the return statement? I believed a return statement would always terminate any script.
Given the behavior described above, why does the script finally end, instead of keeping on appending copies of valid lists to the list called all_chains?
Why does the script append invalid elements to the list all_chains, that is, lists whose elements sum up more than n = 7?
Why are elements already in all_chains removed (or modified), so that they are not present in the final output, even if they were appended to the list all_chains previously?
My code:
def chain(numbers, n, all_chains=[], sum=0, this_chain=[]):
for i in range(len(numbers)):
if numbers[i] not in this_chain:
sum += numbers[i]
if sum <= n:
this_chain.append(numbers[i])
chain(numbers, n, all_chains, sum, this_chain)
else:
if this_chain not in all_chains:
all_chains.append(this_chain)
mocha = numbers[:]
mocha.remove(this_chain[-1])
chain(mocha, n, all_chains, sum=0, this_chain=[])
all_chains.append(this_chain)
return all_chains
numma = [1,2,3,4]
chain(numma, 7, chains=[], sum=0, this_chain=[])
Why does the program not finish upon reaching the return statement?
I believed a return statement would always terminate any script.
return does not terminate the script; it leaves the current routine and returns control to the one that called it. When you're in a recursive call, it simply returns control to the "elder" version of itself that called it.
Given the behavior described above, why does the script finally
end, instead of keeping on appending copies of valid lists to the list
called all_chains?
It ends because it all active invocations eventually make it to the bottom of the function, ending their executions.
Why does the script append invalid elements to the list all_chains,
that is, lists whose elements sum up more than n = 7?
The problem is when you return to try adding 4, the list you think you have is [1, 2], but it actually reads [1, 2, 3] because ... well, see the next point.
Why are elements already in all_chains removed (or modified), so
that they are not present in the final output, even if they were
appended to the list all_chains previously?
Wherever you append to a list, you append a reference to the original, rather than a local copy. This comes from using mutable objects (as #tobias_k already noted). Thus, when you append [1, 2, 3] to the list (several times), all of these are the same object. When you later mistakenly append 4 to the list (thinking that the list has only [1, 2] in it), you change all the references to [1, 2, 3, 4]. I've fixed this in the code below.
Finally, you have all those extra lists because of the missing return statements; instead of stopping when you should, you go through the rest of the function, making more calls, and finding more copies of the same solutions.
I've added some stuff to your code so you can watch the execution. Most of all, I've added some ugly, but useful, print statements to track the execution. I have modules to handle the indentation, counts, and argument printing in more readable form, you can experiment to see what you find most readable.
I've also replaced your straight append operations with copies, so you can differentiate the problems better and learn more from your mistakes.
Remember, good choices come from experience.
Experience comes from bad choices.
import copy
invocation = 0
def chain(numbers, n, all_chains=[], sum=0, this_chain=[]):
global invocation
invocation += 1
local_id = invocation
indent = " "
print indent*local_id, local_id, "ENTER", "\tsum", sum, "\tthis", this_chain, "\tall", all_chains
for i in range(len(numbers)):
if numbers[i] not in this_chain:
sum += numbers[i]
if sum <= n:
print indent*local_id, local_id, "append to this", sum, this_chain, numbers[i]
this_chain.append(numbers[i])
chain(numbers, n, all_chains, sum, this_chain)
else:
if this_chain not in all_chains:
print indent*local_id, local_id, "append to all 1", this_chain
all_chains.append(copy.copy(this_chain))
mocha = numbers[:]
mocha.remove(this_chain[-1])
chain(mocha, n, all_chains, sum=0, this_chain=[])
print indent*local_id, local_id, "append to all 2", this_chain
all_chains.append(copy.copy(this_chain))
print indent*local_id, local_id, "LEAVE", all_chains
return all_chains
numma = [1, 2, 3, 4]
result = chain(numma, 7)
print
for x in result:
print x
Execution trace:
1 ENTER sum 0 this [] all []
1 append to this 1 [] 1
2 ENTER sum 1 this [1] all []
2 append to this 3 [1] 2
3 ENTER sum 3 this [1, 2] all []
3 append to this 6 [1, 2] 3
4 ENTER sum 6 this [1, 2, 3] all []
4 append to all 1 [1, 2, 3]
5 ENTER sum 0 this [] all [[1, 2, 3]]
5 append to this 1 [] 1
6 ENTER sum 1 this [1] all [[1, 2, 3]]
6 append to this 3 [1] 2
7 ENTER sum 3 this [1, 2] all [[1, 2, 3]]
7 append to this 7 [1, 2] 4
8 ENTER sum 7 this [1, 2, 4] all [[1, 2, 3]]
8 append to all 2 [1, 2, 4]
8 LEAVE [[1, 2, 3], [1, 2, 4]]
7 append to all 2 [1, 2, 4]
7 LEAVE [[1, 2, 3], [1, 2, 4], [1, 2, 4]]
6 append to all 2 [1, 2, 4]
6 LEAVE [[1, 2, 3], [1, 2, 4], [1, 2, 4], [1, 2, 4]]
5 append to all 2 [1, 2, 4]
5 LEAVE [[1, 2, 3], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 4]]
4 append to all 2 [1, 2, 3]
4 LEAVE [[1, 2, 3], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 3]]
3 append to all 2 [1, 2, 3]
3 LEAVE [[1, 2, 3], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 3], [1, 2, 3]]
2 append to this 7 [1, 2, 3] 4
9 ENTER sum 7 this [1, 2, 3, 4] all [[1, 2, 3], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 3], [1, 2, 3]]
9 append to all 2 [1, 2, 3, 4]
9 LEAVE [[1, 2, 3], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 3], [1, 2, 3], [1, 2, 3, 4]]
2 append to all 2 [1, 2, 3, 4]
2 LEAVE [[1, 2, 3], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 3], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4]]
1 append to all 2 [1, 2, 3, 4]
1 LEAVE [[1, 2, 3], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 3], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]
[1, 2, 3]
[1, 2, 4]
[1, 2, 4]
[1, 2, 4]
[1, 2, 4]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3, 4]
[1, 2, 3, 4]
[1, 2, 3, 4]