我需要一个生成器,它可以将一组代理作为输入来获取'和一组'项目',并生成每个代理获得相同数量的项目的所有分区。例如:
>>> for p in equalPartitions(["A","B"], [1,2,3,4]): print(p)
{'A': [1, 2], 'B': [3, 4]}
{'A': [1, 3], 'B': [2, 4]}
{'A': [1, 4], 'B': [2, 3]}
{'A': [2, 3], 'B': [1, 4]}
{'A': [2, 4], 'B': [1, 3]}
{'A': [3, 4], 'B': [1, 2]}
对于两个代理人来说这很容易(假设项目数是偶数):
itemsPerAgent = len(items) // len(agents)
for bundle0 in itertools.combinations(items, itemsPerAgent):
bundle1 = [item for item in items if item not in bundle0]
yield {
agents[0]: list(bundle0),
agents[1]: bundle1
}
对于三个代理商来说,这变得更加复杂:
itemsPerAgent = len(items) // len(agents)
for bundle0 in itertools.combinations(items, itemsPerAgent):
bundle12 = [item for item in items if item not in bundle0]
for bundle1 in itertools.combinations(bundle12, itemsPerAgent):
bundle2 = [item for item in bundle12 if item not in bundle1]
yield {
agents[0]: list(bundle0),
agents[1]: list(bundle1),
agents[2]: bundle2
}
是否有更通用的解决方案,适用于任意数量的代理?
答案 0 :(得分:2)
这是一个递归解决方案,它通过向第一个代理分配适当数量的项目,并将剩下的问题交给其他人进一步调用来工作:
from itertools import combinations
def part(agents, items):
if len(agents) == 1:
yield {agents[0]: items}
else:
quota = len(items) // len(agents)
for indexes in combinations(range(len(items)), quota):
remainder = items[:]
selection = [remainder.pop(i) for i in reversed(indexes)][::-1]
for result in part(agents[1:], remainder):
result[agents[0]] = selection
yield result
在单个代理的简单情况下,我们生成一个字典并终止。
如果有多个代理人,我们:
生成应分配给第一个代理的items
的所有索引组合。
将这些索引中的项目从items
的副本以相反的顺序(以避免弄乱索引)弹出到selection
中,然后用{{1}再次将结果反转回来保持预期的顺序。
以递归方式呼叫[::-1]
剩余的座席和项目。
将我们已经做出的选择添加到这些递归调用产生的每个结果中,然后产生。
这是在行动:
part()
>>> for p in part(["A", "B"], [1, 2, 3, 4]):
... print(p)
...
{'A': [1, 2], 'B': [3, 4]}
{'A': [1, 3], 'B': [2, 4]}
{'A': [1, 4], 'B': [2, 3]}
{'A': [2, 3], 'B': [1, 4]}
{'A': [2, 4], 'B': [1, 3]}
{'A': [3, 4], 'B': [1, 2]}
>>> for p in part(["A", "B", "C"], [1, 2, 3, 4, 5, 6, 7, 8, 9]):
... print(p)
...
{'A': [1, 2, 3], 'B': [4, 5, 6], 'C': [7, 8, 9]}
{'A': [1, 2, 3], 'B': [4, 5, 7], 'C': [6, 8, 9]}
{'A': [1, 2, 3], 'B': [4, 5, 8], 'C': [6, 7, 9]}
{'A': [1, 2, 3], 'B': [4, 5, 9], 'C': [6, 7, 8]}
{'A': [1, 2, 3], 'B': [4, 6, 7], 'C': [5, 8, 9]}
# [...]
{'A': [7, 8, 9], 'B': [3, 4, 5], 'C': [1, 2, 6]}
{'A': [7, 8, 9], 'B': [3, 4, 6], 'C': [1, 2, 5]}
{'A': [7, 8, 9], 'B': [3, 5, 6], 'C': [1, 2, 4]}
{'A': [7, 8, 9], 'B': [4, 5, 6], 'C': [1, 2, 3]}
如您所见,它处理>>> for p in part(["A", "B", "C"], [1, 2, 3, 4, 5, 6, 7]):
... print(p)
...
{'A': [1, 2], 'B': [3, 4], 'C': [5, 6, 7]}
{'A': [1, 2], 'B': [3, 5], 'C': [4, 6, 7]}
{'A': [1, 2], 'B': [3, 6], 'C': [4, 5, 7]}
{'A': [1, 2], 'B': [3, 7], 'C': [4, 5, 6]}
# [...]
{'A': [6, 7], 'B': [2, 5], 'C': [1, 3, 4]}
{'A': [6, 7], 'B': [3, 4], 'C': [1, 2, 5]}
{'A': [6, 7], 'B': [3, 5], 'C': [1, 2, 4]}
{'A': [6, 7], 'B': [4, 5], 'C': [1, 2, 3]}
在items
之间无法平分的情况。此外,与基于agents
的各种答案不同,它不会浪费计算重复结果的工作,因此运行速度比它们快得多。
答案 1 :(得分:1)
如果你有一个permutations
函数可以处理输入中的重复元素而不会在输出中产生重复的排列,那么你可以非常有效地完成这项工作。遗憾的是,itertools.permutations
没有做我们想要的事情(len(list(itertools.permutations('aaa')))
是6
,而不是我们想要的1
。
这是我为之前的一些问题编写的排列函数,它恰好使用重复的输入值做了正确的事情:
def permutations(seq):
perm = sorted(seq) # the first permutation is the sequence in sorted order
while True:
yield perm
# find largest index i such that perm[i] < perm[i+1]
for i in range(len(perm)-2, -1, -1):
if perm[i] < perm[i+1]:
break
else: # if none was found, we've already found the last permutation
return
# find the largest index j such that perm[i] < perm[j] (always exists)
for j in range(len(perm)-1, -1, -1):
if perm[i] < perm[j]:
break
# Swap values at indexes i and j, then reverse the values from i+1
# to the end. I've packed that all into one operation, with slices.
perm = perm[:i]+perm[j:j+1]+perm[-1:j:-1]+perm[i:i+1]+perm[j-1:i:-1]
现在,以下是如何使用它为您的代理商分配项目:
def equal_partitions(agents, items):
items_per_agent, extra_items = divmod(len(items), len(agents))
item_assignments = agents * items_per_agent + agents[:extra_items]
for assignment in permutations(item_assignments):
result = {}
for agent, item in zip(assignment, items):
result.setdefault(agent, []).append(item)
yield result
第一行构建一个对代理的引用列表,其长度与项列表的长度相同。每个代理重复的次数与他们收到的项目数相同。如果items
列表无法完全均匀分配,我会向前几个代理添加一些额外的引用。如果您愿意,可以添加其他内容(例如['extra'] * extra_items
)。
主循环在赋值列表的排列上运行。然后它运行一个内部循环,将来自排列的代理与相应的项匹配,并将结果打包成您想要的格式的字典。
对于任意数量的代理或项目,此代码在时间和空间上应该是渐近最优的。也就是说,它可能仍然很慢,因为它依赖于用纯Python编写的permutation
函数,而不是C中更快的实现。可能有一种有效的方法来获得我们想要的排列使用itertools
,但我不确定如何。
答案 2 :(得分:0)
一种极其缺乏记忆力的解决方案,但是很短且更多&#34; pythonic&#34;。此外,结果中字典的顺序非常随意,imo。
import itertools as it
from pprint import pprint
from time import time
agents = ('a', 'b', 'c')
items = (1,2,3,4,5,6,7,8,9)
items_per_agent = int(len(items)/len(agents))
def split_list(alist,max_size=1):
"""Yield successive n-sized chunks from alist."""
for i in range(0, len(alist), max_size):
yield alist[i:i+max_size]
def my_solution():
# I have put this into one-liner below
# combos = set()
# i=0
# for perm in it.permutations(items, len(items)):
# combo = tuple(tuple(sorted(chunk)) for chunk in split_list(perm, max_size=items_per_agent))
# combos.add(combo)
# print(combo, i)
# i+=1
combos = {tuple(tuple(sorted(chunk)) for chunk in split_list(perm, max_size=items_per_agent)) for perm in it.permutations(items, len(items))}
# I have put this into one-liner below
# result = []
# for combo in combos:
# result.append(dict(zip(agents,combo)))
result = [dict(zip(agents,combo)) for combo in combos]
pprint(result)
my_solution()
答案 3 :(得分:0)
# -*- coding: utf-8 -*-
from itertools import combinations
from copy import copy
def main(agents, items):
if len(items) % len(agents):
return []
result = [{'remain': items}]
part_size = len(items) / len(agents)
while True:
for item in result[:]:
remain_agent = set(agents) - set(item.keys())
if not remain_agent:
continue
result.remove(item)
agent = remain_agent.pop()
for combination in combinations(item['remain'], part_size):
current_item = copy(item)
current_item.update({agent: combination, 'remain': list(set(item['remain']) - set(combination))})
result.append(current_item)
break
else:
break
for item in result:
item.pop('remain', None)
return result
if __name__ == '__main__':
agents = ['A', 'B', 'C']
items = [1, 2, 3, 4, 5, 6]
t = main(agents, items)
这是在行动:
In [3]: agents = ['A', 'B']
In [4]: items = [1, 2, 3, 4]
In [5]: result = main(agents, items)
In [6]: for item in result:
...: print item
...:
{'A': (1, 2), 'B': (3, 4)}
{'A': (1, 3), 'B': (2, 4)}
{'A': (1, 4), 'B': (2, 3)}
{'A': (2, 3), 'B': (1, 4)}
{'A': (2, 4), 'B': (1, 3)}
{'A': (3, 4), 'B': (1, 2)}
答案 4 :(得分:-1)
from itertools import combinations,permutations
def get(items, no_of_agents):
def chunks(l, n):
"""Yield successive n chunks from l."""
rt = []
ln = len(l) // n
for i in range(0, len(l) - ln - 1, ln):
rt.append(l[i:i + ln])
rt.append(l[i + ln:])
return rt
for i in permutations(items, len(items)):
yield chunks(i,no_of_agents)
def get_equal_partitions(items, agents):
for i in get(items, len(agents)):
yield dict(zip(agents, i))
items = [i for i in range(4)]
agents = ["A","B","C"]
for i in get_equal_partitions(items,agents):
print(i)