I want to generate permutations of elements in a list, but only keep a set where each element is on each position only once.
For example [1, 2, 3, 4, 5, 6] could be a user list and I want 3 permutations. A good set would be:
[1,2,3,5,4,6]
[2,1,4,6,5,3]
[3,4,5,1,6,2]
However, one could not add, for example, [1,3,2,6,5,4] to the above, as there are two permutations in which 1 is on the first position twice, also 5 would be on the 5th position twice, however other elements are only present on those positions once.
My code so far is :
# this simply generates a number of permutations specified by number_of_samples
def generate_perms(player_list, number_of_samples):
myset = set()
while len(myset) < number_of_samples:
random.shuffle(player_list)
myset.add(tuple(player_list))
return [list(x) for x in myset]
# And this is my function that takes the stratified samples for permutations.
def generate_stratified_perms(player_list, number_of_samples):
user_idx_dict = {}
i = 0
while(i < number_of_samples):
perm = generate_perms(player_list, 1)
for elem in perm:
if not user_idx_dict[elem]:
user_idx_dict[elem] = [perm.index(elem)]
else:
user_idx_dict[elem] += [perm.index(elem)]
[...]
return total_perms
but I don't know how to finish the second function.
So in short, I want to give my function a number of permutations to generate, and the function should give me that number of permutations, in which no element appears on the same position more than the others (once, if all appear there once, twice, if all appear there twice, etc).
Let's starting by solving the case of generating n or fewer rows first. In that case, your output must be a Latin rectangle or a Latin square. These are easy to generate: start by constructing a Latin square, shuffle the rows, shuffle the columns, and then keep just the first r rows. The following always works for constructing a Latin square to start with:
1 2 3 ... n
2 3 4 ... 1
3 4 5 ... 2
... ... ...
n 1 2 3 ...
Shuffling rows is a lot easier than shuffling columns, so we'll shuffle the rows, then take the transpose, then shuffle the rows again. Here's an implementation in Python:
from random import shuffle
def latin_rectangle(n, r):
square = [
[1 + (i + j) % n for i in range(n)]
for j in range(n)
]
shuffle(square)
square = list(zip(*square)) # transpose
shuffle(square)
return square[:r]
Example:
>>> latin_rectangle(5, 4)
[(2, 4, 3, 5, 1),
(5, 2, 1, 3, 4),
(1, 3, 2, 4, 5),
(3, 5, 4, 1, 2)]
Note that this algorithm can't generate all possible Latin squares; by construction, the rows are cyclic permutations of each other, so you won't get Latin squares in other equivalence classes. I'm assuming that's OK since generating a uniform probability distribution over all possible outputs isn't one of the question requirements.
The upside is that this is guaranteed to work, and consistently in O(n^2) time, because it doesn't use rejection sampling or backtracking.
Now let's solve the case where r > n, i.e. we need more rows. Each column can't have equal frequencies for each number unless r % n == 0, but it's simple enough to guarantee that the frequencies in each column will differ by at most 1. Generate enough Latin squares, put them on top of each other, and then slice r rows from it. For additional randomness, it's safe to shuffle those r rows, but only after taking the slice.
def generate_permutations(n, r):
rows = []
while len(rows) < r:
rows.extend(latin_rectangle(n, n))
rows = rows[:r]
shuffle(rows)
return rows
Example:
>>> generate_permutations(5, 12)
[(4, 3, 5, 2, 1),
(3, 4, 1, 5, 2),
(3, 1, 2, 4, 5),
(5, 3, 4, 1, 2),
(5, 1, 3, 2, 4),
(2, 5, 1, 3, 4),
(1, 5, 2, 4, 3),
(5, 4, 1, 3, 2),
(3, 2, 4, 1, 5),
(2, 1, 3, 5, 4),
(4, 2, 3, 5, 1),
(1, 4, 5, 2, 3)]
This uses the numbers 1 to n because of the formula 1 + (i + j) % n in the first list comprehension. If you want to use something other than the numbers 1 to n, you can take it as a list (e.g. players) and change this part of the list comprehension to players[(i + j) % n], where n = len(players).
If runtime is not that important I would go for the lazy way and generate all possible permutations (itertools can do that for you) and then filter out all permutations which do not meet your requirements.
Here is one way to do it.
import itertools
def permuts (l, n):
all_permuts = list(itertools.permutations(l))
picked = []
for a in all_permuts:
valid = True
for p in picked:
for i in range(len(a)):
if a[i] == p[i]:
valid = False
break
if valid:
picked.append (a)
if len(picked) >= n:
break
print (picked)
permuts ([1,2,3,4,5,6], 3)
Related
With greate respect to the answer of #alias there: (Find minimum sum) I would like to solve similar puzzle. Having 4 agents and 4 type of works. Each agent does work on some price (see initial matrix in the code). I need find the optimal allocation of agents to the particular work. Following code almost copy paste from the mentioned answer:
initial = ( # Row - agent, Column - work
(7, 7, 3, 6),
(4, 9, 5, 4),
(5, 5, 4, 5),
(6, 4, 7, 2)
)
opt = Optimize()
agent = [Int(f"a_{i}") for i, _ in enumerate(initial)]
opt.add(And(*(a != b for a, b in itertools.combinations(agent, 2))))
for w, row in zip(agent, initial):
opt.add(Or(*[w == val for val in row]))
minTotal = Int("minTotal")
opt.add(minTotal == sum(agent))
opt.minimize(minTotal)
print(opt.check())
print(opt.model())
Mathematically correct answer: [a_2 = 4, a_1 = 5, a_3 = 2, a_0 = 3, minTotal = 14] is not working for me, because I need get index of agent instead.
Now, my question - how to rework the code to optimize by indexes instead of values? I've tried to leverage the Array but have no idea how to minimize multiple sums.
You can simply keep track of the indexes and walk each row to pick the corresponding element. Note that the itertools.combinations can be replaced by Distinct. We also add extra checks to make sure the indices are between 1 and 4 to ensure there's no out-of-bounds access:
from z3 import *
initial = ( # Row - agent, Column - work
(7, 7, 3, 6),
(4, 9, 5, 4),
(5, 5, 4, 5),
(6, 4, 7, 2)
)
opt = Optimize()
def choose(i, vs):
if vs:
return If(i == 1, vs[0], choose(i-1, vs[1:]))
else:
return 0
agent = [Int(f"a_{i}") for i, _ in enumerate(initial)]
opt.add(Distinct(*agent))
for a, row in zip(agent, initial):
opt.add(a >= 1)
opt.add(a <= 4)
opt.add(Or(*[choose(a, row) == val for val in row]))
minTotal = Int("minTotal")
opt.add(minTotal == sum(choose(a, row) for a, row in zip (agent, initial)))
opt.minimize(minTotal)
print(opt.check())
print(opt.model())
This prints:
sat
[a_1 = 1, a_0 = 3, a_2 = 2, a_3 = 4, minTotal = 14]
which I believe is what you're looking for.
Note that z3 also supports arrays, which you can use for this problem. However, in SMTLib, arrays are not "bounded" like in programming languages. They're indexed by all elements of their domain type. So, you won't get much of a benefit from doing that, and the above formulation seems to be the most straightforward.
I have some permutations of a list:
>>> import itertools
>>> perms = list(itertools.permutations([0,1,2,3]))
>>> perms
[(0, 1, 2, 3), (0, 1, 3, 2), (0, 2, 1, 3), (0, 2, 3, 1), (0, 3, 1, 2), (0, 3, 2, 1), (1, 0, 2, 3), (1, 0, 3, 2), (1, 2, 0, 3), (1, 2, 3, 0), (1, 3, 0, 2), (1, 3, 2, 0), (2, 0, 1, 3), (2, 0, 3, 1), (2, 1, 0, 3), (2, 1, 3, 0), (2, 3, 0, 1), (2, 3, 1, 0), (3, 0, 1, 2), (3, 0, 2, 1), (3, 1, 0, 2), (3, 1, 2, 0), (3, 2, 0, 1), (3, 2, 1, 0)]
>>> len(perms)
24
What function can I use (without access to the list perm) to get the index of an arbitrary permutation, e.g. (0, 2, 3, 1) -> 3?
(You can assume that permuted elements are always an ascending list of integers, starting at zero.)
Hint: The factorial number system may be involved. https://en.wikipedia.org/wiki/Factorial_number_system
Off the top of my head I came up with the following, didn't test it thoroughly.
from math import factorial
elements = list(range(4))
permutation = (3, 2, 1, 0)
index = 0
nf = factorial(len(elements))
for n in permutation:
nf //= len(elements)
index += elements.index(n) * nf
elements.remove(n)
print(index)
EDIT: replaced nf /= len(elements) with nf //= len(elements)
I suppose this is a challenge, so here is my (recursive) answer:
import math
import itertools
def get_index(l):
# In a real function, there should be more tests to validate that the input is valid, e.g. len(l)>0
# Terminal case
if len(l)==1:
return 0
# Number of possible permutations starting with l[0]
span = math.factorial(len(l)-1)
# Slightly modifying l[1:] to use the function recursively
new_l = [ val if val < l[0] else val-1 for val in l[1:] ]
# Actual solution
return get_index(new_l) + span*l[0]
get_index((0,1,2,3))
# 0
get_index((0,2,3,1))
# 3
get_index((3,2,1,0))
# 23
get_index((4,2,0,1,5,3))
# 529
list(itertools.permutations((0,1,2,3,4,5))).index((4,2,0,1,5,3))
# 529
You need to write your own function. Something like this would work
import math
def perm_loc(P):
N = len(P)
assert set(P) == set(range(N))
def rec(perm):
nums = set(perm)
if not perm:
return 0
else:
sub_res = rec(perm[1:]) # Result for tail of permutation
sub_size = math.factorial(len(nums) - 1) # How many tail permutations exist
sub_index = sorted(nums).index(perm[0]) # Location of first element in permutaiotn
# in the sorted list of number
return sub_index * sub_size + sub_res
return rec(P)
The function that does all the work is rec, with perm_loc just serving as a wrapper around it. Note that this algorithm is based on the nature of the permutation algorithm that itertools.permutation happens to use.
The following code tests the above function. First on your sample, and then on all permutations of range(7):
print perm_loc([0,2,3,1]) # Print the result from the example
import itertools
def test(N):
correct = 0
perms = list(itertools.permutations(range(N)))
for (i, p) in enumerate(perms):
pl = perm_loc(p)
if i == pl:
correct += 1
else:
print ":: Incorrect", p, perms.index(p), perm_loc(N, p)
print ":: Found %d correct results" % correct
test(7) # Test on all permutations of range(7)
from math import factorial
def perm_to_permidx(perm):
# Extract info
n = len(perm)
elements = range(n)
# "Gone"s will be the elements of the given perm
gones = []
# According to each number in perm, we add the repsective offsets
offset = 0
for i, num in enumerate(perm[:-1], start=1):
idx = num - sum(num > gone for gone in gones)
offset += idx * factorial(n - i)
gones.append(num)
return offset
the_perm = (0, 2, 3, 1)
print(perm_to_permidx(the_perm))
# 3
Explanation: All permutations of a given range can be considered as a groups of permutations. So, for example, for the permutations of 0, 1, 2, 3 we first "fix" 0 and permute rest, then fix 1 and permute rest, and so on. Once we fix a number, the rest is again permutations; so we again fix a number at a time from the remaining numbers and permute the rest. This goes on till we are left with one number only. Every level of fixing has a corresponding (n-i)! permutations.
So this code finds the "offsets" for each level of permutation. The offset corresonds to where the given permutation starts when we fix numbers of perm in order. For the given example of (0, 2, 3, 1), we first look at the first number in the given perm which is 0, and figure the offset as 0. Then this goes to gones list (we will see its usage). Then, at the next level of permutation we see 2 as the fixing number. To calculate the offset for this, we need the "order" of this 2 among the remaining three numbers. This is where gones come into play; if an already-fixed and considered number (in this case 0) is less than the current fixer, we subtract 1 to find the new order. Then offset is calculated and accumulated. For the next number 3, the new order is 3 - (1 + 1) = 1 because both previous fixers 0 and 2 are at the "left" of 3.
This goes on till the last number of the given perm since there is no need to look at it; it will have been determined anyway.
Before you suggest that this question is similar to another, read P.P.S, pls.
Simillarly to this question, I am looking to find all the non-repeating combinations of a list of factors: But beeing looking for a python solution, and also having a slighty diferent premise, I decided it's worth opening a new question.
The input to the combinations function would be of the form [2,5,3,1,5,1,11,2], a list where the odd entries are the primes and the even are the number of times they are present. This number would be (2^5)*3*5*(11^2), or 58080. My goal is to print all the combinations (in this case products) of the different factors.
My try is the following (b is the list where i have the primes, and div a blank list where I put the divisors(don't mind 1 as a divisor):
n=len(b)//2
a=1
if a<=n:
for i in range (n):
for g in range (1,b[2*i+1]+1):
div.append (b[2*i]**g)
a+=1
if a<=n:
for i in range (n):
for o in range (i+1,n):
for g in range(1,b[2*i+1]+1):
for h in range(1,b[2*o+1]+1):
div.append ((b[2*i]**g)*(b[2*o]**h))
This adds to the list all the combinations of at most two different prime factors, but there must be a way to continue this to numbers of n different prime factors without mannualy adding more code. But the most important is that it will not generate repeated products. If there is an answer out there please redirect me to it. Thanks in advance to all.
P.S. For example, take 60. 60 will be factored by a function (not showed here) into [2, 2, 3, 1, 5, 1]. My desired output is all the divisors of 60, in order or not, like this [1,2,3,4,5,6,10,12,15,30,60] All the combinations of the products of the factors. (2,2*2,3,5,2*3,2*5,2*2*3,2*2*5,3*5,2*2*3*5 (and 1, that is added to div before or after))
P.P.S. The difference to this (another) question relies on 2 things.
First, and most important, the point of this question isn't finding divisors, but combinations. Divisors is just the context for it, but I would like to know for future problems how to make such iterations. Second, like I said in the comments, even if were about divisors, finding the primes and only then combining them is more efficient (for large N) than looking for numbers until the sqrt, and the refered post revolves around that (for an example why, see in comments).
Your friend is itertools:
from itertools import product
from functools import reduce
def grow(factor, power):
#returns list [1, factor, factor^2, ..., factor^power]
array = []
for pw in range(power+1):
if pw != 0:
k *= factor
else:
k = 1
array.append(k)
return array
x = [2,2,3,1,5,1]
prime_factors = [x[i] for i in range(0, len(x), 2)]
powers = [x[i] for i in range(1, len(x), 2)]
divisor_tree = [grow(*n) for n in zip(prime_factors, powers)]
divisor_groups = product(*divisor_tree)
# returns iterator of [(1, 1, 1), (1, 1, 5), (1, 3, 1), (1, 3, 5), (2, 1, 1), (2, 1, 5), (2, 3, 1), (2, 3, 5), (4, 1, 1), (4, 1, 5), (4, 3, 1), (4, 3, 5)]
result = [reduce(lambda x,y: x*y, n) for n in divisor_groups]
print(result)
Output:
[1, 5, 3, 15, 2, 10, 6, 30, 4, 20, 12, 60]
Now I introduce what it does:
Extracts prime_factors and their powers from your list
zip(prime_factors, powers) pairs them with each other
grow returns list consecutive powers as commented
divisor_groups is iterable of all possible selections from group of these lists, each item taken from separate list
reduce(lambda x,y: x*y, n) maps selected tuple of factors to a product of these factors, e.g. (2,3,5) -> 30
Edit:
You might like to implement grow in this way, less efficient but more readable:
def grow(factor, power):
return [factor**i for i in range(power+1)]
I am trying to use an unbounded generator in itertools.permutations but it doesn't seem to be working. The return generator is never created because the function just runs forever. To understand what I mean, consider:
from itertools import count, permutations
all_permutations = permutations(count(1), 4)
How I imagine this working is that it generates all possible 4-length permutations of the first 4 natural numbers. Then it should generate all possible 4-length permutations of the first 5 natural numbers, with no repeats so 5 must be included in all of these. What happens though is that python is hung on creating all_permutations.
Before I go off and create my own function from scratch, I am wondering if there is another library that might be able to do what I'm looking for? Also, shouldn't the built-in function here be able to handle this? Is this perhaps a bug that should be worked out?
EDIT: For some iterations...
1 2 3 4
1 2 4 3
...
4 3 2 1
1 2 3 5
1 2 5 3
...
5 3 2 1
1 2 4 5
1 2 5 4
...
Nice question! Here's an efficient method that generates them systematically, without repetitions (and without any need to check):
First the permutations of the first n elements;
then the permutations involving the n+1st element and n-1 of the previous ones;
then those involving the n+2nd element and n-1 of the previous ones, etc.
In other words, the last element drawn is always included in the current batch. This only keeps around a tuple of the consumed source elements (unavoidable, since we'll keep using all of them in permutations).
As you can see, I simplified the implementation a little: Instead of step 1, I initialize the base with n-1 elements and go straight to the main loop.
from itertools import islice, permutations, combinations
def step_permutations(source, n):
"""Return a potentially infinite number of permutations, in forward order"""
isource = iter(source)
# Advance to where we have enough to get started
base = tuple(islice(isource, n-1))
# permutations involving additional elements:
# the last-selected one, plus <n-1> of the earlier ones
for x in isource:
# Choose n-1 elements plus x, form all permutations
for subset in combinations(base, n-1):
for perm in permutations(subset + (x,), n):
yield perm
# Now add it to the base of elements that can be omitted
base += (x,)
Demonstration:
>>> for p in step_permutations(itertools.count(1), 3):
print(p)
(1, 2, 3)
(1, 3, 2)
(2, 1, 3)
(2, 3, 1)
(3, 1, 2)
(3, 2, 1)
(1, 2, 4)
(1, 4, 2)
(2, 1, 4)
(2, 4, 1)
(4, 1, 2)
(4, 2, 1)
(1, 3, 4)
(1, 4, 3)
(3, 1, 4)
(3, 4, 1)
(4, 1, 3)
(4, 3, 1)
(2, 3, 4)
(2, 4, 3)
(3, 2, 4)
...
Something like this:
from itertools import count, permutations
def my_permutations(gen, n=4):
i = iter(gen)
population = []
seen = set()
while True:
for p in permutations(population, n):
if p not in seen:
yield p
seen.add(p)
population.append(next(i))
Beware, the memory usage is growing forever, but as far as I can see there is no way around that.
More efficient version:
def my_permutations(gen, n=4):
i = iter(gen)
population = []
while True:
population.append(next(i))
*first, last = population
perms = permutations(first, n-1)
yield from (p[:i] + (last,) + p[i:] for p in perms for i in range(n))
I am looking for a Pythonic method to generate all pairwise-unique unique pairings (where a pairing is a system consisting of pairs, and pairwise-unique indicates that (a,b) ≠ (b,a)) for a set containing even number n items.
I like the code from here:
for perm in itertools.permutations(range(n)):
print zip(perm[::2], perm[1::2])
except that it generates all order-unique, pairwise-unique pairings, or (n/2)! times more pairings than I want (redundancy), which, although I can filter out, really bog down my program at large n.
That is, for n = 4, I am looking for the following output (12 unique pairings):
[(0, 1), (2, 3)]
[(0, 1), (3, 2)]
[(1, 0), (2, 3)]
[(1, 0), (3, 2)]
[(1, 2), (0, 3)]
[(1, 2), (3, 0)]
[(1, 3), (0, 2)]
[(2, 0), (1, 3)]
[(2, 0), (3, 1)]
[(3, 1), (0, 2)]
[(0, 3), (2, 1)]
[(3, 0), (2, 1)]
Note that (a,b) ≠ (b,a).
Is this possible? I am also okay with a function that generates the 3 non–pairwise-unique pairings for n = 4 where (a,b) = (b,a), as it is straightforward to permute what I need from there. My main goal is to avoid the superfluous permutations on the order of the pairs in the pairing.
Thanks in advance for your help and suggestions—I really appreciate it.
I think this gives you the fundamental pairings that you need: 1 when N=2; 3 when N=4; 15 when N=6; 105 when n=8, etc.
import sys
def pairings(remainder, partial = None):
partial = partial or []
if len(remainder) == 0:
yield partial
else:
for i in xrange(1, len(remainder)):
pair = [[remainder[0], remainder[i]]]
r1 = remainder[1:i]
r2 = remainder[i+1:]
for p in pairings(r1 + r2, partial + pair):
yield p
def main():
n = int(sys.argv[1])
items = list(range(n))
for p in pairings(items):
print p
main()
In the linked question "Generating all unique pair permutations", (here), an algorithm is given to generate a round-robin schedule for any given n. That is, each possible set of matchups/pairings for n teams.
So for n = 4 (assuming exclusive), that would be:
[0, 3], [1, 2]
[0, 2], [3, 1]
[0, 1], [2, 3]
Now we've got each of these partitions, we just need to find their permutations in order to get the full list of pairings. i.e [0, 3], [1, 2] is a member of a group of four: [0, 3], [1, 2] (itself) and [3, 0], [1, 2] and [0, 3], [2, 1] and [3, 0], [2, 1].
To get all the members of a group from one member, you take the permutation where each pair can be either flipped or not flipped (if they were, for example, n-tuples instead of pairs, then there would be n! options for each one). So because you have two pairs and options, each partition yields 2 ^ 2 pairings. So you have 12 altogether.
Code to do this, where round_robin(n) returns a list of lists of pairs. So round_robin(4) --> [[[0, 3], [1, 2]], [[0, 2], [3, 1]], [[0, 1], [2, 3]]].
def pairs(n):
for elem in round_robin(n):
for first in [elem[0], elem[0][::-1]]:
for second in [elem[1], elem[1][::-1]]:
print (first, second)
This method generates less than you want and then goes up instead of generating more than you want and getting rid of a bunch, so it should be more efficient. ([::-1] is voodoo for reversing a list immutably).
And here's the round-robin algorithm from the other posting (written by Theodros Zelleke)
from collections import deque
def round_robin_even(d, n):
for i in range(n - 1):
yield [[d[j], d[-j-1]] for j in range(n/2)]
d[0], d[-1] = d[-1], d[0]
d.rotate()
def round_robin_odd(d, n):
for i in range(n):
yield [[d[j], d[-j-1]] for j in range(n/2)]
d.rotate()
def round_robin(n):
d = deque(range(n))
if n % 2 == 0:
return list(round_robin_even(d, n))
else:
return list(round_robin_odd(d, n))
I'm not sure if I understood the problem well, anyway here is my solution:
import itertools
n = 4
out = set()
for perm in itertools.permutations(range(n)):
pairs = tuple(sorted(zip(perm[::2], perm[1::2])))
out.add(pairs)
for i, p in enumerate(sorted(out)):
print i,p
it returns 12 unique pairs for n=4, and 120 for n=6.