2009-08-21 163 views
1

我正在創建一個預測應用程序,該程序將運行生產工廠能夠運行的各種「模式」的模擬。該工廠每天可以運行一種模式,因此我正在編寫一個功能,將每日選擇的不同模式相加,最大限度地提高工廠的產量,並與所提供的銷售預測數字保持最佳匹配。這些數據將被加載到一個模式對象的數組中,然後用於計算工廠的預測輸出。幫助創建遞歸函數C#

我已經創建了這個功能,但是,我需要使它們遞歸,以便能夠處理模式和工作日(根據生產需要而變化)的任何數量(合理範圍內)。下面列出的是我使用for循環來模擬我想要做什麼的代碼。有人能指出我正確的方向,以創建一個遞歸函數來取代多個for循環的需要嗎?

其中GetNumbers4方法將有四種模式,而GetNumbers5將有5種模式。詮釋開始將是工作日的數量。

private static void GetNumber4(int start) 
    { 
     int count = 0; 
     int count1 = 0;   

     for (int i = 0; 0 <= start; i++) 
     { 
      for (int j = 0; j <= i; j++) 
      { 

       for (int k = 0; k <= j; k++) 
       { 
        count++; 

        for (int l = 0; l <= i; l++) 
        { 
         count1 = l; 
        } 

        Console.WriteLine(start + " " + (count1 - j) + " " + (j - k) + " " + k); 
        count1 = 0; 
       } 

      } 
      start--; 

     } 
     Console.WriteLine(count); 

    } 

    private static void GetNumber5(int start) 
    { 
     int count = 0; 
     int count1 = 0; 

     for (int i = 0; 0 <= start; i++) 
     { 
      for (int j = 0; j <= i; j++) 
      { 

       for (int k = 0; k <= j; k++) 
       { 

        for (int l = 0; l <= k; l++) 
        { 
         count++; 
         for (int m = 0; m <= i; m++) 
         { 
          count1 = m; 
         } 
         Console.WriteLine(start + " " + (count1 - j) + " " + (j - k) + " " + (k - l) + " " + l); 
         count1 = 0; 
        } 

       } 

      } 
      start--; 

     } 
     Console.WriteLine(count); 

    } 

編輯:

我認爲,這將是更有幫助,如果我給什麼,我試圖做一個例子。例如,如果一個工廠可以以「A」,「B」,「C」三種模式運行並且有三個工作日,那麼代碼將返回以下結果。

3 0 0 
2 1 0 
2 0 0 
1 2 0 
1 1 1 
1 0 2 
0 3 0 
0 2 1 
0 1 2 
0 0 3 

的一系列數字表示的三種模式A B C.我將這些結果加載到具有相應的生產速率的模式的對象。這樣做可以讓我快速創建每種可能組合的列表;它反而給我一個發生的頻率。

基於已經提供的解決方案之一,我想要做這樣的事情。

//Where Modes is a custom classs 
    private static Modes GetNumberRecur(int start, int numberOfModes) 
    { 
     if (start < 0) 
     { 
      return Modes; 

     } 

     //Do work here 
     GetNumberRecur(start - 1); 
    } 

感謝大家誰已經提供了輸入。

+2

你永遠不需要*遞歸函數。任何你可以遞歸地做的事情都可以迭代地完成,有些問題只適用於遞歸,就像遍歷一個文件系統一樣。 – 2009-08-21 20:06:46

+0

爲什麼不算1做任何事情? – Jimmy 2009-08-21 20:10:54

+0

Count1用於將最內循環的結果傳遞給循環外的console.writeline。 – 2009-08-21 20:19:37

回答

6

調用GetNumber(5,X)應產生相同的結果GetNumber5( X):

static void GetNumber(int num, int max) { 
    Console.WriteLine(GetNumber(num, max, "")); 
} 
static int GetNumber(int num, int max, string prefix) { 
    if (num < 2) { 
     Console.WriteLine(prefix + max); 
     return 1; 
    } 
    else { 
     int count = 0; 
     for (int i = max; i >= 0; i--) 
      count += GetNumber(num - 1, max - i, prefix + i + " "); 
     return count; 
    } 
} 
+0

你絕對釘上它。非常感謝。 – 2009-08-21 20:54:18

+0

說實話,一個更好的解決方案可能會涉及GetNumber產生一個int []或者其他東西的迭代器,所以你的其他組件可以直接獲取這些值。 – Jimmy 2009-08-21 21:25:32

0

我以前提供了一個簡單的C#遞歸函數here。 最頂級的功能最終得到每個排列的副本,所以它應該很容易適應您的需求。

4

遞歸函數只需要終止條件。在你的情況,這似乎是在start小於0:

