Frage

brauchte ich einen Algorithmus für alle möglichen Partitionen einer positiven Zahl zu erzeugen, und ich kam mit einer (geschrieben als Antwort), aber es ist exponentielle Zeit.

Der Algorithmus sollte alle Möglichkeiten eine Zahl zurückgeben kann als die Summe der positiven Zahlen ausgedrückt werden weniger als oder gleich selbst. So zum Beispiel für die Anzahl 5 , wäre das Ergebnis:

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

Also meine Frage ist: gibt es einen effizienteren Algorithmus für diesen

EDIT: Frage stand unter dem Titel "Sum Zerlegung einer Zahl" , da ich nicht wirklich wusste, was das hieß. ShreevatsaR wies darauf hin, dass sie genannt wurden "Partitionen", so I edited die Frage Titel entsprechend.

War es hilfreich?

Lösung

Es heißt Partitionen . [Siehe auch Wikipedia:. Partition (Zahlentheorie) ]

Die Anzahl der Partitionen p (n) wächst exponentiell, so alles, was Sie tun, um zu generieren alle Partitionen notwendigerweise exponentielle Zeit in Anspruch nehmen müssen.

Das heißt, Sie können es besser machen als das, was Ihr Code tut. Siehe diesem oder seine aktualisierte Version in Python Algorithmen und Datenstrukturen von "http://11011110.livejournal.com/4862.html" rel = "noreferrer"> David Eppstein .

Andere Tipps

Hier ist meine Lösung (exponentielle Zeit) in Python:

q = { 1: [[1]] }

def decompose(n):
    try:
        return q[n]
    except:
        pass

    result = [[n]]

    for i in range(1, n):
        a = n-i
        R = decompose(i)
        for r in R:
            if r[0] <= a:
                result.append([a] + r)

    q[n] = result
    return result

>>> decompose(5)
[[5], [4, 1], [3, 2], [3, 1, 1], [2, 2, 1], [2, 1, 1, 1], [1, 1, 1, 1, 1]]

Wenn Sie zu einem effizienteren Algorithmus fragen, ich weiß nicht, welche zu vergleichen. Aber hier ist ein Algorithmus, der in geradlinig Weise geschrieben (Erlang):

-module(partitions).

-export([partitions/1]).

partitions(N) -> partitions(N, N).

partitions(N, Max) when N > 0 ->
    [[X | P]
     || X <- lists:seq(min(N, Max), 1, -1),
        P <- partitions(N - X, X)];
partitions(0, _) -> [[]];
partitions(_, _) -> [].

Es ist exponentiell in der Zeit (wie bei Can Berk Güder Lösung in Python ) und linear in Stapelspeicher. Aber mit demselben Trick, memoization, können Sie große Verbesserung erreichen, indem er etwas Speicher und weniger Exponenten speichern. (Es ist zehnmal schneller für N = 50)

mp(N) ->
    lists:foreach(fun (X) -> put(X, undefined) end,
          lists:seq(1, N)), % clean up process dictionary for sure
    mp(N, N).

mp(N, Max) when N > 0 ->
    case get(N) of
      undefined -> R = mp(N, 1, Max, []), put(N, R), R;
      [[Max | _] | _] = L -> L;
      [[X | _] | _] = L ->
          R = mp(N, X + 1, Max, L), put(N, R), R
    end;
mp(0, _) -> [[]];
mp(_, _) -> [].

mp(_, X, Max, R) when X > Max -> R;
mp(N, X, Max, R) ->
    mp(N, X + 1, Max, prepend(X, mp(N - X, X), R)).

prepend(_, [], R) -> R;
prepend(X, [H | T], R) -> prepend(X, T, [[X | H] | R]).

Auf jeden Fall sollten Sie Benchmark für Ihre Sprache und Zwecke.

Hier ist eine viel langatmige Art und Weise tun (das ist, was ich tat, bevor ich den Begriff „Partition“ wusste, das es mir ermöglicht, eine Google-Suche zu tun):

