Is there an efficient way in Python to get all partitions of a list of size n
into two subsets of size n/2
? I want to get some iterative construct
Here's some code that performs timeit tests on the various solutions to this problem.
To make the comparisons fair, I've commented out the argument-checking test in my functions.
I've also added a function parts_combo_set
that combines features of my combinations-based answer with that of Blckknght. It appears to be the fastest, except for very small lists.
#!/usr/bin/env python
''' Generate all pairs of equal-sized partitions of a list of even length
Testing the speed of the code at
http://stackoverflow.com/q/36025609/4014959
Written by PM 2Ring 2016.03.16
'''
from __future__ import print_function, division
from itertools import combinations
from timeit import Timer
def parts_combo(lst):
n = len(lst)
#if n % 2 != 0:
#raise ValueError('list length MUST be even')
first = lst[0]
for left in combinations(lst, n // 2):
if left[0] != first:
break
right = [u for u in lst if u not in left]
yield [list(left), right]
def parts_combo_set(lst):
n = len(lst)
#if n % 2 != 0:
#raise ValueError('list length MUST be even')
first = lst[0]
allset = set(lst)
for left in combinations(lst, n // 2):
if left[0] != first:
break
yield [list(left), list(allset.difference(left))]
def parts_gosper(lst):
n = len(lst)
#if n % 2 != 0:
#raise ValueError('list length MUST be even')
lim = 1 << (n - 1)
bits = (1 << n // 2) - 1
while bits < lim:
#Use bits to partition lst
ss = [[], []]
for i, u in enumerate(lst):
ss[bits & (1<> 2)
def sub_lists(sequence):
all_but_first = set(sequence[1:])
for item in combinations(sequence[1:], len(sequence)//2 - 1):
yield [[sequence[0]] + list(item), list(all_but_first.difference(item))]
def amit(seq):
first, rest = seq[0], seq[1:]
return [
[
list((first,) + t),
[x for x in rest if x not in t]
]
for t in combinations(rest, len(seq) // 2 - 1)
]
funcs = (
parts_combo,
parts_combo_set,
parts_gosper,
sub_lists,
amit,
)
def rset(seq):
fset = frozenset
return fset([fset([fset(u),fset(v)]) for u,v in seq])
def validate():
func = funcs[0]
master = rset(func(lst))
print('\nValidating against', func.func_name)
for func in funcs[1:]:
print(func.func_name, rset(func(lst)) == master)
def time_test(loops, reps):
''' Print timing stats for all the functions '''
for func in funcs:
fname = func.func_name
print('\n%s' % fname)
setup = 'from __main__ import lst,' + fname
#cmd = 'list(%s(lst))' % fname
cmd = 'for t in %s(lst):pass' % fname
t = Timer(cmd, setup)
r = t.repeat(reps, loops)
r.sort()
print(r)
num = 6
lst = list(range(1, num + 1))
print('num =', num)
#parts = funcs[0]
#for i, t in enumerate(parts(lst), 1):
#print('{0:2d}: {1}'.format(i, t))
validate()
time_test(10000, 3)
outputs
time_test(10000, 3)
num = 6
Validating against parts_combo
parts_combo_set True
parts_gosper True
sub_lists True
amit True
parts_combo
[0.58100390434265137, 0.58798313140869141, 0.59674692153930664]
parts_combo_set
[0.74442911148071289, 0.77211689949035645, 0.79338312149047852]
parts_gosper
[1.0791628360748291, 1.089813232421875, 1.1191768646240234]
sub_lists
[0.77199792861938477, 0.79007697105407715, 0.81944608688354492]
amit
[0.60080099105834961, 0.60345196723937988, 0.60417318344116211]
time_test(1000, 3)
num = 8
Validating against parts_combo
parts_combo_set True
parts_gosper True
sub_lists True
amit True
parts_combo
[0.22465801239013672, 0.22501206398010254, 0.23627114295959473]
parts_combo_set
[0.29469203948974609, 0.29857206344604492, 0.30069589614868164]
parts_gosper
[0.43568992614746094, 0.44395184516906738, 0.44651198387145996]
sub_lists
[0.31375885009765625, 0.32754802703857422, 0.37077498435974121]
amit
[0.22520613670349121, 0.22674393653869629, 0.24075913429260254]
time_test(500, 3)
num = 10
parts_combo
[0.52618098258972168, 0.52645611763000488, 0.53866386413574219]
parts_combo_set
[0.40614008903503418, 0.41370606422424316, 0.41525506973266602]
parts_gosper
[1.0068988800048828, 1.026188850402832, 1.1649439334869385]
sub_lists
[0.48507213592529297, 0.50991582870483398, 0.51528096199035645]
amit
[0.48686790466308594, 0.52898812294006348, 0.68387198448181152]
time_test(100, 3)
num = 12
parts_combo
[0.47471189498901367, 0.47522807121276855, 0.4798729419708252]
parts_combo_set
[0.3045799732208252, 0.30534601211547852, 0.35700607299804688]
parts_gosper
[0.83456206321716309, 0.83824801445007324, 0.87273812294006348]
sub_lists
[0.36697721481323242, 0.36919784545898438, 0.38349604606628418]
amit
[0.40012097358703613, 0.40033888816833496, 0.40788102149963379]
time_test(50, 3)
num = 14
parts_combo
[0.97016000747680664, 0.97931098937988281, 1.2653739452362061]
parts_combo_set
[0.81669902801513672, 0.88839983940124512, 0.91469597816467285]
parts_gosper
[1.772817850112915, 1.9343690872192383, 1.9586498737335205]
sub_lists
[0.78162002563476562, 0.79451298713684082, 0.8126368522644043]
amit
[0.89046502113342285, 0.89572596549987793, 0.91031289100646973]
time_test(50, 3)
num = 16
parts_combo
[4.1981601715087891, 4.3565289974212646, 4.3795731067657471]
parts_combo_set
[2.5452880859375, 2.5757780075073242, 2.6059379577636719]
parts_gosper
[7.5856668949127197, 7.6066100597381592, 7.6397140026092529]
sub_lists
[3.677016019821167, 3.6800520420074463, 3.7420001029968262]
amit
[4.1738030910491943, 4.1768841743469238, 4.1960680484771729]
time_test(10, 3)
num = 18
parts_combo
[3.8362669944763184, 3.8807728290557861, 4.0259079933166504]
parts_combo_set
[1.9355819225311279, 1.9540839195251465, 1.9573280811309814]
parts_gosper
[6.3178229331970215, 6.6125278472900391, 7.0462160110473633]
sub_lists
[2.1632919311523438, 2.238231897354126, 2.2747220993041992]
amit
[3.6137850284576416, 3.6162960529327393, 3.6475629806518555]
time_test(10, 3)
num = 20
parts_combo
[16.874133110046387, 17.585763931274414, 19.725590944290161]
parts_combo_set
[7.5462148189544678, 7.5597691535949707, 7.8375740051269531]
parts_gosper
[27.312526941299438, 27.637516021728516, 28.016690015792847]
sub_lists
[7.7865769863128662, 7.8874318599700928, 8.5498230457305908]
amit
[15.554526805877686, 15.626868009567261, 16.224159002304077]
These tests were performed on a 2GHz single-core machine with 2 GB of RAM running Python 2.6.6.