How to exclude some results of itertools permution by condition? - python

I'm trying to solve the problem from the Rosalind.
Return: The total number of signed permutations of length n, followed by a list of
all such permutations (you may list the signed permutations in any order).
I have an idea for a solution in Python, but I cannot implement it to the end. Consider for example that n = 2.
numbers = [1, -1, 2, -2]
ordering = permutations(numbers,n)
So now I've got some tuples as a result:
(1, -1) (1, 2) (1, -2) (-1, 1) (-1, 2) (-1, -2) (2, 1) (2, -1) (2, -2) (-2, 1)
(-2, -1) (-2, 2)
I need to exclude those that have elements of equal modulus. For example, (-1, 1). Is it possible to implement this, and if possible, how?

A pythonic solution using list comprehension:
filtered_perms = [(x,y) for x,y in ordering if abs(x) != abs(y)]
Edit:
Code that works fine with python 3.7:
import itertools as itt
# create permutation generator object
perms = itt.permutations([-2, -1, 1, 2], 2)
# here generator is turned into a list with certain permutations excluded
filtered_perms = [(x,y) for x,y in perms if abs(x) != abs(y)]
# print whole list
print(filtered_perms)
# print first permutation
print(filtered_perms[0])
# print length of the list
print(len(filtered_perms))
Edit2:
To fix the problem with no elements in ordering:
ordering = list(itertools.permutations([-2, -1, 1, 2],2))
after that, ordering will be a list of all elements from itertools.permutations.

The solutions proposed before are right, but if in the case that you want to process the resulting list after generating the permutations would be a nice idea to have a generator instead of a list. For that, I recommend you to design your own generator function based on itertools.permutations:
def signed_permutations(iterable, r=2):
for item in permutations(iterable, r):
if abs(item[0]) != abs(item[1]):
yield item
And you can use it as:
for item in signed_permutations(numbers):
do_something(item)
Or if you just want to create a list:
sigperm = list(signed_permutations(numbers))

The filter function is probably what you're looking for.
list(filter(lambda pair: abs(pair[0]) != abs(pair[1]), ordering))
The condition might be wrong, I'm not sure what you mean by equal modulus.

Related

Best way to compare every index of one list to every other index of that same list

