【问题标题】:Getting all possible sums that add up to a given number获取加起来等于给定数字的所有可能总和
【发布时间】:2011-11-11 23:09:42
【问题描述】:

我正在为安卓制作一个数学应用程序。在这些字段之一中,用户可以输入一个 int(无数字且大于 0)。这个想法是得到所有可能的总和,使这个整数,没有双打(在这种情况下为 4+1 == 1+4)。唯一已知的就是这个 int。

例如:

假设用户输入4,我希望app返回:

  • 4
  • 3+1
  • 2+2
  • 2+1+1
  • 1+1+1+1

显然 4 == 4 所以也应该添加。关于我应该如何做这件事的任何建议?

【问题讨论】:

  • 如果能加上算法标志就更好了
  • 5 呢? :::: 5 4+1 2.5 + 2.5 ? 1+1+1+1+1
  • 你应该知道一个数字的分区数增长很快。到 22 岁时,已经有超过 1000 个分区,当您达到 100 个时,您会看到大约 190,000,000 个分区。
  • @nik 不是整数(因为它说没有数字)
  • 从技术上讲,int 是“总和”。加起来为和的数字称为“加数”

标签: java algorithm math partition-problem


【解决方案1】:

这是一个声称可以做到这一点的简单算法

来自:http://introcs.cs.princeton.edu/java/23recursion/Partition.java.html

public class Partition { 

    public static void partition(int n) {
        partition(n, n, "");
    }
    public static void partition(int n, int max, String prefix) {
        if (n == 0) {
            StdOut.println(prefix);
            return;
        }

        for (int i = Math.min(max, n); i >= 1; i--) {
            partition(n-i, i, prefix + " " + i);
        }
    }


    public static void main(String[] args) { 
        int N = Integer.parseInt(args[0]);
        partition(N);
    }

}

