2013-01-11 3 views
0

각 n 개의 오지에 x면이있는 것을 감안할 때, 저는 모든 가능성을 그립니다. 예를 들어, 10 면체가 있고 모두 1면에 값이있는 경우 :
미리 정의 된면 수에 대해서만 작동하는 경험적 방법 (EnumerateDrawsH)이 있습니다. 임의의 수의 측면에서 작동하는 재귀 적 메서드 (EnumerateDrawsR) 현재로서는 R의 버전이 H보다 성능이 훨씬 낮습니다. R에서 쓰는 추첨의 총 개수를 추적하는 효율적인 방법이 필요합니다. 이는 합계 매개 변수 여야하지만 올바르지 않습니다. . 내가 발견 한 유일한 해결 방법은 재귀의 각 수준에서 총계를 다시 실행하고 재귀를 끝내기 위해 테스트되는 drawSum 로컬 변수에 저장하는 것입니다. 누구든지 합계 매개 변수에서 올바른 값을 얻는 방법을 알고 있습니까?N 개의 X 직면 오지를 N 개 던져 가능한 모든 무승부를 열거하십시오.

using System; 
using System.Diagnostics; 
using System.IO; 
using System.Text; 

static class EnumerateDicesDraws 
{ 
    public static void Run() 
    { 
    int nbDices = 10; 
    Stopwatch watch; 

    watch = Stopwatch.StartNew(); 
    EnumerateDicesDraws.EnumerateDrawsH(nbDices); 
    watch.Stop(); 
    Console.WriteLine(watch.Elapsed); 

    watch = Stopwatch.StartNew(); 
    EnumerateDicesDraws.EnumerateDrawsR(nbDices, 3); 
    watch.Stop(); 
    Console.WriteLine(watch.Elapsed); 

    Console.ReadKey(true); 
    } 

    public static void EnumerateDrawsR(int nbDices, int nbSides) 
    { 
    string filePath = Path.Combine(Path.GetTempPath(), string.Concat("Draws[R]", nbDices, ".txt")); 
    int[] dices = new int[nbSides]; 
    using (StreamWriter writer = new StreamWriter(filePath, false)) 
     EnumerateDrawsR(0, 0, nbDices, dices, writer); 
    } 
    private static void EnumerateDrawsR(int index, int sum, int nbDices, int[] dices, StreamWriter writer) 
    { 
    int drawSum = 0; 
    for (int i = 0; i < dices.Length; i++) 
    { 
     drawSum += dices[i]; 
     if (drawSum == nbDices) 
     { 
     for (int j = 0; j < dices.Length - 1; j++) 
      writer.Write(string.Format("{0:D2} ", dices[j])); 
     writer.Write(string.Format("{0:D2} ", dices[dices.Length - 1])); 
     writer.Write(string.Format("{0:D3}", sum)); 
     writer.WriteLine(); 
     for (; index < dices.Length; index++) 
      dices[index] = 0; 
     return; 
     } 
    } 
    /* 
    if (sum == nbDices) 
    { 
     for (int j = 0; j < dices.Length; j++) 
      writer.Write(string.Format("{0:D2} ", dices[j], j < dices.Length - 1 ? " " : null)); 
     writer.WriteLine(); 
     for (; index < dices.Length; index++) 
      dices[index] = 0; 
     return; 
    } 
     */ 
    if (index < dices.Length) 
    { 
     EnumerateDrawsR(index + 1, sum + dices[index], nbDices, dices, writer); 
     EnumerateDrawsR(index, sum + ++dices[index], nbDices, dices, writer); 
    } 
    } 
    public static void EnumerateDrawsH(int nbDices) 
    { 
    StringBuilder draws = new StringBuilder(); 
    string format = "{0:D2} {1:D2} {2:D2}\r\n"; 
    int cumul = 0; 
    int[] dices = new int[3]; 
    for (dices[0] = 0; dices[0] <= nbDices; dices[0]++) 
    { 
     cumul = dices[0]; 
     if (cumul == nbDices) 
     { 
     for (int i = 1; i < dices.Length; i++) 
      dices[i] = 0; 
     draws.AppendFormat(format, dices[0], dices[1], dices[2]); 
     break; 
     } 
     for (dices[1] = 0; dices[1] <= nbDices; dices[1]++) 
     { 
     cumul = dices[0] + dices[1]; 
     if (cumul == nbDices) 
     { 
      for (int i = 2; i < dices.Length; i++) 
      dices[i] = 0; 
      draws.AppendFormat(format, dices[0], dices[1], dices[2]); 
      break; 
     } 
     for (dices[2] = 0; dices[2] <= nbDices; dices[2]++) 
     { 
      cumul = dices[0] + dices[1] + dices[2]; 
      if (cumul == nbDices) 
      { 
      draws.AppendFormat(format, dices[0], dices[1], dices[2]); 
      break; 
      } 
     } 
     } 
    } 
    File.WriteAllText(Path.Combine(Path.GetTempPath(), string.Concat("Draws[H]", nbDices, ".txt")), draws.ToString()); 
    } 
} 

