Using itertools.permutation where r > n - python

I am trying to generate all permuations of a set of items and the my R needs to be larger than the size of the set of items
Here is an example :
itertools.permutations ("ABC", 4)
this always returns 0 items as R > N.
I want this
[A, A, A, A]
[A, A, A, B]
[A, A, B, A]
[A, B, A, A]
...
How can I achieve this?

You don't seem to want the permutation, but the Cartesian product:
itertools.product("ABC", repeat=4)
https://docs.python.org/3/library/itertools.html#itertools.product

Related

next item of list inside dataframe

I have a dataframe that has a column where each row has a list.
I want to get the next element after the value I am looking for (in another column).
For example:
Let's say I am looking for 'b':
|lists |next_element|
|---------|------------|
|[a,b,c,d]| c | #(c is the next value after b)
|[c,b,a,e]| a | #(a is the next value after b)
|[a,e,f,b]| [] | #(empty, because there is no next value after b)
*All lists have the element. There are no lists without the value I am looking for
Thank you
Try writing a function and use apply.
value = 'b'
def get_next(x):
get_len = len(x)-1
for i in x:
if value.lower() == i.lower():
curr_idx = x.index(i)
if curr_idx == get_len:
return []
else:
return x[curr_idx+1]
df["next_element"] = df["lists"].apply(get_next)
df
Out[649]:
lists next_element
0 [a, b, c, d] c
1 [c, b, a, e] a
2 [a, e, f, b] []
First observation, since you want the next element of a list of string elements, the expected data type should be a string for that column, and not a list.
So, instead of the next_element columns as [c, a, []] its better to use [c, a, None]
Secondly, you should try avoiding apply methods directly over series and instead utilize the str methods that pandas provides for series which is a vectorized way of solving such problems super fast.
With the above in mind, let's try this completely vectorized one-liner -
element = 'b'
df['next_element'] = df.lists.str.join('').str.split(element).str[-1].str[0]
lists next_element
0 [a, b, c, d] c
1 [c, b, a, e] a
2 [a, e, f, b] NaN
First I combine each row as a single string [a,b,c,d]->'abcd`
Next I split this by 'b' to get substrings
I pick the last element from this list and finally the first element from that, for each row, using str functions which are vectorized over each row.
Read more about pandas.Series.str methods on official documentation/tutorial here
df = df.assign(next_element = "")
print(df)
for ind in df.index:
c= df["Lists"][ind]
for i,v in enumerate(c):
if v == "b":
df["next_element"][ind] = c[i+1]
print(df)
Try with this one you will get the exact output what you expected.

Big O time complexity for itertools combinations

I created a code using itertools combination. The code snippet is below. What is the Big O complexity? Is it O(n!)?
a_list =[ 'A', 'B','C' ]
all_combinations = []
for r in range(len(a_list) + 1):
combinations_object = itertools.combinations(a_list, r)
combinations_list = list(combinations_object)
all_combinations += combinations_list
print(all_combinations)
[(), (A,), (B,), (C,), (A, B), (A, C), (B, C), (A, B, B)]
Thank you!
Getting all combinations of a list is O(n!). Since you're doing that n times to get combinations with different values of r, the whole algorithm is O(n*n!).

Generate a list from linear combination in simpy

I am using sympy to do some calculations, but I need to manipulate results of multiplications using rules I have to define. Let us suppose I have
a, b, c, d = symbols('a b c d', commutative=False)
res = a*b + b*c + c*d
I was wondering how can I write a function which takes res and gives a list of this kind
[[a,b],[b,c],[c,d]]
since every time I try to perform operations like list(res) python throws the exception Mul (Add) object is not iterable. Thanks in advance
There is no function for this already but you can make one like this:
def factors(expr):
result = []
for term in Add.make_args(expr):
flat_factors = []
for factor in Mul.make_args(term):
symbol, power = factor.as_base_exp()
flat_factors += [symbol] * power
result.append(flat_factors)
return result
That gives:
In [74]: factors(a*b + b*c + c*d)
Out[74]: [[a, b], [b, c], [c, d]]
In [75]: factors(a**2*b + a*b**2)
Out[75]: [[a, b, b], [a, a, b]]
This is a way:
[x.as_ordered_factors() for x in res.as_ordered_terms()]
No sure, but do you wanna do something like this?
res = 'a*b + b*c + c*d'
def list_func(res):
list1 = res.split('+')
return ([item.split('*') for item in list1])
print(list_func(res))

loops list slicing + elements allocation Python

