Search code examples
pythonarraysrandom

Randomly Interleave 2 Arrays In Python


Suppose I have two arrays:

a = [1, 2, 3, 4]
b = [5, 6, 7, 8, 9]

I want to interleave these two arrays to a variable 'c' (note 'a' and 'b' aren't necessarily of equal length) but I don't want them interleaved in a deterministic way. In short, it isn't enough to just zip these two arrays. I don't want:

c = [1, 5, 2, 6, 3, 7, 4, 8, 9]

Instead, I want something random like:

c = [5, 6, 1, 7, 2, 3, 8, 4, 9]

Also notice that the order of 'a' and 'b' are preserved in the resulting array, 'c'.

The current solution I have requires a for loop and some random number generation. I don't like it and I'm hoping someone can point me to a better solution.

# resulting array
c = []

# this tells us the ratio of elements to place in c. if there are more elements 
# in 'a' this ratio will be larger and as we iterate over elements, we will place
# more elements from 'a' into 'c'.
ratio = float(len(a)) / float(len(a) + len(b))

while a and b:
    which_list = random.random()
    if which_list < ratio:
        c.append(a.pop(0))
    else:
        c.append(b.pop(0))

# tack on any extra elements to the end
if a:
    c += a
elif b:
    c += b

Solution

  • edit: I think this recent one is best:

    a = [1, 2, 3, 4]
    b = [5, 6, 7, 8, 9]
    c = [x.pop(0) for x in random.sample([a]*len(a) + [b]*len(b), len(a)+len(b))]
    

    Or more efficiently:

    c = map(next, random.sample([iter(a)]*len(a) + [iter(b)]*len(b), len(a)+len(b)))
    

    Note that the first method above modifies the original lists (as your code did) while the second method does not. On Python 3.x you would need to do list(map(...)) since map returns an iterator.

    original answer below:

    Here is an option that saves a few lines:

    a = [1, 2, 3, 4]
    b = [5, 6, 7, 8, 9]
    
    c = []
    tmp = [a]*len(a) + [b]*len(b)
    while a and b:
        c.append(random.choice(tmp).pop(0))
    
    c += a + b
    

    Here is another option, but it will only work if you know that all of your elements are not falsy (no 0, '', None, False, or empty sequences):

    a = [1, 2, 3, 4]
    b = [5, 6, 7, 8, 9]
    
    ratio = float(len(a)) / float(len(a) + len(b))
    c = [(not a and b.pop(0)) or (not b and a.pop(0)) or
         (random.random() < ratio and b.pop(0)) or a.pop(0)
         for _ in range(len(a) + len(b))]