2010-04-07 80 views
1

我試圖想出一個算法,將打印出所有可能的方法來總結N個整數,以便他們總計一個給定的值。打印所有方法來總計n個整數,以便它們總計給定的總和。

例子。打印所有的方式來總結的4個整數,使他們總結爲5。

結果應該是這樣的:

5 0 0 0 
4 1 0 0 
3 2 0 0 
3 1 1 0 
2 3 0 0 
2 2 1 0 
2 1 2 0 
2 1 1 1 
1 4 0 0 
1 3 1 0 
1 2 2 0 
1 2 1 1 
1 1 3 0 
1 1 2 1 
1 1 1 2 
+3

是它的功課? – Jack 2010-04-07 15:36:36

+0

-1:你嘗試過什麼?如果您承認問題與家庭作業有關,並且您已經做出了誠意嘗試自己先解決問題,則家庭作業問題是可以接受的。 http://meta.stackexchange.com/questions/10811/how-to-ask-and-answer-homework-questions/10812#10812 – 2010-04-07 15:47:57

+4

只有正整數?因爲使用負數,這將是無限的... – 2010-04-07 15:48:21

回答

2

這是基於Alinium的代碼。
我修改了它以便打印出所有可能的組合,因爲他已經完成了所有的排列組合。
此外,我不認爲你需要for循環時,n = 1,因爲在這種情況下,只有一個數字應該導致總和等於價值。
各種其他修改,以獲得邊界案件的工作。

def sum(n, value): 
    arr = [0]*n # create an array of size n, filled with zeroes 
    sumRecursive(n, value, 0, n, arr); 

def sumRecursive(n, value, sumSoFar, topLevel, arr): 
    if n == 1: 
     if sumSoFar <= value: 
      #Make sure it's in ascending order (or only level) 
      if topLevel == 1 or (value - sumSoFar >= arr[-2]): 
       arr[(-1)] = value - sumSoFar #put it in the n_th last index of arr 
       print arr 
    elif n > 0: 
     #Make sure it's in ascending order 
     start = 0 
     if (n != topLevel): 
      start = arr[(-1*n)-1] #the value before this element 

     for i in range(start, value+1): # i = start...value 
      arr[(-1*n)] = i # put i in the n_th last index of arr 
      sumRecursive(n-1, value, sumSoFar + i, topLevel, arr) 

乳寧款項(4,5)返回:
[0,0,0,5]
[0,0,1,4]
[0,0,2,3]
[0,1,1,3]
[1,1,1,2]

+0

你說什麼它返回不是所有的方法來總結4個整數加起來5 .....或者我不明白你寫了什麼? – noghead 2010-04-07 19:10:46

+0

從他們提到的只打印'組合'。因此,這裏[1,1,1,2]被視爲等價於[1,1,2,1]和[1,2,1,1]和[2,1,1,1],所以只返回一個而不是全部四個。 – FromCanada 2010-04-07 20:25:27

+0

我明白了。但是[0,1,2,2]呢? muddybruin是否忘記把它當作組合中的一個,或者他的解決方案是否不會返回它?尚未有機會運行他的解決方案。 – noghead 2010-04-07 20:57:25

0

我沒有測試過這一點:

 
    procedure allSum (int tot, int n, int desiredTotal) return int 
     if n > 0 
      int i = 
      for (int i = tot; i>=0; i--) { 
       push i onto stack; 
       allSum(tot-i, n-1, desiredTotal); 
       pop top of stack 
      } 
     else if n==0 
      if stack sums to desiredTotal then print the stack end if 
     end if 

我肯定有更好的方法來做到這一點。

+0

p>在i> = p中引用了什麼? – 2010-04-09 13:09:36

+0

「p」應該是零(0)。他們在鍵盤上彼此相鄰...... – redcayuga 2010-04-09 13:34:22

1

解決這個問題的關鍵是遞歸。這是一個在python中的工作實現。它打印出所有可能的排列總和。你可能想要擺脫重複的組合,可能通過使用一些Set或hashing機制來濾除它們。

def sum(n, value): 
    arr = [0]*n # create an array of size n, filled with zeroes 
    sumRecursive(n, value, 0, n, arr); 

def sumRecursive(n, value, sumSoFar, topLevel, arr): 
    if n == 1: 
     if sumSoFar > value: 
      return False 
     else: 
      for i in range(value+1): # i = 0...value 
       if (sumSoFar + i) == value: 
        arr[(-1*n)] = i # put i in the n_th last index of arr 
        print arr; 
        return True 

    else: 
     for i in range(value+1): # i = 0...value 
      arr[(-1*n)] = i # put i in the n_th last index of arr 
      if sumRecursive(n-1, value, sumSoFar + i, topLevel, arr): 
       if (n == topLevel): 
        print "\n" 

經過一些額外的努力,這可能會被簡化爲擺脫我傳遞給遞歸函數的一些參數。正如redcayuga的僞代碼所暗示的那樣,使用堆棧而不是手動管理數組,將是一個更好的主意。

+0

謝謝,我會在下班時嘗試您的解決方案。 – noghead 2010-04-07 19:07:29

2

在純數學,求和整數以獲得給定的總的一種方式被稱爲分區。如果你是谷歌的「整數分區」,有很多信息。您正在尋找具有特定數量的元素的整數分區。我相信你可以採用其中一種已知的生成機制並適應這種額外的情況。維基百科對主題Partition_(number_theory)有很好的概述。 Mathematica甚至有一個功能可以做你想做的事情:IntegerPartitions[5, 4]

