Why reading/unpacking data from itertools.permutation changed its attribute/content? - python

I am trying to use the permutation feature from itertools, then I noticed. If I try to unpack/read the data from permutation, it changes some attribute info
from itertools import permutations
a = permutations('abc')
print(('a', 'b', 'c') in a)
for x in a:
print(x)
print(('a', 'b', 'c') in a)
for x in a:
print(x)
Output:
True
('a', 'c', 'b')
('b', 'a', 'c')
('b', 'c', 'a')
('c', 'a', 'b')
('c', 'b', 'a')
False
How come does this happen? I checked out the official page, and cannot find any clue.
My environment is pycharm with python 3.7.4

As others aready said, the problem is that a is not a list, but a generator; that is, a sequence that gets used up as you iterate over it -- hence you can only iterate over it once.
If you look carefully, you'll see that your first print loop only printed five of the six permutations; the first permutation disappeared when you checked it against ('a', 'b', 'c') in your first print statement. The for-loop then prints out what's left, and the rest of your code is trying to drink from an empty cup.
To get the behavior you expect, make a into a list like this:
a = list(permutations('abc'))
And when you get a chance, read up on generators, iterators, and "comprehensions"; they're everywhere in Python (often hidden in plain sight), and they're great.

Get rid from generator and convert the output to list since the comparison is vanishing because it used already. itertools.permutation is just an iterator which is shifting to next when you use one value in comparison.
CODE:
from itertools import permutations
a = list(permutations('abc'))
print(('a', 'b', 'c') in a)
for x in a:
print(x)
print(('a', 'b', 'c') in a)
for x in a:
print(x)
OUTPUT:
True
('a', 'b', 'c')
('a', 'c', 'b')
('b', 'a', 'c')
('b', 'c', 'a')
('c', 'a', 'b')
('c', 'b', 'a')
True
('a', 'b', 'c')
('a', 'c', 'b')
('b', 'a', 'c')
('b', 'c', 'a')
('c', 'a', 'b')
('c', 'b', 'a')

Related

Is there a more elegant way of getting permutations with replacement in python?

I currently want all permutations of a set of elements with replacement.
Example:
elements = ['a', 'b']
permutations with replacement =
[('a', 'a', 'a'),
('a', 'a', 'b'),
('a', 'b', 'a'),
('a', 'b', 'b'),
('b', 'a', 'a'),
('b', 'a', 'b'),
('b', 'b', 'a'),
('b', 'b', 'b')]
The only way I have been able to do this is so far is with itertools.product as follows:
import itertools as it
sample_space = ['a', 'b']
outcomes = it.product(sample_space, sample_space, sample_space)
list(outcomes)
I am just wondering if there is a better way to do this as it obvious that this can get unwieldy and error prone as the sample space and required length gets larger
was expecting to find something along the lines of itertools.permutations(['a', 'b'], length=3, replace=True) maybe?
I tried itertools.permutations but the only arguments are iterable, and r which is the length required.
The output for the above example using it.permutations(sample_space, 3) would be an empty list []
If you're sampling with replacement, what you get is by definition not a permutation (which just means "rearrangement") of the set. So I wouldn't look to the permutations function for this. product is the right thing; e.g. itertools.product(['a','b'], repeat=3).
Note that if you sample from a two-element set with replacement N times, then you have effectively created an N-digit binary number, with all the possibilities that go with it. You've just swapped in 'a' and 'b' for 0 and 1.

Generate iterator object with tuples of varying size

I am trying to create a branch and bound algorithm, to do this I would like to create an iterator object which stores all possible combinations of a list of items of size 0 to n.
Take the following example to demonstrate:
import itertools as it
list_tmp = ['a', 'b', 'c', 'd']
tmp_it = sum([list(map(list, it.combinations(list_tmp, i))) for i in range(2 + 1)], [])
tmp_it is a list of all possible combinations of size 0 to 2. This code works perfectly for small list sizes, but I need to act on a larger list and so would like to preserve
the iterator characteristics of the it.combinations object (generate the combinations on the fly). e.g.
for iteration in it.combinations(list_tmp, 2):
print(iteration)
Is there any method of doing this for combinations of multiple sizes? Rather than converting to a list and losing the characteristics of the iterator object.
You can do this using itertools.chain.from_iterable, which lazily evaluates its argument. Something like this:
tmp_it = it.chain.from_iterable(it.combinations(list_tmp, i) for i in range(2+1)))
You can chain iterators:
>>> sizes = it.chain.from_iterable(it.combinations(list_tmp, i) for i in range(len(list_tmp)))
>>> for i in sizes:
... print(i)
...
()
('a',)
('b',)
('c',)
('d',)
('a', 'b')
('a', 'c')
('a', 'd')
('b', 'c')
('b', 'd')
('c', 'd')
('a', 'b', 'c')
('a', 'b', 'd')
('a', 'c', 'd')
('b', 'c', 'd')

how to find words out of given alphabets in ascending order