【讨论】:

    【解决方案2】:

    有一些简短而优雅的递归解决方案可以生成它们,但以下可能更容易在现有代码中使用和实现:

    import java.util.*;
    
    public class SumIterator implements Iterator<List<Integer>>, Iterable<List<Integer>> {
    
      // keeps track of all sums that have been generated already
      private Set<List<Integer>> generated;
    
      // holds all sums that haven't been returned by `next()`
      private Stack<List<Integer>> sums;
    
      public SumIterator(int n) {
    
        // first a sanity check...
        if(n < 1) {
          throw new RuntimeException("'n' must be >= 1");
        }
    
        generated = new HashSet<List<Integer>>();
        sums = new Stack<List<Integer>>();
    
        // create and add the "last" sum of size `n`: [1, 1, 1, ... , 1]
        List<Integer> last = new ArrayList<Integer>();
        for(int i = 0; i < n; i++) {
          last.add(1);
        }
        add(last);
    
        // add the first sum of size 1: [n]
        add(Arrays.asList(n));
      }
    
      private void add(List<Integer> sum) {
        if(generated.add(sum)) {
          // only push the sum on the stack if it hasn't been generated before
          sums.push(sum);
        }
      }
    
      @Override
      public boolean hasNext() {
        return !sums.isEmpty();
      }
    
      @Override
      public Iterator<List<Integer>> iterator() {
        return this;
      }
    
      @Override
      public List<Integer> next() {
        List<Integer> sum = sums.pop();                         // get the next sum from the stack
        for(int i = sum.size() - 1; i >= 0; i--) {              // loop from right to left
          int n = sum.get(i);                                   //   get the i-th number
          if(n > 1) {                                           //   if the i-th number is more than 1
            for(int j = n-1; j > n/2; j--) {                    //     if the i-th number is 10, loop from 9 to 5
              List<Integer> copy = new ArrayList<Integer>(sum); //       create a copy of the current sum
              copy.remove(i);                                   //       remove the i-th number
              copy.add(i, j);                                   //       insert `j` where the i-th number was
              copy.add(i + 1, n-j);                             //       insert `n-j` next to `j`
              add(copy);                                        //       add this new sum to the stack
            }                                                   //     
            break;                                              //   stop looping any further
          }                                                     
        }
        return sum;
      }
    
      @Override
      public void remove() {
        throw new UnsupportedOperationException();
      }
    }
    

    你可以这样使用它:

    int n = 10;
    for(List<Integer> sum : new SumIterator(n)) {
      System.out.println(n + " = " + sum);
    }
    

    将打印:

    10 = [10] 10 = [6, 4] 10 = [6, 3, 1] 10 = [6, 2, 1, 1] 10 = [7, 3] 10 = [7, 2, 1] 10 = [8, 2] 10 = [9, 1] 10 = [5, 4, 1] 10 = [5, 3, 1, 1] 10 = [5, 2, 1, 1, 1] 10 = [8, 1, 1] 10 = [7, 1, 1, 1] 10 = [4, 3, 1, 1, 1] 10 = [4, 2, 1, 1, 1, 1] 10 = [6, 1, 1, 1, 1] 10 = [5, 1, 1, 1, 1, 1] 10 = [3, 2, 1, 1, 1, 1, 1] 10 = [4, 1, 1, 1, 1, 1, 1] 10 = [3, 1, 1, 1, 1, 1, 1, 1] 10 = [2, 1, 1, 1, 1, 1, 1, 1, 1] 10 = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

    【讨论】:

    • 非常好的代码,但是来自 Ashkan Aryan 的代码更干净,因为这是为喜欢干净代码的客户准备的,我会使用那个。我支持你,因为它是一段非常好的代码!
    • 代码遗漏了除 1 以外的数字重复的组合(5+5、6+2+2、3+3+3+1、4+4+2 等)。
    【解决方案3】:

    这是称为partitions 的数学概念。一般来说,这很……困难,但有一些技术适用于少数人。从 wiki 页面链接的大量有用内容。

    【讨论】:

      【解决方案4】:

      对于数字 N,您知道最大项数是 N。因此,您将从枚举所有这些可能性开始。

      对于每个可能的术语数量,都有许多可能性。这个公式现在让我无法理解,但基本上,这个想法是从 (N+1-i + 1 + ... + 1) 开始,其中 i 是项数,然后将 1 从左向右移动,第二种情况会是 (Ni + 2 + ... + 1) 直到你不能做另一个动作而不导致一个未排序的组合。

      (另外,你为什么又给这个机器人加了标签?)

      【讨论】:

      • 因为它是针对应用程序的,所以并不会真正影响算法...... :)
      【解决方案5】:

      这与subset sum problem 算法有关。

      N = {N*1, (N-1)+1, (N-2)+2, (N-3)+3 .., N-1 = {(N-1), ((N-1)-1)+2, ((N-1)-1)+3..}

      等等。

      所以这是一个涉及替换的递归函数;但是,在处理大量数字时这是否有意义,您必须自己决定。

      【讨论】:

        【解决方案6】:

        所有这些解决方案似乎都有些复杂。这可以通过简单地“增加”一个初始化为包含 1's=N 的列表来实现。

        如果人们不介意从 c++ 转换,以下算法会产生所需的输出。

        bool next(vector<unsigned>& counts) {
            if(counts.size() == 1)
                return false;
        
            //increment one before the back
            ++counts[counts.size() - 2];
        
            //spread the back into all ones
            if(counts.back() == 1)
                counts.pop_back();
            else {
                //reset this to 1's
                unsigned ones = counts.back() - 1;
                counts.pop_back();
                counts.resize(counts.size() + ones, 1);
            }
            return true;
        }
        
        void print_list(vector<unsigned>& list) {
            cout << "[";
            for(unsigned i = 0; i < list.size(); ++i) {
                cout << list[i];
                if(i < list.size() - 1)
                    cout << ", ";
            }
            cout << "]\n";
        }
        
        int main() {
            unsigned N = 5;
            vector<unsigned> counts(N, 1);
            do {
                print_list(counts);
            } while(next(counts));
            return 0;
        }
        

        对于 N=5,算法给出以下结果

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-06-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多