【问题标题】:Algorithm to partition/distribute sum between buckets in all unique ways以所有独特方式在桶之间划分/分配总和的算法
【发布时间】:2013-03-14 21:53:14
【问题描述】:

问题

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

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

我希望我在表达自己时清晰相当连贯。

示例

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

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

免责声明

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

【问题讨论】:

  • 你不见了[2,2,1]
  • @IvayloStrandjev 谢谢。如果您可以编辑我的算法以使其适合它?
  • 搜索“整数分区”以查找更多骗子。
  • @mbeckish 呃……你能给个骗子的链接吗?在您当前链接的问题中,顺序很重要(但轮换不重要),这里两者都重要。因此,问题是并且应该是分开的。

标签: algorithm unique distribution partitioning


【解决方案1】:

对我来说,编写几行代码比写一篇关于算法的 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))。

为什么不用动态规划?

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

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

【讨论】:

  • 谢谢。 3 个问题:- 1) 你能解释一下堆叠选择部分吗? 2)我假设这是Java。您能否解释一下 Vector var 的工作原理?就像一个可以在两端弹出和追加的列表? 3)动态编程到底是什么意思? the Wikipedia page 并没有太大帮助...
【解决方案2】:

这是 Haskell 中依赖 this answer 的东西:

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]]

【讨论】:

  • 非常感谢。只有一件事:任何更易读的语言?伪代码或 Python 就可以了。我无法理解。你能解释一下你在这里使用的一些 Haskell 习语/语法吗?
  • @YatharthROCK 我希望我可以用 Python 为您编写它,但我不熟悉它。我想你必须学习一个基本的 Haskell 教程来学习它。语法 [x,y | x
【解决方案3】:

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

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)+"]");
        }
}

【讨论】:

  • 我想要任意数量的桶。这不这样做。您可能需要一个递归算法。
  • 是的,对于任意存储桶,您需要一个递归代码,同时使用动态编程来避免使用组合。
【解决方案4】:

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

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

【讨论】:

  • 你确定吗?如果与球相比,桶的数量较少,那将花费非常长的时间......
【解决方案5】:

这称为整数分区。

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

【讨论】:

    【解决方案6】:

    只需在此处添加我的方法以及其他方法。它是用 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
    

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

    #!/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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-11-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多