2008-12-30 28 views
28

我需要一個算法來生成一個正數的所有可能的分區,我想出了一個(作爲答案張貼),但它是指數時間。生成一個數字的分區

該算法應該返回所有可能的方式,一個數字可以表示爲小於或等於自身的正數的總和。因此,例如,對於數,其結果將是:

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

所以我的問題是:是否有更高效的算法呢?

編輯:問題題爲「一些和分解」,因爲我真的不知道這是什麼叫。 ShreevatsaR pointed out他們被稱爲「分區」,所以我相應地編輯了問題標題。

+1

只是好奇:是一個理論問題(這是好的)還是它有實際用途? – PhiLho 2008-12-30 17:02:21

+0

它確實對我有用。我需要生成一個數字N的所有分區。每個分區對應不同的分佈,因此我試圖最大化一個不同的「覆蓋」值。 – 2008-12-30 17:13:09

+1

如果你只是在尋找分區的數量而不是特定的公式,那麼就有一個封閉的解決方案。 – AlexQueue 2011-09-08 15:14:02

回答

17

這裏是我的解決方案(指數時間)在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]] 
6

當你問到更高效的算法時,我不知道要比較哪一個。但這裏是一個用直接的方式(二郎)一種算法:

-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(_, _) -> []. 

它是在時間(同Can Berk Güder's solution in Python)指數和線性的堆棧空間。但是使用相同的技巧,記憶,你可以通過節省內存和減少指數來實現重大改進。 (它比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]). 

無論如何,你應該爲你的語言和目的設定基準。

-1

Java實現。可以從記憶中受益。

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

這裏做的更囉嗦的方式(這是我做之前,我知道了「分區」一詞,這使我做了谷歌搜索):

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

下面是使用我在Haskell中編寫的paramorphisms的一個解決方案。

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 

這絕對不是最有效的,但我認爲它非常優雅,它的確具有啓發性。