Darren J. Fitzpatrick
Darren J. Fitzpatrick

Reputation: 7409

How to randomly partition a list into n nearly equal parts?

I have read the answers to the Slicing a list into n nearly-equal-length partitions [duplicate] question.

This is the accepted answer:

def partition(lst, n): 
    division = len(lst) / float(n) 
    return [ lst[int(round(division * i)): int(round(division * (i + 1)))] for i in xrange(n) ]

I am wondering, how does one modify these solutions in order to randomly assign items to a partition as opposed to incremental assignment.

Upvotes: 25

Views: 49550

Answers (6)

Barney Szabolcs
Barney Szabolcs

Reputation: 12514

The random partition that also preserves the order:

def partition_preserve_order(list_in, n):
    indices = list(range(len(list_in)))
    shuffle(indices)
    index_partitions = [sorted(indices[i::n]) for i in range(n)]
    return [[list_in[i] for i in index_partition] 
            for index_partition in index_partitions]

(that is we shuffle the indices then sort them within the partitions)

example:

random_partition_preserve_order(list('abcdefghijklmnopqrstuvxyz'), 3)
# [
#     ['c', 'd', 'g', 'm', 'p', 'r', 'v', 'x', 'y'], 
#     ['b', 'e', 'h', 'k', 'o', 'q', 't', 'u'], 
#     ['a', 'f', 'i', 'j', 'l', 'n', 's', 'z']
# ]

Upvotes: 0

Timmmm
Timmmm

Reputation: 96586

Shuffling the list doesn't preserve order. You could do something like this instead (pretty easy to adapt to more than two parts). Completely untested.

from __future__ import annotations
from typing import TypeVar
import random

T = TypeVar("T")

def partition_list(s: list[T]) -> tuple[list[T], list[T]]:
    """
    Randomly partition a list into two lists, preserving order. The number to
    take is drawn from a uniform distribution.
    """
    len_a = random.randint(0, len(s))
    len_b = len(s) - len_a
    put_in_a = [True] * len_a + [False] * len_b
    random.shuffle(put_in_a)
    a: list[T] = []
    b: list[T] = []

    for val, in_a in zip(s, put_in_a):
        if in_a:
            a.append(val)
        else:
            b.append(val)

    return a, b

Upvotes: 1

ErichBSchulz
ErichBSchulz

Reputation: 15659

Complete 2018 solution (python 3.6):

import random 
def partition (list_in, n):
    random.shuffle(list_in)
    return [list_in[i::n] for i in range(n)]

Beware! this may mutate your original list

Upvotes: 29

Teodor Pripoae
Teodor Pripoae

Reputation: 1394

First you randomize the list and then you split it in n nearly equal parts.

Upvotes: 2

SilentGhost
SilentGhost

Reputation: 319581

shuffle input list.

Upvotes: 3

bobince
bobince

Reputation: 536379

Call random.shuffle() on the list before partitioning it.

Upvotes: 46

Related Questions