편집

그녀는 고정 재귀 종료 조건 nlucaroni가 제시 한 솔루션입니다.

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.IO; 
using System.Linq; 

static class Programm 
{ 
    static void Main() 
    { 
    var watch = Stopwatch.StartNew(); 
    var outputFile = DiceRoll.Draws(nbDicesTotal: 10, nbValues: 6); 
    watch.Stop(); 
    Console.WriteLine(watch.Elapsed); 

    Console.WriteLine(); 

    Console.WriteLine("Output to :"); 
    Console.WriteLine(outputFile); 

    Console.ReadKey(true); 
    } 
} 

static class DiceRoll 
{ 
    /// <summary> 
    /// Writes all possible draws (or distributions) for a given number of dices having a given number of values 
    /// </summary> 
    /// <param name="nbDicesTotal">Total number of dices rolled</param> 
    /// <param name="nbValues">Number of different values of each dice</param> 
    /// <returns>Output file path in system temporary folder</returns> 
    public static string Draws(int nbDicesTotal, int nbValues) 
    { 
    var filePath = Path.Combine(Path.GetTempPath(), string.Format("DiceDraws[{0}].txt", nbDicesTotal)); 
    var distribution = new int[nbValues]; 
    using (var writer = new StreamWriter(filePath, false)) 
     foreach (var draw in Draws(0, 0, nbDicesTotal, distribution)) 
     //Call to Select method adds huge cost of performance. Remove call to Select to compare. 
     writer.WriteLine(string.Join(" ", draw.Select(x => x.ToString("D2")))); 
    return filePath; 
    } 

    /// <summary> 
    /// Returns all possible draws (or distributions) for a given number of dices having a given number of values 
    /// </summary> 
    /// <param name="distributionIndex">We are incrementing the number of dices of this value</param> 
    /// <param name="nbAddedDices">Number of dices (up to nbDicesTotal) considered in the recursion. EXIT CONDITION OF THE RECURSION</param> 
    /// <param name="nbDicesTotal">Total number of dices rolled</param> 
    /// <param name="distribution">Distribution of dice values in a draw. Index is dice value. Value is the number of dices of value 'index'</param> 
    /// <returns>All possible draws</returns> 
    /// <remarks>Recursive methode. Should not be called directly, only from the other overload</remarks> 
    static IEnumerable<IEnumerable<int>> Draws(int distributionIndex, 
              int nbAddedDices, 
              int nbDicesTotal, 
              int[] distribution) 
    { 
    // Uncomment for exactness check 
    // System.Diagnostics.Debug.Assert(distribution.Sum() == nbAddedDices); 

    if (nbAddedDices == nbDicesTotal) 
    { 
     yield return distribution; 

     nbAddedDices -= distribution.Length - distributionIndex; 
     for (; distributionIndex < distribution.Length; distributionIndex++) 
     distribution[distributionIndex] = 0; 
    } 

    if (distributionIndex < distribution.Length) 
    { 
     foreach (var draw in Draws(distributionIndex + 1, nbAddedDices, nbDicesTotal, distribution)) 
     yield return draw; 

     distribution[distributionIndex]++; 

     foreach (var draw in Draws(distributionIndex, nbAddedDices + 1, nbDicesTotal, distribution)) 
     yield return draw; 
    } 
    } 
} 
+1

3^10 선택으로 생각해보십시오. 그것은 계산하기 쉽습니다. 그런 다음이 값 (1을 뺀 값)까지 계산하고 각 값을 기준 3으로 변환합니다. 각 기준 3 자리는 다이 값 (1을 뺀 값)입니다. (여분의 3 면체 다이가 있습니까? 그것을 그리는 데 어려움이 있습니다.) – HABO

답변

1

당신은 모두를 열거 할 수는 n 자리 기본 x에서 계산하여 그립니다.

예를 들어, 배열의 모든 주사위를 최저 값으로 시작하십시오. 주사위가 0에서 (x-1)까지라고 가정 해 봅시다. 그런 다음 0의 배열로 시작합니다.이 값은 첫 번째 값과 최저값이며, 두 번째 주사위를 높이고 첫 번째 주사위를 재설정하고 진행하는 x-1과 같아 질 때까지 오른쪽에서 가장 큰 값을 늘릴 수 있습니다. .

컴퓨터 프로그래밍의 요지 4 : Fascicle 3에는 특정 순서로 열거해야하는 정보가 있습니다.