2

问题

我需要一个算法来做到这一点:

找到所有独特的方法来在不关心顺序的“桶”中划分给定的总和

我希望我是清除合理连贯地表达自己。

例子

对于总和 5 和 3 桶,算法应该返回的是:

[5, 0, 0]
[4, 1, 0]
[3, 2, 0]
[3, 1, 1] [2, 2, 1]

免责声明

如果这个问题可能是一个骗子,我很抱歉,但我不知道这些问题到底是什么。尽管如此,我还是使用我能想到的所有措辞在 Google 和 SO 上进行了搜索,但只找到了以最均匀的方式分发的结果,而不是所有独特的方式。

4

6 回答 6

2

对我来说,写几行代码比写一篇关于算法的 5 页文章要容易一些。想到的最简单的版本:

vector<int> ans;

void solve(int amount, int buckets, int max){
  if(amount <= 0) { printAnswer(); return;}
  if(amount > buckets * max) return; // we wont be able to fulfill this request anymore

  for(int i = max; i >= 1; i--){
    ans.push_back(i);
    solve(amount-i, buckets-1, i);
    ans.pop_back();
  } 
}

void printAnswer(){
  for(int i = 0; i < ans.size(); i++) printf("%d ", ans[i]);
  for(int i = 0; i < all_my_buckets - ans.size(); i++) printf("0 ");
  printf("\n");
}

它也值得改进到您堆叠您的选择的地步solve( amount-k*i, buckets-k, i-1)- 这样您就不会产生太深的重复。(据我所知,堆栈的大小为 O(sqrt(n)) 。

为什么没有动态规划?

我们不想找到所有这些可能性的计数,所以即使我们再次到达相同的点,无论如何我们都必须打印每个数字,所以复杂性将保持不变。

希望对你有帮助,有什么问题可以问我

于 2013-03-13T15:17:46.967 回答
1

这是 Haskell 中依赖于这个答案的东西:

import Data.List (nub, sort)

parts 0 = []
parts n = nub $ map sort $ [n] : [x:xs | x <- [1..n`div`2], xs <- parts(n - x)]

partitions n buckets = 
  let p = filter (\x -> length x <= buckets) $ parts n 
  in map (\x -> if length x == buckets then x else addZeros x) p  
    where addZeros xs = xs ++ replicate (buckets - length xs) 0


OUTPUT:
*Main> partitions 5 3
[[5,0,0],[1,4,0],[1,1,3],[1,2,2],[2,3,0]]
于 2013-03-13T15:57:16.270 回答
0

一种完全不同的方法,但如果您不关心效率或优化,您总是可以使用旧的“无桶”分区算法。然后,您可以通过检查答案中零的数量来过滤搜索。

例如[1,1,1,1,1]将被忽略,因为它有超过 3 个桶,但[2,2,1,0,0]会通过。

于 2013-03-13T14:59:06.843 回答
0

这称为整数分区。

Fast Integer Partition Algorithms是一篇全面的论文,描述了执行整数分区的所有最快算法。

于 2013-03-13T15:10:32.960 回答
0

如果只有三个桶,这将是最简单的代码。

for(int i=0;i<=5;i++){
        for(int j=0;j<=5-i&&j<=i;j++){
            if(5-i-j<=i && 5-i-j<=j)
                System.out.println("["+i+","+j+","+(5-i-j)+"]");
        }
}
于 2013-03-13T15:18:41.423 回答
0

只需在此处添加我的方法以及其他方法即可。它是用 Python 编写的,所以它实际上就像伪代码。

我的第一种方法奏效了,但效率极低:

def intPart(buckets, balls):
    return uniqify(_intPart(buckets, balls))

def _intPart(buckets, balls):
    solutions = []

    # base case
    if buckets == 1:
        return [[balls]]

    # recursive strategy
    for i in range(balls + 1):
        for sol in _intPart(buckets - 1, balls - i):
            cur = [i]
            cur.extend(sol)
            solutions.append(cur)

    return solutions

def uniqify(seq):
    seen = set()
    sort = [list(reversed(sorted(elem))) for elem in seq]
    return [elem for elem in sort if str(elem) not in seen and not seen.add(str(elem))]

这是我重新设计的解决方案。它完全避免了通过使用 max_ 变量跟踪前一个桶中的球来“唯一化”它的需要。这会对列表进行排序并防止任何欺骗:

def intPart(buckets, balls, max_ = None):
    # init vars
    sols = []
    if max_ is None:
        max_ = balls
    min_ = max(0, balls - max_)

    # assert stuff
    assert buckets >= 1
    assert balls >= 0

    # base cases
    if (buckets == 1):
        if balls <= max_:
            sols.append([balls])
    elif balls == 0:
        sol = [0] * buckets
        sols.append(sol)

    # recursive strategy
    else:
        for there in range(min_, balls + 1):
            here = balls - there
            ways = intPart(buckets - 1, there, here)
            for way in ways:
                sol = [here]
                sol.extend(way)
                sols.append(sol)

    return sols

只是为了全面起见,这是从 用 Perl 编写的MJD偷来的另一个答案:

#!/usr/bin/perl

sub part {
  my ($n, $b, $min) = @_;
  $min = 0 unless defined $min;

  # base case
  if ($b == 0) {
    if ($n == 0) { return ([]) }
    else         { return ()   }
  }

  my @partitions;
  for my $first ($min .. $n) {
    my @sub_partitions = part($n - $first, $b-1, $first);
    for my $sp (@sub_partitions) {
      push @partitions, [$first, @$sp];
    }
  }
  return @partitions;
}
于 2013-03-14T13:33:18.833 回答