I am creating a program, in python, that allows me to generate NFT Art based on the given assets.
Obviously the number of arts that can be generated varies according to the assets (layers and layer images) and this is precisely the problem, how can I calculate the possible combinations also counting the optional layers?
To be clearer:
for example I have 4 layers:
l1 = ["A","B"]
l2 = ["C"]
l3 = ["D","E"] #optional
l4 = ["F","G"] #optional
where l3 and l4 are optional. So the combinations I expect are:
1. ["A","C"]
2. ["B","C"]
3. ["A","C","D"]
4. ["A","C","E"]
5. ["B","C","D"]
6. ["B","C","E"]
7. ["A","C","F"]
8. ["A","C","G"]
9. ["B","C","F"]
10. ["B","C","G"]
11. ["A","C","D","F"]
12. ["A","C","D","G"]
13. ["A","C","E","F"]
14. ["A","C","E","G"]
15. ["B","C","D","F"]
16. ["B","C","D","G"]
17. ["B","C","E","F"]
18. ["B","C","E","G"]
How can I get to that? I tried with itertools.product but obviusly it take into account all lists
One way to do this is with the powerset recipe from the itertools docs. Chaining together the products of 'required lists' with every subset of the 'optional-list-set' gives a generator that produces each possibility once:
def powerset(iterable):
"""powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"""
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s) + 1))
def product_with_optional(required_sequences, optional_sequences):
return chain.from_iterable(product(*required_sequences, *optionals)
for optionals in powerset(optional_sequences))
optional_combinations = product_with_optional(required_sequences=[l1, l2],
optional_sequences=[l3, l4])
which gives:
1 ('A', 'C')
2 ('B', 'C')
3 ('A', 'C', 'D')
4 ('A', 'C', 'E')
5 ('B', 'C', 'D')
6 ('B', 'C', 'E')
7 ('A', 'C', 'F')
8 ('A', 'C', 'G')
9 ('B', 'C', 'F')
10 ('B', 'C', 'G')
11 ('A', 'C', 'D', 'F')
12 ('A', 'C', 'D', 'G')
13 ('A', 'C', 'E', 'F')
14 ('A', 'C', 'E', 'G')
15 ('B', 'C', 'D', 'F')
16 ('B', 'C', 'D', 'G')
17 ('B', 'C', 'E', 'F')
18 ('B', 'C', 'E', 'G')
I'm assuming the ordering of the optional layers matter, so you can just iteratively create all combinations of the optional layers, then use itertools.product on the layers + optional_layers to generate the lists.
import itertools
from pprint import pprint
l1 = ["A","B"]
l2 = ["C"]
l3 = ["D","E"] #optional
l4 = ["F","G"] #optional
layers = [l1, l2]
optional_layers = [l3, l4]
results = []
results += itertools.product(*layers)
for i in range(len(optional_layers) + 1):
comb = itertools.combinations(optional_layers, r=i)
for c in comb:
results += itertools.product(*layers, *c)
pprint(results)
Output
[('A', 'C'),
('B', 'C'),
('A', 'C'),
('B', 'C'),
('A', 'C', 'D'),
('A', 'C', 'E'),
('B', 'C', 'D'),
('B', 'C', 'E'),
('A', 'C', 'F'),
('A', 'C', 'G'),
('B', 'C', 'F'),
('B', 'C', 'G'),
('A', 'C', 'D', 'F'),
('A', 'C', 'D', 'G'),
('A', 'C', 'E', 'F'),
('A', 'C', 'E', 'G'),
('B', 'C', 'D', 'F'),
('B', 'C', 'D', 'G'),
('B', 'C', 'E', 'F'),
('B', 'C', 'E', 'G')]
Related
so I will do my best to explain what I'm looking for,
at the moment I have a 100 item list that I want to repetitively shuffle using a set pattern to first check if the pattern will eventually bring me back to where I began
and 2 to print the result of each loop to a text file.
so using a 3 item list as my example
[a,b,c]
and the shuffle pattern [3 1 2]
where the 3rd item becomes the first.
the first item becomes the second
and the second item becomes the 3rd
on a loop would generate the following patterns
[a,b,c]
[3,1,2]
[c,a,b]
[b,c,a]
[a,b,c]
but I have a list at the moment of 100 items that I need to find every single arrangement for a few different patterns I would like to test out.
does anyone know of a way to do this in python please.
You can define function and call this function multi times like below:
>>> def func(lst, ptr):
... return [lst[idx-1] for idx in ptr]
>>> lst = ['a','b','c']
>>> ptr = [3,1,2]
>>> for _ in range(5):
... lst = func(lst, ptr)
... print(lst)
['c', 'a', 'b']
['b', 'c', 'a']
['a', 'b', 'c']
['c', 'a', 'b']
['b', 'c', 'a']
You could use numpy advanced integer indexing if your list contains a numeric type:
import numpy as np
original_list=[1,2,3]
numpy_array = np.array(original_list)
pattern = [2,1,0]
print(numpy_array[pattern])
>>> array([3, 2, 1])
def rearrange(pattern : list,L:list):
new_list = []
for i in pattern :
new_list.append(L[i-1])
return new_list
print(rearrange([3,1,2],['a','b','c']))
output :
['c', 'a', 'b']
Itertools could be what you need.
import itertools
p = itertools.permutations(['a','b','c', 'd'])
list(p)
Output:
[('a', 'b', 'c', 'd'),
('a', 'b', 'd', 'c'),
('a', 'c', 'b', 'd'),
('a', 'c', 'd', 'b'),
('a', 'd', 'b', 'c'),
('a', 'd', 'c', 'b'),
('b', 'a', 'c', 'd'),
('b', 'a', 'd', 'c'),
('b', 'c', 'a', 'd'),
('b', 'c', 'd', 'a'),
('b', 'd', 'a', 'c'),
('b', 'd', 'c', 'a'),
('c', 'a', 'b', 'd'),
('c', 'a', 'd', 'b'),
('c', 'b', 'a', 'd'),
('c', 'b', 'd', 'a'),
('c', 'd', 'a', 'b'),
('c', 'd', 'b', 'a'),
('d', 'a', 'b', 'c'),
('d', 'a', 'c', 'b'),
('d', 'b', 'a', 'c'),
('d', 'b', 'c', 'a'),
('d', 'c', 'a', 'b'),
('d', 'c', 'b', 'a')]
I have a requirement where say i have a list
lis = ['a','b','c','d','e','f']
I have to now create a combination of them eg:
l1 = [a],['b,c,d,e,f]
l2: [b],[a,c,d,e,f]
.
.
l10 [a,b,c],[d,e,f]
.
l11 [a,b,c,d] [e,f]
The repeated elements on the left and right nodes will be removed:
eg: i don't need two lists as:
l1: [b,c] , [a,d,e,f]
l2: [a,d,e,f], [b,c]
Since they are the same
The pseudo code i have in mind is:
for length = 1, i will take one element from list and club others
similar for length=2, will take two element and club others
till length=len(list)-1, will do the clubbing
and then later remove the duplicates.
Any better solution i could try?
This may no be optimal, but is very straightforward:
from itertools import chain, combinations
def power_set(iterable):
"""power_set([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"""
source = list(iterable)
return chain.from_iterable(combinations(source, r) for r in range(1, len(source) // 2 + 1))
def complement(source, universe):
return tuple(set(universe) - set(source))
lst = ['a', 'b', 'c', 'd', 'e', 'f']
result = set(frozenset({si, complement(si, lst)}) for si in power_set(lst))
for s, c in result:
print(s, c)
Output
('a', 'd', 'e') ('f', 'c', 'b')
('f', 'a', 'c', 'b') ('d', 'e')
('b', 'e') ('d', 'f', 'a', 'c')
('a', 'b', 'f') ('d', 'e', 'c')
('e', 'd', 'a', 'f', 'b') ('c',)
('c', 'f') ('d', 'a', 'e', 'b')
('d', 'f') ('a', 'e', 'b', 'c')
('d',) ('e', 'c', 'a', 'f', 'b')
('f', 'a', 'e', 'c') ('b', 'd')
('e', 'c', 'd', 'a', 'b') ('f',)
('b', 'c', 'd') ('f', 'a', 'e')
('a', 'b', 'e') ('d', 'f', 'c')
('b', 'c') ('d', 'f', 'a', 'e')
('f', 'a', 'b') ('c', 'd', 'e')
('d', 'e', 'b', 'c') ('a', 'f')
('c', 'd', 'f') ('a', 'e', 'b')
('e', 'c', 'd', 'f', 'b') ('a',)
('a', 'c') ('d', 'f', 'e', 'b')
('f', 'e', 'c') ('a', 'b', 'd')
('a', 'd') ('f', 'e', 'b', 'c')
('b', 'c', 'e') ('d', 'f', 'a')
('a', 'c', 'e') ('d', 'f', 'b')
('d', 'e', 'f') ('a', 'c', 'b')
('a', 'c', 'd') ('f', 'e', 'b')
('d', 'f', 'e', 'c') ('a', 'b')
('f', 'a', 'e', 'b') ('c', 'd')
('d', 'a', 'c') ('b', 'e', 'f')
('a', 'e') ('d', 'f', 'c', 'b')
('a', 'b', 'c') ('d', 'f', 'e')
('a', 'd', 'f') ('e', 'b', 'c')
('d', 'e', 'b') ('a', 'c', 'f')
('c', 'd', 'a', 'f', 'b') ('e',)
('b',) ('e', 'c', 'd', 'a', 'f')
('e', 'f') ('d', 'a', 'c', 'b')
('d', 'c', 'b') ('a', 'e', 'f')
('b', 'f') ('d', 'a', 'e', 'c')
('d', 'a', 'e') ('b', 'c', 'f')
('b', 'd', 'e') ('f', 'a', 'c')
('a', 'e', 'c') ('b', 'd', 'f')
('c', 'e') ('d', 'f', 'a', 'b')
('d', 'a', 'b') ('c', 'e', 'f')
Given a list of tuples as following:
values = [
('a', 'b', 'c'),
('d', 'e'),
('f', 'g', 'h')
]
I'd like to calculate different combinations of those values, but not as a cartesian product, rather as a sum on some custom rules. To clarify, if we calculate the cartesian product between those tuples, we will get 3*2*3 = 18 different combinations. But my desire is to get something like this:
combinations = [
('a', 'd', 'f'),
('a', 'e', 'g'),
('a', 'e', 'h'),
('b', 'd', 'f'),
('b', 'e', 'g'),
('b', 'e', 'h'),
('c', 'd', 'f'),
('c', 'e', 'g'),
('c', 'e', 'h')
]
So the resulting list contains 9 different combinations instead of 18.
Example with 4 tuples:
values = [
('a', 'b', 'c'),
('d', 'e'),
('f', 'g', 'h'),
('i', 'j', 'k', 'l')
]
The result would be
combinations = [
('a', 'd', 'f', 'i'),
('a', 'e', 'g', 'j'),
('a', 'e', 'h', 'k'),
('a', 'e', 'h', 'l'),
('b', 'd', 'f', 'i'),
('b', 'e', 'g', 'j'),
('b', 'e', 'h', 'k'),
('b', 'e', 'h', 'l'),
('c', 'd', 'f', 'i'),
('c', 'e', 'g', 'j'),
('c', 'e', 'h', 'k'),
('c', 'e', 'h', 'l'),
]
To Explain the logic for the outputs further:
In both inputs, the first tuple is behaving as it would in a cartesian product.
However, all the other tuples except the first are being iterated (or zipped) together. Additionally, if one of the tuples being iterated together "runs out of values" so to speak, we use the last value in the tuple instead.
What would be the efficient way to achieve this?
With the extra example provided, we can figure out how the logic will look. Essentially, the first row is being treated specially and used in the normal "cartesian product" sense.
However, the rest of the rows are being effectively extended to the largest length, and being zipped together. Coding that up, it can look something like follows:
from itertools import product
def extend_to_max_len(tup, length):
'''extends a tuple to a specified length by
filling the empty spaces with last element of given tuple
'''
fill_count = length - len(tup)
return (*tup, *[tup[-1]]*fill_count)
def non_cartesian_sum(values):
'''Expects a list of tuples.
gives the output according to the custom rules:
top: first row: to be used for cartesian product with zip of remaining rows
bottom: remaining rows: extended to longest length before zipping
'''
if len(values) < 2:
print("Check length of input provided")
return None
top = values[0]
bottom = values[1:]
max_len = max(len(row) for row in bottom)
bottom = [extend_to_max_len(row, max_len) for row in bottom]
out = [(first, *rest) for first, rest in product(top, zip(*bottom))]
return out
values = [
('a', 'b', 'c'),
('d', 'e'),
('f', 'g', 'h'),
('i', 'j', 'k', 'l')
]
out = non_cartesian_sum(values)
print(out)
Output:
[('a', 'd', 'f', 'i'),
('a', 'e', 'g', 'j'),
('a', 'e', 'h', 'k'),
('a', 'e', 'h', 'l'),
('b', 'd', 'f', 'i'),
('b', 'e', 'g', 'j'),
('b', 'e', 'h', 'k'),
('b', 'e', 'h', 'l'),
('c', 'd', 'f', 'i'),
('c', 'e', 'g', 'j'),
('c', 'e', 'h', 'k'),
('c', 'e', 'h', 'l')]
Note that you may want to add more input validation as required, before using this function for your use case.
This works for the data provided.
values = [
('a', 'b', 'c'),
('d', 'e'),
('f', 'g', 'h')
]
length_of_1 = len(values[1])
length_of_2 = len(values[2])
output = []
for item0 in values[0]:
for i in range(max(length_of_1, length_of_2)):
if i >= length_of_1:
item1 = values[1][-1]
else:
item1 = values[1][i]
if i >= length_of_2:
item2 = values[2][-1]
else:
item2 = values[2][i]
triple = (item0, item1, item2)
output.append(triple)
for tup in output:
print(tup)
Output:
('a', 'd', 'f')
('a', 'e', 'g')
('a', 'e', 'h')
('b', 'd', 'f')
('b', 'e', 'g')
('b', 'e', 'h')
('c', 'd', 'f')
('c', 'e', 'g')
('c', 'e', 'h')
Try this
values = [
('a', 'b', 'c'),
('d', 'e'),
('f', 'g', 'h')
]
combination = [(a,b,c) for a in values[0] for b in values[1] for c in values[2]]
print(combination)
This question already has answers here:
How to get the cartesian product of multiple lists
(17 answers)
Closed 6 years ago.
I have about 12 lists [a, b, c, ... , z] with arbitrary elements and i´ve got a series of combinations through itertools.combinations(iterable, n) resulting in lists of combinations that match each of the original list.
The great deal now is to get a list with all the possible combinations, picking one element(combination) of each combinations list.
One simplified exemple would be:
A = [a,b,c]
B = [d,e,f]
C = [g,h,i]
my_iterable = [A, B, C]
And the output should be:
>>> foo(my_iterable)
(a,d,g), (a,d,h), (a,d,i), (a,e,g), (a,e,h), ... , (c,f,i)
The input iterables, e.g. 'A, B & C', may have variable lengths and foo() may be a generator function.
A = ['a','b','c']
B = ['d','e','f']
C = ['g','h','i']
l = [(a, b, c) for a in A for b in B for c in C]
print(l)
out:
[('a', 'd', 'g'), ('a', 'd', 'h'), ('a', 'd', 'i'), ('a', 'e', 'g'), ('a', 'e', 'h'), ('a', 'e', 'i'), ('a', 'f', 'g'), ('a', 'f', 'h'), ('a', 'f', 'i'), ('b', 'd', 'g'), ('b', 'd', 'h'), ('b', 'd', 'i'), ('b', 'e', 'g'), ('b', 'e', 'h'), ('b', 'e', 'i'), ('b', 'f', 'g'), ('b', 'f', 'h'), ('b', 'f', 'i'), ('c', 'd', 'g'), ('c', 'd', 'h'), ('c', 'd', 'i'), ('c', 'e', 'g'), ('c', 'e', 'h'), ('c', 'e', 'i'), ('c', 'f', 'g'), ('c', 'f', 'h'), ('c', 'f', 'i')]
I'm making a specialized utility similar to John the Ripper, and I'd like to use a loop that returns all strings up to x characters that can be formed from the string. For example, if the "seed" string is abcd, it should return:
a
b
c
d
aa
ab
ac
and so on. If the character limit is 10, it would generate aaaaaaaaaa, abcddcbaaa, and so on. Is there a simple for loop to do this, or is it more complicated than that?
I'll self-plagiarize from this answer and add a maximum length:
from itertools import product
def multiletters(seq, max_length):
for n in range(1, max_length+1):
for s in product(seq, repeat=n):
yield ''.join(s)
which gives
>>> list(multiletters("abc", 2))
['a', 'b', 'c', 'aa', 'ab', 'ac', 'ba', 'bb', 'bc', 'ca', 'cb', 'cc']
>>> list(multiletters("abcd", 4))[:8]
['a', 'b', 'c', 'd', 'aa', 'ab', 'ac', 'ad']
and so on.
def all_strings(alphabet, length_limit=None):
n_letters = len(alphabet)
length = 0
n_strings = 1
buf = []
while True:
for i in xrange(0, n_strings):
k = i
for j in xrange(length - 1, -1, -1):
buf[j] = alphabet[k % n_letters]
k /= n_letters
yield ''.join(buf)
length += 1
if length == length_limit:
break
n_strings *= n_letters
buf.append(alphabet[0])
for s in all_strings('abcd', length_limit=4):
print s
As pointed out in the comment's use itertools.premutations or even better take a look #DSM's answer, as this one misses the doubles:
In [194]: from itertools import chain, permutations
In [195]: s = 'abcd'
In [196]: map(''.join,chain.from_iterable(permutations(s,x)
for x in range(1,len(s)+1)))
Out[196]:
['a',
'b',
'c',
'd',
'ab',
'ac',
'ad',
'ba',
'bc',
'bd',
...
'dbca',
'dcab',
'dcba']
Anyway, here's a version of #DSM's answer that returns a list:
from itertools import product
def ms(seq, max_length):
return [''.join(s) for n in range(1, max_length+1)
for s in product(seq,repeat=n)]
Use itertools.permuataions.
for i in range(2,4):
tuples = itertools.permutations('abca' , i)
print( list(tuples))
The example code sequence generates:
[('a', 'b'), ('a', 'c'), ('a', 'a'), ('b', 'a'), ('b', 'c'), ('b', 'a'), ('c', 'a'), ('c', 'b'), ('c', 'a'), ('a', 'a'), ('a', 'b'), ('a', 'c')]
[('a', 'b', 'c'), ('a', 'b', 'a'), ('a', 'c', 'b'), ('a', 'c', 'a'), ('a', 'a', 'b'), ('a', 'a', 'c'), ('b', 'a', 'c'), ('b', 'a', 'a'), ('b', 'c', 'a'), ('b', 'c', 'a'), ('b', 'a', 'a'), ('b', 'a', 'c'), ('c', 'a', 'b'), ('c', 'a', 'a'), ('c', 'b', 'a'), ('c', 'b', 'a'), ('c', 'a', 'a'), ('c', 'a', 'b'), ('a', 'a', 'b'), ('a', 'a', 'c'), ('a', 'b', 'a'), ('a', 'b', 'c'), ('a', 'c', 'a'), ('a', 'c', 'b')]