我怎样才能最大限度地划分一组?

时间:2009-10-24 18:25:48

标签: algorithm list permutation data-partitioning

我正在尝试解决Project Euler问题之一。因此,我需要一种算法来帮助我以任何顺序查找集合的所有可能分区。

例如,给定集合2 3 3 5

2 | 3 3 5
2 | 3 | 3 5
2 | 3 3 | 5
2 | 3 | 3 | 5
2 5 | 3 3

等等。几乎所有组合成员的组合。我当然搜索了网络,但是找不到对我有用的东西,因为我说程序员而不是高级数学。

任何人都可以帮我解决这个问题吗?我几乎可以阅读任何编程语言,从BASIC到Haskell,所以用你想要的任何语言发布。

6 个答案:

答案 0 :(得分:2)

您是否考虑过搜索树?每个节点都表示选择放置元素的位置,叶节点是答案。我不会给你代码,因为这是Project Euler乐趣的一部分;)

答案 1 :(得分:1)

答案 2 :(得分:0)

嗯,问题有两个方面。

第一,这些物品可以按任何顺序排列。所以对于N个项目,有N个!排列(假设项目被视为唯一的) 其次,您可以将分组设想为每个项目之间的一个标记,表示分隔。这些标志中将有N-1个,因此对于给定的排列,将有2 ^(N-1)个可能的分组。
这意味着对于N个项目,总共会有N!*(2 ^(N-1)个)分组/排列,它们变得非常快。

在您的示例中,前四项是一个排列的分组。最后一项是另一种排列的分组。您的商品可以被视为:

2 on 3 off 3 off 5
2月3日3关5 2月3日3月5日 2月3日3月5日 2关5 on 3 off 3

排列(显示的顺序)可以通过像树一样看待它们,如另外两个所提到的那样。这几乎肯定会涉及递归,例如here。 分组在很多方面与它们无关。获得所有排列后,如果需要,可以将它们与分组链接。

答案 3 :(得分:0)

一般情况下,我会看一下用于计算配置数量的递归结构,并构建一个类似的递归来枚举它们。最好是计算整数和配置之间的一对一映射。这适用于排列,组合等,并确保每个配置只枚举一次。

现在即使recursion for the number of partitions of some identical items也相当复杂。

对于多集的分区,计数相当于解决Project Euler problem 181到任意多集的泛化。

答案 4 :(得分:0)

以下是此部分问题所需的代码:

def memoize(f):
    memo={}
    def helper(x):
        if x not in memo:
            memo[x]=f(x)
        return memo[x]
    return helper

@memoize
def A000041(n):
    if n == 0: return 1
    S = 0
    J = n-1
    k = 2
    while 0 <= J:
        T = A000041(J)
        S = S+T if k//2%2!=0 else S-T
        J -= k if k%2!=0 else k//2
        k += 1
    return S

print A000041(100) #the 100's number in this series, as an example

答案 5 :(得分:-1)

我很快就掀起了一些代码来做到这一点。但是,我遗漏了将给定列表的每个可能组合分开,因为我不确定它实际上是否需要,但如果需要,它应该很容易添加。

无论如何,代码在少量时运行得相当好,但是,正如CodeByMoonlight已经提到的那样,可能性非常快,因此运行时也会相应增加。

无论如何,这是python代码:

import time

def separate(toseparate):
  "Find every possible way to separate a given list."
  #The list of every possibility
  possibilities = []
  n = len(toseparate)
  #We can distribute n-1 separations in the given list, so iterate from 0 to n
  for i in xrange(n):
    #Create a copy of the list to avoid modifying the already existing list
    copy = list(toseparate)
    #A boolean list indicating where a separator is put. 'True' indicates a separator
    #and 'False', of course, no separator.
    #The list will contain i separators, the rest is filled with 'False'
    separators = [True]*i + [False]*(n-i-1)
    for j in xrange(len(separators)):
      #We insert the separators into our given list. The separators have to
      #be between two elements. The index between two elements is always
      #2*[index of the left element]+1.
      copy.insert(2*j+1, separators[j])
    #The first possibility is, of course, the one we just created
    possibilities.append(list(copy))
    #The following is a modification of the QuickPerm algorithm, which finds
    #all possible permutations of a given list. It was modified to only permutate
    #the spaces between two elements, so it finds every possibility to insert n
    #separators in the given list.
    m = len(separators)
    hi, lo = 1, 0
    p = [0]*m
    while hi < m:
      if p[hi] < hi:
        lo = (hi%2)*p[hi]
        copy[2*lo+1], copy[2*hi+1] = copy[2*hi+1], copy[2*lo+1]
        #Since the items are non-unique, some possibilities will show up more than once, so we
        #avoid this by checking first.
        if not copy in possibilities:
          possibilities.append(list(copy))
        p[hi] += 1
        hi = 1
      else:
        p[hi] = 0
        hi += 1
  return possibilities

t1 = time.time()
separations = separate([2, 3, 3, 5])
print time.time()-t1
sepmap = {True:"|", False:""}
for a in separations:
  for b in a:
    if sepmap.has_key(b):
      print sepmap[b],
    else:
      print b,
  print "\n",

它基于QuickPerm算法,您可以在此处详细了解:QuickPerm

基本上,我的代码会生成一个包含n个分色的列表,将它们插入到给定的列表中,然后查找列表中所有可能的分色排列。

所以,如果我们使用你的例子,我们会得到:

2  3  3  5 
2 | 3  3  5 
2  3 | 3  5 
2  3  3 | 5 
2 | 3 | 3  5 
2  3 | 3 | 5 
2 | 3  3 | 5 
2 | 3 | 3 | 5 

在0.000154972076416秒。

但是,我仔细阅读了您正在进行的问题的问题描述,并且我看到您是如何尝试解决这个问题的,但是看看运行时间增加的速度,我认为它不会像您期望的那样快。请记住,项目欧拉的问题应该在一分钟左右解决。