0

Suppose that we are given arrays A and B of positive integers. A and B contain the same integers (the same number of times), so they are naturally the same length.

Consider permutations of two arrays U and V to be valid if U[i] != V[i] for i = 0, 1, ..., len(U) - 1.

We want to find a valid pair of permutations for A and B. However, we want our algorithm to be such that all pairs of valid permutations are equally likely to be returned.


I've been working on this problem today and cannot seem to come up with a sleek solution. Here is my best solution thus far:

import random

def random_valid_permutation(values):
    A = values[:]
    B = values[:]

    while not is_valid_permutation(A, B):
        random.shuffle(A)
        random.shuffle(B)

    return A, B

def is_valid_permutation(A, B):
    return all([A[i] != B[i] for i in range(len(A))])

Unfortunately, since this method involves a random shuffle of each array, it could in theory take infinite time to produce a valid output. I have come up with a couple of alternatives that do run in finite (and reasonable) time, but their implementation is much longer.

Does anyone know of a sleek way to solve this problem?

Ryan
  • 7,621
  • 5
  • 18
  • 31
  • 6
    These are known as [derangements] and this was actually [asked before](http://stackoverflow.com/questions/25200220/generate-a-random-derangement-of-a-list) here on Stack Overflow. [This particular answer](http://stackoverflow.com/a/25238398/1473772) may be the most "sleek" answer for you. – El'endia Starman Nov 28 '15 at 08:15

1 Answers1

0

First note that every permutation, A, has the same number of derangements B as any other permutation A. Thus it is enough to generate a single A and then generate random B until you get a match. The probability that a permutation, B, is a derangement of A is known to be (approximately) 1/e (a little better than 1 out of 3) in a way that is essentially independent of the number of items. There is over a 99% probability that you will find a valid B with less than a dozen trials. Unless your list of values is large, fishing using the built-in random.shuffle might be quicker than rolling your own with the overhead of checking with each new placement of an item if it has led to a clash. The following is almost instantaneous with a thousand elements and still only takes about a second or so with a million elements:

import random

def random_valid_permutation(values):
    A = values[:]
    B = values[:]
    random.shuffle(A)
    random.shuffle(B)

    while not is_valid_permutation(A, B):
        random.shuffle(B)

    return A, B

def is_valid_permutation(A, B):
    return all(A[i] != B[i] for i in range(len(A)))

As an optimization -- I removed [ and ] form the definition of is_valid_permutation() since all can work directly on generator expressions. There is no reason to create the whole list in memory since any clash will typically detected long before the end of the list.

John Coleman
  • 51,337
  • 7
  • 54
  • 119