def magic_chunker (remainder, chunkSet, prevChunkSet, chunkSets):
    if remainder > 0:
        if prevChunkSet and (len(prevChunkSet) > len(chunkSet)): # counting down from previous
            # make a chunk that is one less than relevant one in the prevChunkSet
            position = len(chunkSet)
            chunk = prevChunkSet[position] - 1
            prevChunkSet = [] # clear prevChunkSet, no longer need to reference it
        else: # begins a new countdown; 
            if chunkSet and (remainder > chunkSet[-1]): # no need to do iterations any greater than last chunk in this set
                chunk = chunkSet[-1]
            else: # i.e. remainder is less than or equal to last chunk in this set
                chunk = remainder #else use the whole remainder for this chunk
        chunkSet.append(chunk)
        remainder -= chunk
        magic_chunker(remainder, chunkSet, prevChunkSet, chunkSets)
    else: #i.e. remainder==0
        chunkSets.append(list(chunkSet)) #save completed partition
        prevChunkSet = list(chunkSet)
        if chunkSet[-1] > 1: # if the finalchunk was > 1, do further recursion
            remainder = chunkSet.pop() #remove last member, and use it as remainder
            magic_chunker(remainder, chunkSet, prevChunkSet, chunkSets)
        else: # last chunk is 1
            if chunkSet[0]==1: #the partition started with 1, we know we're finished
                return chunkSets
            else: #i.e. still more chunking to go 
                # clear back to last chunk greater than 1
                while chunkSet[-1]==1:
                    remainder += chunkSet.pop()
                remainder += chunkSet.pop()
                magic_chunker(remainder, chunkSet, prevChunkSet, chunkSets)

partitions = []
magic_chunker(10, [], [], partitions)
print partitions

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

Hier ist eine Lösung bei der Verwendung paramorphisms, die ich in Haskell geschrieben.

import Numeric.Natural       (Natural)
import Control.Monad         (join)
import Data.List             (nub)
import Data.Functor.Foldable (ListF (..), para)

partitions :: Natural -> [[Natural]]
partitions = para algebra
    where algebra Nothing          = []
          algebra (Just (0,_))     = [[1]]
          algebra (Just (_, past)) = (nub . (getAll =<<)) (fmap (1:) past)

getAll :: [Natural] -> [[Natural]]
getAll = fmap (dropWhile (==0) . sort) . subsets
    where subsets xs = flip sumIndicesAt xs <$> indices xs

indices :: [Natural] -> [[Natural]]
indices = join . para algebra
    where algebra Nil                 = []
          algebra (Cons x (xs, []))   = [[x:xs]]
          algebra (Cons x (xs, past)) = (:) <$> [x:xs,[]] <*> past

Es ist definitiv nicht die effizienteste herum, aber ich denke, es ist sehr elegant und es ist sicherlich lehrreich.

hier ist der Java-Code für diese Frage

static void printArray(int p[], int n){
        for (int i = 0; i < n; i++)
            System.out.print(p[i]+" ");
        System.out.println();
}

// Function to generate all unique partitions of an integer
static void printAllUniqueParts(int n) {
    int[] p = new int[n]; // An array to store a partition
    int k = 0; // Index of last element in a partition
    p[k] = n; // Initialize first partition as number itself

    // This loop first prints current partition, then generates next
    // partition. The loop stops when the current partition has all 1s
    while (true) {
        // print current partition
        printArray(p, k + 1);

        // Generate next partition

        // Find the rightmost non-one value in p[]. Also, update the
        // rem_val so that we know how much value can be accommodated
        int rem_val = 0;
        while (k >= 0 && p[k] == 1) {
            rem_val += p[k];
            k--;
        }

        // if k < 0, all the values are 1 so there are no more partitions
        if (k < 0){
            break;
        }
        // Decrease the p[k] found above and adjust the rem_val
        p[k]--;
        rem_val++;

        while (rem_val > p[k]) {
            p[k + 1] = p[k];
            rem_val = rem_val - p[k];
            k++;
        }
        p[k + 1] = rem_val;
        k++;
    }
}

public static void main(String[] args) {
    System.out.println("All Unique Partitions of 5");
    printAllUniqueParts(5);

    System.out.println("All Unique Partitions of 7");
    printAllUniqueParts(7);

    System.out.println("All Unique Partitions of 9");
    printAllUniqueParts(8);
}