This is just a general algorithmic question, but I was wondering if there is a more efficient/faster way of comparing every element of a list to every other element of a list without duplicate checking.
I have an implementation which is straightforward
for item in list:
for comparator in list:
if comparator == item:
pass
else:
# do something...
But the problem with this is that there's going to be a duplicate check & doesn't scale well. Is there an methodology that can do this more efficiently or more quickly?
We can do better than just double loop and avoid comparing with the same index. We can also eliminate 'mirrored checks' (e.g. if we check a==b we don't need to check b==a). This is what combinations from itertools produces. So each time we add another item to the input we get another n-1 possible comparisons.
The example below generates the combinations of length 2 and creates tuples of the two numbers and the result of an equality check.
>>> from itertools import combinations
>>> [(a, b, a == b) for a, b in combinations([1,2,3,1,2], 2)]
[(1, 2, False), (1, 3, False), (1, 1, True), (1, 2, False), (2, 3, False), (2, 1, False), (2, 2, True), (3, 1, False), (3, 2, False), (1, 2, False)]
To avoid comparing duplicates, use enumerate() and skip the case where the indexes are the same.
for i, item in enumerate(list):
for j, comparator in enumerate(list):
if i == j:
continue
if comparator != item:
# do something
There's no way to avoid the scaling problem -- you have to loop over every pair of elements to achieve your goal. That's inherently O(n^2).
for i in list1:
if i in list2:
continue
else:
# do something
not sure if this is what you need.
Parallel computing using multiprocessing/threading might make it faster.
from multiprocessing.dummy import Pool as ThreadPool
def action(enum_elem):
index, item = enum_elem
for index2, comparator in enumerate(list1):
if index == index2:
continue
if comparator != item:
# do something
pool = ThreadPool(5)
pool.map(action, list(enumerate(list1)))

Make simple graph with Tkinter [duplicate]

I would like to loop through a list checking each item against the one following it.
Is there a way I can loop through all but the last item using for x in y? I would prefer to do it without using indexes if I can.
Note
freespace answered my actual question, which is why I accepted the answer, but SilentGhost answered the question I should have asked.
Apologies for the confusion.
for x in y[:-1]
If y is a generator, then the above will not work.
the easiest way to compare the sequence item with the following:
for i, j in zip(a, a[1:]):
# compare i (the current) to j (the following)
If you want to get all the elements in the sequence pair wise, use this approach (the pairwise function is from the examples in the itertools module).
from itertools import tee, izip, chain
def pairwise(seq):
a,b = tee(seq)
b.next()
return izip(a,b)
for current_item, next_item in pairwise(y):
if compare(current_item, next_item):
# do what you have to do
If you need to compare the last value to some special value, chain that value to the end
for current, next_item in pairwise(chain(y, [None])):
if you meant comparing nth item with n+1 th item in the list you could also do with
>>> for i in range(len(list[:-1])):
... print list[i]>list[i+1]
note there is no hard coding going on there. This should be ok unless you feel otherwise.
To compare each item with the next one in an iterator without instantiating a list:
import itertools
it = (x for x in range(10))
data1, data2 = itertools.tee(it)
data2.next()
for a, b in itertools.izip(data1, data2):
print a, b
This answers what the OP should have asked, i.e. traverse a list comparing consecutive elements (excellent SilentGhost answer), yet generalized for any group (n-gram): 2, 3, ... n:
zip(*(l[start:] for start in range(0, n)))
Examples:
l = range(0, 4) # [0, 1, 2, 3]
list(zip(*(l[start:] for start in range(0, 2)))) # == [(0, 1), (1, 2), (2, 3)]
list(zip(*(l[start:] for start in range(0, 3)))) # == [(0, 1, 2), (1, 2, 3)]
list(zip(*(l[start:] for start in range(0, 4)))) # == [(0, 1, 2, 3)]
list(zip(*(l[start:] for start in range(0, 5)))) # == []
Explanations:
l[start:] generates a a list/generator starting from index start
*list or *generator: passes all elements to the enclosing function zip as if it was written zip(elem1, elem2, ...)
Note:
AFAIK, this code is as lazy as it can be. Not tested.

Two nested for loops

I dont know how to properly ask this question but here is what I am trying to do.
lists = []
for x in range(3):
for y in range(3):
if x!=y:
lists.append([x,y])
Is there a simple solution so it doesnt give me lists that are the same but reversed:
for example [2,0] and [0,2]?
I know I could go through the lists and remove them afterwards but is there a solution to not even make the list? (sorry my english isnt perfect)
You can use itertools.combinations
>>> from itertools import combinations
>>> list(combinations(range(3), 2))
[(0, 1), (0, 2), (1, 2)]
With the above example we take any combination of two elements from range(3) without repeating any elements.
Sure: if you add all pairs with y > x instead of all possible pairs, only one of each pair (x, y) and (y, x) will appear.
lists = []
for x in range(3):
for y in range(x + 1, 3):
lists.append([x,y])
If you don't want those "duplicates", you want a combination
a combination is a way of selecting items from a collection, such that (unlike permutations) the order of selection does not matter
>>> import itertools
>>> list(itertools.combinations(iterable=range(3), r=2))
[(0, 1), (0, 2), (1, 2)]
Above I have used combinations() from the Python module itertools.
Explanation
I've set r=2 because you want a subsequence length of 2 (the form you described as [x, y])
The iterable=range(3) parameter is just a list of elements that are going to be used to make combinations of, so range(3) would result in [0, 1, 2]
The list() applied to the end result is simply to force the output to be printed out to the console because otherwise itertools.combinations returns an iterable that you iterate through to pull the elements one by one.
Easy:
for x in range(3):
for y in range(x, 3):
lists.append([x,y])

Create a list with all possible permutations from a know list of objects, but make the final list x in size

Lets say I have the following list:
lst = [0,1,3,a,b,c]
I would like the final outcome to be all possible permutations of lst, but be 20 characters long.
I have looked and can only find examples that would create a final outcome that would be 6 or less in length.
Any ideas?
I think itertools.product is what you're looking for.
# A simple example
import itertools
lst = [0, 1]
print(list(itertools.product(lst, repeat=2)))
# [(0, 0), (0, 1), (1, 0), (1, 1)]
Note that itertools.product itself returns an itertools.product object, not a list.
# In your case
import itertools
lst = [0, 1, 3, a, b, c]
output = list(itertools.product(lst, repeat=20))
Are you sure you want all permutations? Assuming you want to reuse characters (not a permutation) that would be a list with a length of 6^20.
If you want one string of length 20 built from characters in that list, this should do the job:
from random import choice
''.join(choice(chars) for _ in range(length))
The problem is that permutations and combinations (if you use itertools) follow the definition that each result can only physically exist if it is of equal or lesser length than the originating list.
If you are suggesting that you want one of the valid results to be "013abc013abc013abc01", then you'll have to modify your list to just be 20 items long and be made up of those 6 values.
from itertools import permutations
i = [0,1,3,'a','b','c',0,1,3,'a','b','c',0,1,3,'a','b','c',0,1]
results = []
for a in permutations(i, 20):
results.append(a)
list(permutations(lst,x)) ; where lst is iterable (your input list) and x is the no of elements
for example:
In [8]: lst = [0, 1, 3, 'a', 'b', 'c']
In [9]: from itertools import permutations
In [10]: result=[list(permutations(lst,x)) for x in range(1,len(lst))]

clean way to accomplish -- if x in [(0, 1, 2), (2, 0, 1), (1, 2, 0)]:?

If not, then a canonical name for a function? 'Cycle' makes sense to me, but that's taken.
The example in the header is written for clarity and brevity. Real cases I'm working with have a lot of repetition.
(e.g., I want [1, 1, 0, 0, 0, 2, 1] to "match" [0, 0, 2, 1, 1, 1, 0])
This type of thing is obscuring my algorithm and filling my code with otherwise useless repetition.
You can get the cycles of the list with:
def cycles(a):
return [ a[i:] + a[:i] for i in range(len(a)) ]
You can then check if b is a cycle of a with:
b in cycles(a)
If the length of the list is long, or if want to make multiple comparison to the same cycles, it may be beneficial (performance wise) to embed the results in a set.
set_cycles = set(cycles(a))
b in set_cycles
You can prevent necessarily constructing all the cycles by embedding the equality check in the list and using any:
any( b == a[i:]+a[:i] for i in range(len(a)))
You could also achieve this effect by turning the cycles function into a generator.
Misunderstood your question earlier. If you want to check if any cycle of a list l1 matches a list l2 the best (cleanest/most pythonic) method is probably any(l1 == l2[i:] + l2[:i] for i in xrange(len(l2))). There is also a rotate method in collections.deque that you might find useful.
You could use cycle from itertools, together with islice to cut it up. This basically puts this answer in a list comprehension so the list is shifted once for every element.
>>> from itertools import islice, cycle
>>> l = [0,1,2]
>>> [tuple(islice(cycle(t),i,i+len(t))) for i,_ in enumerate(l)]
[(0, 1, 2), (1, 2, 0), (2, 0, 1)]

Categories