使用Python将非唯一实体划分为唯一集
我有一个项目列表,例如使用Python将非唯一实体划分为唯一集,python,Python,我有一个项目列表,例如[1,1,1,1,1,2,2],我试图找到所有将这些项目捆绑到长度为1或2的元组中的唯一组。例如,对于上面的组,我想找到以下10种可能的组: [[(1,),(1,),(1,),(1,),(2,),(2,)], [(1,1),(1,),(1,),(2,),(2,)], [(1,2),(1,),(1,),(1,),(2,)], [(2,2),(1,),(1,),(1,),(1,)], [(1,1),(1,1),(2,),(2,)], [(1,1),(1,2),(1,)
[1,1,1,1,1,2,2]
,我试图找到所有将这些项目捆绑到长度为1或2的元组中的唯一组。例如,对于上面的组,我想找到以下10种可能的组:
[[(1,),(1,),(1,),(1,),(2,),(2,)],
[(1,1),(1,),(1,),(2,),(2,)],
[(1,2),(1,),(1,),(1,),(2,)],
[(2,2),(1,),(1,),(1,),(1,)],
[(1,1),(1,1),(2,),(2,)],
[(1,1),(1,2),(1,),(2,)],
[(1,2),(1,2),(1,),(1,)],
[(2,2),(1,1),(1,),(1,)],
[(1,1),(1,1),(2,2)],
[(1,1),(1,2),(1,2)]]
我一直在使用itertools,但只能设法使用它来查找唯一的可能元组(例如,set(list(itertools.compositions((1,1,1,1,2,2)))
),并且我所做的任何搜索都会弹出解决方案,其中每个组的大小是恒定的,并且/或者不考虑元素的重复(,)
最终,我正在寻找一种解决方案,它将适用于所有的情况(
[1,1,1,…,1]
)、所有的两种情况([2,2,2,…,2]
)或包含任意数量的1和2的中间组合。正如我在评论中指出的,输入列表的最大长度至关重要。下面的示例代码通过对整个分区集进行后处理(清除重复的分区,以及清除带有“太大”的分区),足够快地解决了您给出的特定示例。但对于“长”的原始列表来说,这将是非常低效的:
def part(xs): # generate all partitions of xs
xs = tuple(xs)
n = len(xs)
def extend(i):
if i == n:
yield ()
return
this = xs[i]
for o in extend(i+1):
yield ((this,),) + o
for j, p in enumerate(o):
yield o[:j] + ((this,) + p,) + o[j+1:]
for o in extend(0):
yield o
def upart(xs): # weed out dups, and partitions with a piece bigger than 2
from collections import Counter
seen = []
for p in part(xs):
if all(len(chunk) <= 2 for chunk in p):
c = Counter(p)
if c not in seen:
seen.append(c)
yield p
xs = [1,1,1,1,2,2]
for o in upart(xs):
print o
自定义生成器
正如在评论中也提到的,如果对一般构建块的结果进行后处理效率太低,那么您需要从头开始“滚动您自己的”。这里有一种非常节省空间的方法,通过构造(而不是后期处理)构建独特的结果。确实没有“通用方法”可以做到这一点——它需要分析手头的特定问题,并编写代码来利用您可以找到的任何怪癖:
def custom_gen(xs):
from collections import Counter
assert all(1 <= i <= 2 for i in xs)
# There are only 5 unique pieces that can be used:
pieces = [(1,), (2,), (1, 1), (2, 2), (1, 2)]
countpieces = {piece: Counter(piece) for piece in pieces}
def extend(i, n1, n2, result):
# try all ways of extending with pieces[i];
# there are n1 1's and n2 2's remaining to be used
assert n1 >= 0 and n2 >= 0
if n1 == n2 == 0:
yield result
return
if i == len(pieces): # dead end
return
piece = pieces[i]
c = countpieces[piece]
p1 = c[1]
p2 = c[2]
# What's the most number of this piece we could
# possibly take?
assert p1 or p2
if p1:
if p2:
most = min(n1 // p1, n2 // p2)
else:
most = n1 // p1
else:
most = n2 // p2
for count in range(most + 1):
for t in extend(i+1,
n1 - count * p1,
n2 - count * p2,
result + [piece] * count):
yield t
c = Counter(xs)
for t in extend(0, c[1], c[2], []):
yield t
def自定义生成(xs):
从收款进口柜台
断言全部(1=0)
如果n1==n2==0:
产量结果
回来
如果i==len(件):#死胡同
回来
件
c=计数件[件]
p1=c[1]
p2=c[2]
#这件最多能卖多少
#可能需要吗?
断言p1或p2
如果p1:
如果p2:
most=最小值(n1//p1,n2//p2)
其他:
most=n1//p1
其他:
most=n2//p2
对于范围内的计数(most+1):
对于延伸中的t(i+1,
n1-计数*p1,
n2-计数*p2,
结果+[件]*计数):
产量t
c=计数器(xs)
对于扩展(0,c[1],c[2],])中的t:
产量t
请注意,递归的深度永远不会超过5(无论输入列表有多长),因此我敢打赌,这是在不深入分析问题数学的情况下可以做到的最有效的方法。正如我在一篇评论中指出的,输入列表的最大长度至关重要。下面的示例代码通过对整个分区集进行后处理,足够快地解决了您给出的具体示例(删除重复项,并删除包含“太大”部分的分区)。但对于“长”的原始列表来说,这将是非常低效的:
def part(xs): # generate all partitions of xs
xs = tuple(xs)
n = len(xs)
def extend(i):
if i == n:
yield ()
return
this = xs[i]
for o in extend(i+1):
yield ((this,),) + o
for j, p in enumerate(o):
yield o[:j] + ((this,) + p,) + o[j+1:]
for o in extend(0):
yield o
def upart(xs): # weed out dups, and partitions with a piece bigger than 2
from collections import Counter
seen = []
for p in part(xs):
if all(len(chunk) <= 2 for chunk in p):
c = Counter(p)
if c not in seen:
seen.append(c)
yield p
xs = [1,1,1,1,2,2]
for o in upart(xs):
print o
自定义生成器
正如评论中所指出的,如果对一般构建块的结果进行后处理效率太低,那么您需要从头开始“滚动您自己的”。这里有一种非常节省空间的方法,通过构造(而不是通过后处理)构建独特的结果。实际上没有“通用方法”要做到这一点,需要分析手头的具体问题,并编写代码以利用您可以发现的任何怪癖:
def custom_gen(xs):
from collections import Counter
assert all(1 <= i <= 2 for i in xs)
# There are only 5 unique pieces that can be used:
pieces = [(1,), (2,), (1, 1), (2, 2), (1, 2)]
countpieces = {piece: Counter(piece) for piece in pieces}
def extend(i, n1, n2, result):
# try all ways of extending with pieces[i];
# there are n1 1's and n2 2's remaining to be used
assert n1 >= 0 and n2 >= 0
if n1 == n2 == 0:
yield result
return
if i == len(pieces): # dead end
return
piece = pieces[i]
c = countpieces[piece]
p1 = c[1]
p2 = c[2]
# What's the most number of this piece we could
# possibly take?
assert p1 or p2
if p1:
if p2:
most = min(n1 // p1, n2 // p2)
else:
most = n1 // p1
else:
most = n2 // p2
for count in range(most + 1):
for t in extend(i+1,
n1 - count * p1,
n2 - count * p2,
result + [piece] * count):
yield t
c = Counter(xs)
for t in extend(0, c[1], c[2], []):
yield t
def自定义生成(xs):
从收款进口柜台
断言全部(1=0)
如果n1==n2==0:
产量结果
回来
如果i==len(件):#死胡同
回来
件
c=计数件[件]
p1=c[1]
p2=c[2]
#这件最多能卖多少
#可能需要吗?
断言p1或p2
如果p1:
如果p2:
most=最小值(n1//p1,n2//p2)
其他:
most=n1//p1
其他:
most=n2//p2
对于范围内的计数(most+1):
对于延伸中的t(i+1,
n1-计数*p1,
n2-计数*p2,
结果+[件]*计数):
产量t
c=计数器(xs)
对于扩展(0,c[1],c[2],])中的t:
产量t
请注意,递归的深度永远不会超过5(无论输入列表有多长),因此我打赌这是在不深入分析问题数学的情况下可以做到的最有效的方法。这不是所有的排序。即使我们将数字限制在自己的插槽中,您也有6!/(2!*4!)=15对…也许我误解了你的意思,但这对我来说似乎很不清楚。原始列表可以有多少个元素?这很关键,因为列表越长,分区的数量越大。如果列表总是“非常小”,然后我们可以研究如何对一整套分区进行后期处理,以排除重复项。否则,您需要一个更高级的自定义生成器,它一开始就不会生成重复项。这不是所有的排序。即使我们将数字限制在其自己的插槽中,您也有6!/(2!*4!)=15对…也许我误解了你的意思,但这对我来说似乎很不清楚。原始列表可以有多少个元素?这很关键,因为列表越长,分区的数量越大。如果列表总是“非常小”,然后我们可以研究如何对一整套分区进行后期处理,以丢弃重复项。否则,您需要一个更高级的自定义生成器,它一开始就不会生成重复项。谢谢!这很好地解决了这个问题。我的问题确实有点大(最大长度为12),对于我的要求,运行几秒钟就可以了