Eine andere Java-Lösung. Es beginnt mit der ersten Partition zu schaffen, die nur die gegebene Zahl ist. Dann geht es in while-Schleife, die die letzte Nummer in zuletzt erstellte Partition zu finden, die dann größer ist 1. Von dieser Zahl zur 1 nächsten Nummer in der Reihe bewegt. Wenn die nächste Nummer würde das gleiche wie die gefundenen Nummer am Ende wird es auf den nächsten in der Reihe bewegt. Schleife stoppt, wenn die erste Anzahl der zuletzt erstellte Partition ist 1. Dies funktioniert, weil zu allen Zeiten Zahlen in allen Partitionen in absteigender Reihenfolge sortiert werden.

Beispiel mit der Nummer 5. Zuerst erstellt er erste Partition, die 5. gerade Zahl ist es letzte Nummer findet dann in den letzten Partition, die größer als 1 ist Da unsere letzte Partition ist array [5, 0, 0, 0, 0] es gründet Nummer 5 bei Index 0. Dann dauert es eine zwischen 5 und bewegt es zur nächsten Position. Das ist, wie wir Partition erhalten [4, 1, 0, 0, 0]. Es geht in der Schleife wieder. Jetzt dauert es eine von 4 und bewegt sie nach oben, so erhalten wir [3, 2, 0, 0, 0]. Dann wird die gleiche Sache und wir bekommen [3, 1, 1, 0, 0]. Bei der nächsten Iteration erhalten wir [2, 2, 1, 0, 0]. Jetzt dauert es eine von den zweiten 2 und versucht, sie zum Index 2 zu bewegen, wo wir 1 haben Sie auf den nächsten Index überspringen, weil wir auch 2 erhalten würden und wir würden Partition [2, 1, 2, 0, 0], die ist Duplikat nur der letzten. stattdessen bekommen wir [2, 1, 1, 1, 0]. Und im letzten Schritt zu bekommen wir [1, 1, 1, 1, 1] und Schleife existiert seit dem erste Reihe neuer Partition 1 ist.

private static List<int[]> getNumberPartitions(int n) {
    ArrayList<int[]> result = new ArrayList<>();
    int[] initial = new int[n];
    initial[0] = n;
    result.add(initial);
    while (result.get(result.size() - 1)[0] > 1) {
        int[] lastPartition = result.get(result.size() - 1);
        int posOfLastNotOne = 0;
        for(int k = lastPartition.length - 1; k >= 0; k--) {
            if (lastPartition[k] > 1) {
                posOfLastNotOne = k;
                break;
            }
        }
        int[] newPartition = new int[n];
        for (int j = posOfLastNotOne + 1; j < lastPartition.length; j++) {
            if (lastPartition[posOfLastNotOne] - 1 > lastPartition[j]) {
                System.arraycopy(lastPartition, 0, newPartition, 0, lastPartition.length);
                newPartition[posOfLastNotOne]--;
                newPartition[j]++;
                result.add(newPartition);
                break;
            }
        }
    }
    return result;
}

Java-Implementierung. Könnte von memoization profitieren.

public class Partition {

    /**
     * partition returns a list of int[] that represent all distinct partitions of n.
     */
    public static List<int[]> partition(int n) {
        List<Integer> partial = new ArrayList<Integer>();
        List<int[]> partitions = new ArrayList<int[]>();
        partition(n, partial, partitions);
        return partitions;
    }

    /**
     * If n=0, it copies the partial solution into the list of complete solutions.
     * Else, for all values i less than or equal to n, put i in the partial solution and partition the remainder n-i.
     */
    private static void partition(int n, List<Integer> partial, List<int[]> partitions) {
        //System.out.println("partition " + n + ", partial solution: " + partial);
        if (n == 0) {
            // Complete solution is held in 'partial' --> add it to list of solutions
            partitions.add(toArray(partial));
        } else {
            // Iterate through all numbers i less than n.
            // Avoid duplicate solutions by ensuring that the partial array is always non-increasing
            for (int i=n; i>0; i--) {
                if (partial.isEmpty() || partial.get(partial.size()-1) >= i) {
                    partial.add(i);
                    partition(n-i, partial, partitions);
                    partial.remove(partial.size()-1);
                }
            }
        }
    }

    /**
     * Helper method: creates a new integer array and copies the contents of the list into the array.
     */
    private static int[] toArray(List<Integer> list) {
        int i = 0;
        int[] arr = new int[list.size()];
        for (int val : list) {
            arr[i++] = val;
        }
        return arr;
    }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top