I have a sequence of
words=[a,b,c,d]
And I want to find words that can be made out of them in ascending order.
the result list has
[a,ab,abc,abcd,b,bc,bcd,c,cd,d]
how to do it.
I have the code but it has C and python mixed, can someone help me with its python equivalent.
here it goes:
word_list=input("Enter the word")
n=len(word_list)
newlist=[]
for(i=0;i<n;i++)
{
c=''
for(j=i;j<n;j++)
{
c.join(j)
newlist=append(c)
}
}
letters = input("Enter the word")
n = len(letters)
words = [letters[start:end+1] for start in range(n) for end in range(start, n)]
You can do it easily with itertools.combinations
Itertools has some great functions for this kind of thing. itertools.combinations does exactly what you want.
The syntax is:
itertools.combinations(iterable [, length] )
so you can enter your list of words directly as it is an iterable. As you want all the different lengths, you will have to do it in a for-loop to get a list of combinations for all lengths.
So if your words are:
words = ['a', 'b', 'c', 'd']
and you do:
import itertools
itertools.combinations(words, 2)
you will get back an itertools object which you can easily convert to a list with list():
list(itertools.combinations(words, 2))
which will return:
[('a', 'b'), ('a', 'c'), ('a', 'd'), ('b', 'c'), ('b', 'd'), ('c', 'd')]
However, if you want a list of all lengths (i.e. including just 'a' and 'abc') then you can just extend the results of each individual list of each list onto another list of all lengths. So something like:
import itertools
words = ['a', 'b', 'c', 'd']
combinations = []
for l in range(1, len(words) + 1):
combinations.extend(list(itertools.combinations(words, l )))
and this will give you the result of:
[('a'), ('b'), ('c'), ('d'), ('a', 'b'), ('a', 'c'), ('a', 'd'), ('b', 'c'), ('b, 'd'), ('c', 'd'), ('a', 'b', 'c'), ('a', 'b', 'd'), ('a', 'c', 'd'), ('b', 'c', 'd), ('a', 'b', 'c', 'd')]
and if you want these to be more readable (as strings rather than tuples), you can use a list comprehension...
combinations = [''.join(c) for c in combinations]
so now combinations is simply an array of the strings:
['a', 'b', 'c', 'd', 'ab', 'ac', 'ad', 'bc', 'bd', 'cd', 'abc', 'abd', 'acd', 'bcd', 'abcd']
you can use itertools :
>>> import itertools
>>> w=['a','b','c','d']
>>> result=[]
>>> for L in range(1, len(w)+1):
... for subset in itertools.combinations(w, L):
... result.append(''.join(subset))
...
>>> result
['a', 'b', 'c', 'd', 'ab', 'ac', 'ad', 'bc', 'bd', 'cd', 'abc', 'abd', 'acd', 'bcd', 'abcd']

Checking if a set of tuple contains items from another set

Let's say I have a set of tuples like this:
foo = {('A', 'B'), ('C', 'D'), ('B', 'C'), ('A', 'C')}
var = {'A', 'C', 'B'}
I want to check if every item from var is in any place in the set of tuples and returning True if it is and False if it isn't.
I tried with this but I don't have luck so far.
all((x for x in var) in (a,b) for (a,b) in foo)
Desired output : True
Actual output : False
However if:
var = {'A','C','D'}
I want it to return False, the logic is checking if the strings 'know' eachother.
Alright, let's explain this, for my last var.
A is paired with C, C is paired D, however D is not paired with A.
For my first logic,
A is paired with B,B is paired with C,C is paired with B, C is paired with A, Everyone 'knows' each other.
.
Generate all the pairs you expect to be present and see if they're there with a subset check:
from itertools import combinations
def _norm(it):
return {tuple(sorted(t)) for t in it}
def set_contains(foo, var):
return _norm(combinations(var, 2)) <= _norm(foo)
print(set_contains({('A', 'B'), ('C', 'D'), ('B', 'C'), ('A', 'C')},
{'A', 'C', 'B'})) # True
print(set_contains({('A', 'B'), ('C', 'D'), ('B', 'C'), ('A', 'C')},
{'A', 'C', 'D'})) # False
It may be possible to reduce on the amount of sorting, depending on how exactly combinations works (I'm not 100% sure what to make of the docs) and if you reuse either foo or var several times and can thus sort one of the parts just once beforehand.
Try this:
foo = {('A', 'B'), ('C', 'D'), ('B', 'C'), ('A', 'C')}
var = {'A', 'C', 'B'}
for elem in var:
if any(elem in tuples for tuples in foo):
print(True)
This is not as 'compact' as the others but works the same.
for x in var:
for y in foo:
if x in y:
print('Found %s in %s' % (x, y))
else:
print('%s not in %s' % (x, y))
B not in ('C', 'D')
B not in ('A', 'C')
Found B in ('A', 'B')
Found B in ('B', 'C')
A not in ('C', 'D')
Found A in ('A', 'C')
Found A in ('A', 'B')
A not in ('B', 'C')
Found C in ('C', 'D')
Found C in ('A', 'C')
C not in ('A', 'B')
Found C in ('B', 'C')

Subsets of a tuple with a fixed length

I wish to have a function, subset(("A","b","C","D"),3), which gives the following output:
("A","b","C")
("A","b","D")
("A","C","D")
("b","C","D")
How might I do this in python 3?
The itertools.combinations function was built explicitly for this purpose:
>>> from itertools import combinations
>>> list(combinations(("A","b","C","D"), 3))
[('A', 'b', 'C'), ('A', 'b', 'D'), ('A', 'C', 'D'), ('b', 'C', 'D')]
>>>
From the docs:
itertools.combinations(iterable, r)
Return r length subsequences of elements from the input iterable.

Categories

Resources