private static void GetNumberRec(int start) 
{ 
    if(start < 0) 
    return; 

    // Do stuff 

    // Recurse 
    GetNumberRec(start-1); 
} 
1

我已經重構你的榜樣這個:

private static void GetNumber5(int start) 
{ 
    var count = 0; 

    for (var i = 0; i <= start; i++) 
    { 
     for (var j = 0; j <= i; j++) 
     { 
      for (var k = 0; k <= j; k++) 
      { 
       for (var l = 0; l <= k; l++) 
       { 
        count++; 

        Console.WriteLine(
         (start - i) + " " + 
         (i - j) + " " + 
         (j - k) + " " + 
         (k - l) + " " + 
         l); 
       } 
      } 
     } 
    } 

    Console.WriteLine(count); 
} 

請確認這是正確的。然後

遞歸版本應該是這樣的:

public static void GetNumber(int start, int depth) 
{ 
    var count = GetNumber(start, depth, new Stack<int>()); 
    Console.WriteLine(count); 
} 

private static int GetNumber(int start, int depth, Stack<int> counters) 
{ 
    if (depth == 0) 
    { 
     Console.WriteLine(FormatCounters(counters)); 
     return 1; 
    } 
    else 
    { 
     var count = 0; 
     for (int i = 0; i <= start; i++) 
     { 
      counters.Push(i); 
      count += GetNumber(i, depth - 1, counters); 
      counters.Pop(); 
     } 
     return count; 
    } 
} 

FormatCounters留給讀者作爲練習到讀取器)

0

我意識到,每個人都打了我一記重拳,在這一點上,但這裏有一個愚蠢的Java算法(很接近C#語法ŧ帽子,你可以嘗試)。

import java.util.ArrayList; 
import java.util.List; 

/** 
* The operational complexity of this is pretty poor and I'm sure you'll be able to optimize 
* it, but here's something to get you started at least. 
*/ 
public class Recurse 
{ 
    /** 
    * Base method to set up your recursion and get it started 
    * 
    * @param start The total number that digits from all the days will sum up to 
    * @param days The number of days to split the "start" value across (e.g. 5 days equals 
    * 5 columns of output) 
    */ 
    private static void getNumber(int start,int days) 
    { 
     //start recursing 
     printOrderings(start,days,new ArrayList<Integer>(start)); 
    } 

    /** 
    * So this is a pretty dumb recursion. I stole code from a string permutation algorithm that I wrote awhile back. So the 
    * basic idea to begin with was if you had the string "abc", you wanted to print out all the possible permutations of doing that 
    * ("abc","acb","bac","bca","cab","cba"). So you could view your problem in a similar fashion...if "start" is equal to "5" and 
    * days is equal to "4" then that means you're looking for all the possible permutations of (0,1,2,3,4,5) that fit into 4 columns. You have 
    * the extra restriction that when you find a permutation that works, the digits in the permutation must add up to "start" (so for instance 
    * [0,0,3,2] is cool, but [0,1,3,3] is not). You can begin to see why this is a dumb algorithm because it currently just considers all 
    * available permutations and keeps the ones that add up to "start". If you want to optimize it more, you could keep a running "sum" of 
    * the current contents of the list and either break your loop when it's greater than "start". 
    * 
    * Essentially the way you get all the permutations is to have the recursion choose a new digit at each level until you have a full 
    * string (or a value for each "day" in your case). It's just like nesting for loops, but the for loop actually only gets written 
    * once because the nesting is done by each subsequent call to the recursive function. 
    * 
    * @param start The total number that digits from all the days will sum up to 
    * @param days The number of days to split the "start" value across (e.g. 5 days equals 
    * 5 columns of output) 
    * @param chosen The current permutation at any point in time, may contain between 0 and "days" numbers. 
    */ 
    private static void printOrderings(int start,int days,List<Integer> chosen) 
    { 
     if(chosen.size() == days) 
     { 
      int sum = 0; 
      for(Integer i : chosen) 
      { 
       sum += i.intValue(); 
      } 

      if(sum == start) 
      { 
       System.out.println(chosen.toString()); 
      } 
      return; 
     } 
     else if(chosen.size() < days) 
     { 
      for(int i=0; i < start; i++) 
      { 
       if(chosen.size() >= days) 
       { 
        break; 
       } 

       List<Integer> newChosen = new ArrayList<Integer>(chosen); 
       newChosen.add(i); 
       printOrderings(start,days,newChosen); 
      } 
     } 
    } 

    public static void main(final String[] args) 
    { 
     //your equivalent of GetNumber4(5) 
     getNumber(5,4); 

     //your equivalent of GetNumber5(5) 
     getNumber(5,5); 
    } 
}