+0

謝謝,我沒有意識到這被稱爲整數分區。 – noghead 2010-04-09 13:56:57

+0

matematica中的代碼只是IntegerPartitions [yourDesiredResut,numberOfIntegersInSum,yourSet]。很好,很乾淨 – 2010-06-05 17:14:09

0

我發現基於Alinium代碼

class Domain_partition 

    attr_reader :results, 
       :domain, 
       :sum, 
       :size 

    def initialize(_dom, _size, _sum) 
     _dom.is_a?(Array) ? @domain=_dom.sort : @domain= _dom.to_a 
     @results, @sum, @size = [], _sum, _size 
     arr = [0]*size # create an array of size n, filled with zeroes 
     sumRecursive(size, 0, arr) 
    end 

    def sumRecursive(n, sumSoFar, arr) 

     if n == 1 
      #Make sure it's in ascending order (or only level) 
      if sum - sumSoFar >= arr[-2] and @domain.include?(sum - sumSoFar) 
       final_arr=Array.new(arr) 
       final_arr[(-1)] = sum - sumSoFar #put it in the n_th last index of arr 
       @results<<final_arr 
      end 

     elsif n > 1 

      #********* dom_selector ******** 

      n != size ? start = arr[(-1*n)-1] : start = domain[0] 
      dom_bounds=(start*(n-1)..domain.last*(n-1)) 

      restricted_dom=domain.select do |x| 

       if x < start 
        false; next 
       end 

       if size-n > 0 
        if dom_bounds.cover? sum-(arr.first(size-n).inject(:+)+x) then true 
        else false end 
       else 
        dom_bounds.cover?(sum+x) ? true : false 
       end 
      end # *************************** 

      for i in restricted_dom 
       _arr=Array.new(arr) 
       _arr[(-1*n)] = i 
       sumRecursive(n-1, sumSoFar + i, _arr) 
      end 
     end 
    end 
end 

a=Domain_partition.new (-6..6),10,0 
p a 

b=Domain_partition.new [-4,-2,-1,1,2,3],10,0 
p b 
0

如果你有興趣的產生與域說明紅寶石的方式(詞彙)下令整數分區,即獨特的無序組Si的正整數(無0的)總和爲N,然後嘗試以下。 (無序簡單地意味着[1,2,1]和[1,1,2]是相同的分區)

該問題不需要遞歸,因爲查找下一個詞法限制分區其實很簡單...

概念:從最後一個加數(整數)開始,查找兩個加數之間的差值大於1的第一個實例。在此處將分割分爲兩部分。從較高的整數(這將是一個部分中的最後一個整數)中刪除1,並將較低的整數(後一部分的第一個整數)加1。然後找到後面部分的第一個詞法順序分區,它將新的最大整數作爲最大加數值。我使用Sage找到第一個詞法分區,因爲它的速度很快,但沒有它就很容易完成。最後,加入這兩部分,瞧!你有N個S部分的下一個詞法分區。

例如[6,5,3,2,2] - > [6,5],[3,2,2] - > [6,4],[4,2,2] - > [6,4],[ 4,3,1] - > [6,4,4,3,1]

因此,在Python和調用賢者的小任務,找到第一個詞法分區給定n和s部分...

from sage.all import * 

def most_even_partition(n,s): # The main function will need to recognize the most even partition possible (i.e. last lexical partition) so it can loop back to the first lexical partition if need be 
    most_even = [int(floor(float(n)/float(s)))]*s 
    _remainder = int(n%s) 

    j = 0 
    while _remainder > 0: 
     most_even[j] += 1 
     _remainder -= 1 
     j += 1 
    return most_even 

def portion(alist, indices): 
    return [alist[i:j] for i, j in zip([0]+indices, indices+[None])] 


def next_restricted_part(p,n,s): 
    if p == most_even_partition(n,s):return Partitions(n,length=s).first() 

    for i in enumerate(reversed(p)): 
     if i[1] - p[-1] > 1: 
      if i[0] == (s-1): 
       return Partitions(n,length=s,max_part=(i[1]-1)).first() 
      else: 
       parts = portion(p,[s-i[0]-1]) # split p (soup?) 
       h1 = parts[0] 
       h2 = parts[1] 
       next = list(Partitions(sum(h2),length=len(h2),max_part=(h2[0]-1)).first()) 
       return h1+next 

如果你想要零(不是實際的整數分區),那麼這些函數只需要很小的修改。

0

試試看看這個代碼。我希望它更容易理解。我測試了它,它產生了正確的序列。

void partition(int n, int m = 0) 
{ 
    int i; 
    // if the partition is done 
    if(n == 0){ 
     // Output the result 
     for(i = 0; i < m; ++i) 
      printf("%d ", list[i]); 
     printf("\n"); 
     return; 
    } 
    // Do the split from large to small int 
    for(i = n; i > 0; --i){ 
     // if the number not partitioned or 
     // willbe partitioned no larger than 
     // previous partition number 
     if(m == 0 || i <= list[m - 1]){ 
      // store the partition int 
      list[m] = i; 
      // partition the rest 
      partition(n - i, m + 1); 
     } 
    } 
} 

要求澄清,如果需要的話。

的是一個輸出

6 
5 1 
4 2 
4 1 1 
3 3 
3 2 1 
3 1 1 1 
2 2 2 
2 2 1 1 
2 1 1 1 1 
1 1 1 1 1 1 


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