Uzyskanie wszystkich możliwych Sum, które sumują się do danej liczby

Robię aplikację matematyczną na Androida. W jednym z tych pól użytkownik może wprowadzić int (bez cyfr i powyżej 0). Chodzi o to, aby uzyskać wszystkie możliwe sumy, które sprawiają, że INT, bez dublowania (4+1 = = 1+4 w tym przypadku). Jedyną znaną rzeczą jest ta int.

Na przykład:

Powiedzmy, że użytkownik wchodzi 4, chciałbym, aby aplikacja wróciła:

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

Oczywiście 4 = = 4 więc to też powinno być dodane. Jakieś sugestie, jak mam to zrobić?

Author: Ashkan Aryan, 2011-09-07

6 answers

Oto prosty algorytm, który rzekomo to robi

From: 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);
    }

}
 15
Author: Ashkan Aryan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-07 09:29:49

Istnieją krótkie i eleganckie rekurencyjne rozwiązania do ich generowania, ale następujące mogą być łatwiejsze w użyciu i zaimplementowaniu w istniejącym kodzie:

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();
  }
}

Możesz go użyć tak:

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

Który wydrukuje:

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]
 6
Author: Bart Kiers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-07 13:10:49

Jest to pojęcie matematyczne znane jako partycje . Ogólnie rzecz biorąc, jest... trudne, ale są techniki dla małych liczb. Mnóstwo przydatnych rzeczy związanych ze stroną wiki.

 2
Author: AakashM,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-07 09:18:09

Dla liczby N wiesz, że maksymalna liczba terminów to N. więc zaczniesz od wyliczenia wszystkich tych możliwości.

Dla każdej możliwej liczby pojęć istnieje wiele możliwości. Formuła wymyka mi się teraz, ale zasadniczo chodzi o to, aby zacząć od (N+1-i + 1+... + 1) gdzie i jest liczbą pojęć, a aby przesunąć 1s od lewej do prawej, drugim przypadkiem byłoby (N-i + 2+... + 1) dopóki nie możesz wykonać kolejnego ruchu bez wyniku niesortowanego kombinacja.

(również, dlaczego otagowałeś tego androida ponownie?)

 0
Author: njzk2,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-07 08:54:08

Jest to związane z algorytmem problemu sumy podzbiorów .

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

Itd.

Więc jest to funkcja rekurencyjna z podstawieniem; czy to ma sens, czy nie, gdy mamy do czynienia z dużymi liczbami, jest czymś, o czym będziesz musiał sam zdecydować.

 0
Author: mcfinnigan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-07 08:59:04

Wszystkie te rozwiązania wydają się trochę skomplikowane. Można to osiągnąć po prostu "inkrementując" listę zainicjalizowaną na 1 ' s=n.

Jeśli ludzie nie mają nic przeciwko konwersji z c++, poniższy algorytm tworzy potrzebne wyjście.

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;
}

Dla N=5 algorytm daje następujące

[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]
 0
Author: ceorron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-12 23:13:15