I am pretty beginner in Python and trying to do the following:
main_list[80,80,30,30,30,30,20,10,5,4,3,2,1] #list of integers
- slicing the main_list in multiple lists for example list1,2,3,..,n with a sum of sub lists < 100
for i in range of n:
print(list(i))
list1[80,20], list2[80,10,5,4,1], list3[30,30,30], listn[30,3,2]
Thanks!
It's not really clear to me what you consider an acceptable output so I'm assuming that it's any list where its elements sum less than 100.
The solution I found is using recursion. For the list [a, b, c, d] we are going to check the condition for this sublists:
[a]
[a, b] (if the condition for [a] is met)
[a, b, c] (if the condition for [a, b] is met)
[a, b, c, d] (if the condition for [a, b, c] is met)
[a, c] (if the condition for [a] is met)
[a, c, d] (if the condition for [a, c] is met)
[a, d] (if the condition for [a] is met)
[b]
[b, c] (if the condition for [b] is met)
[b, c, d] (if the condition for [b, c] is met)
[b, d] (if the condition for [b] is met)
[c]
[c, d] (if the condition for [c] is met)
[d]
The concept is that for the "n" element in the list we are going to look for the sublists of size "n - 1" to 0 (so the element itself) that meet the requirements. The sublists are formed by the elements at the right of the studied element of each iteration, so for the first 30, the sublist to use would be [30, 30, 30, 20, 10, 5, 4, 3, 2, 1]
This process of finding the sublists for each element is the one that uses recursion. It calls itself for each element of the sublists checking if it meets the condition. For the example above, if the condition is met for [a, b] then it will also try for [a, b, c] and [a, b, d] (by calling itself with the sum of (a, b) and the sublist [c, d].
I've added a few prints so you can study how it works, but you should just use the results variable at the end of the script for getting your results.
main_list = [80,80,30,30,30,30,20,10,5,4,3,2,1]
def less_than_hundred(input) -> bool:
return input < 100
def sublists_meet_condition(condition, input):
"""
This function is used to call the sublists_for_element function with every element in the original list and its sublist:
- For the first element (80) it calls the second function with the sublist [80,30,30,30,30,20,10,5,4,3,2,1]
- For the fifth element (30) it calls the second function with the sublist [30,20,10,5,4,3,2,1]
Its purpose is to collect all the sublists that meet the requirements for each element
"""
results = []
for index, element in enumerate(input):
print('Iteration {} - Element {}'.format(index, element))
if condition(element):
results.append([element])
print('{} = {}'.format([element], element))
num_elements = len(input) - index
main_element = element
sublist = input[index+1:]
for result in sublists_for_element(condition, main_element, sublist):
new_result = [element] + result
sum_new_result = sum(new_result)
results.append(new_result)
print('{} = {}'.format([element] + result, sum_new_result))
return results
def sublists_for_element(condition, sum_main_elements, sublist):
"""
This function is used to check every sublist with the given condition.
The variable sum_main_elements allows the function to call itself and check if for a given list of numbers that meet the conditions [30, 30, 4] for example, any of the elements of the remaining sublists also meets the condition for example adding the number 3 still meets the condition.
Its purpose is to return all the sublists that meet the requirements for the given sum of main elements and remaining sublist
"""
num_elements = '{}{}'.format('0' if len(sublist) + 1 < 10 else '',len(sublist) + 1)
#print('Elements: {} | Main element: {} | Sublist: {}'.format(num_elements, sum_main_elements, sublist))
result = []
for index, element in enumerate(sublist):
if condition(sum_main_elements + element):
result.append([element])
sublist_results = sublists_for_element(condition, sum_main_elements + element, sublist[index+1:])
for sublist_result in sublist_results:
result.append([element] + sublist_result)
return result
results = sublists_meet_condition(less_than_hundred, main_list)

Combing for loops

When flattening for loops, you would do something like this:
for a, b, c in itertools.product(x1, x2, x3):
...
But how would you handle the case where one of the elements is to be used as a parameter to retrieve a list also to be traversed? For example:
for a, b in itertools.product(x1, get_b_elements(a)):
...
Is this even possible?
for a in x1:
for b in get_b_elements(a):
#do something with (a, b)
Just to list an alternative:
for (a, b) in [(a_i, b_i) for a_i in x1 for b_i in get_b_elements(a_i)]:
#do something with (a, b)
As #wim notes, none of these "flatten" the loop as you want.
try this:
alphabets = [a,b,c,d]
xs = [x1,x2,x3]
itertools.product(alphabets, xs)

Categories