C# 列举所有可能的抽奖,掷N个X面骰子
给定n个骰子,每个骰子有x个边,我试图列举所有可能的结果。 例如,如果我们有10个三面骰子,它们都落在值1的一侧:C# 列举所有可能的抽奖,掷N个X面骰子,c#,combinatorics,dice,C#,Combinatorics,Dice,给定n个骰子,每个骰子有x个边,我试图列举所有可能的结果。 例如,如果我们有10个三面骰子,它们都落在值1的一侧: 10 00 00 我有一个启发式方法(EnumerateDrawsH),它只适用于预定义的边数,还有一个递归方法(EnumerateDrawsR),它适用于任意边数。目前,我的R版本的性能远不如H。 在R中,我需要一种有效的方法来跟踪正在写入的绘图中的骰子总数,它应该是sum参数,但它是不正确的。我找到的唯一解决方法是在递归的每个级别上重做总计,并将其存储在drawSum局部变量中
10 00 00
我有一个启发式方法(EnumerateDrawsH),它只适用于预定义的边数,还有一个递归方法(EnumerateDrawsR),它适用于任意边数。目前,我的R版本的性能远不如H。 在R中,我需要一种有效的方法来跟踪正在写入的绘图中的骰子总数,它应该是sum参数,但它是不正确的。我找到的唯一解决方法是在递归的每个级别上重做总计,并将其存储在drawSum局部变量中,该变量经过测试以结束递归。有人知道如何在sum参数中获得正确的值吗
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());
}
}
使用系统;
使用系统诊断;
使用System.IO;
使用系统文本;
静态类枚举SDraws
{
公共静态无效运行()
{
int=10;
秒表;
watch=Stopwatch.StartNew();
EnumerateDicesDraws.EnumeratedDrawsh(nbDices);
看,停;
控制台写入线(手表已过);
watch=Stopwatch.StartNew();
EnumerateDicesDraws.EnumeratedDrawsr(nbDices,3);
看,停;
控制台写入线(手表已过);
Console.ReadKey(true);
}
公共静态空值(int-nbdice,int-nbSides)
{
string filePath=Path.Combine(Path.GetTempPath(),string.Concat(“Draws[R]”,nbDices,.txt”);
int[]骰子=新的int[nbSides];
使用(StreamWriter=newstreamwriter(filePath,false))
枚举drawsr(0,0,nbDices,dices,writer);
}
私有静态void枚举drawsr(int索引、int总和、int nbDices、int[]dices、StreamWriter)
{
int drawSum=0;
for(int i=0;i 对于(骰子[0]=0;骰子[0],您可以通过使用n
数字在基数x
中计数来枚举所有绘图
例如,将数组中的所有骰子初始化为最低值,假设骰子标记为从0
到(x-1)
。然后,从一个0
数组开始,这是第一个也是最低的值,然后可以增加最右边的值,直到它等于x-1
,增加第二个骰子,重置第一个骰子,然后继续,等等
计算机编程的艺术第4卷:如果你需要按一定顺序列举,第3分册中有大量的信息。试着把它想象成3^10个选项。这很容易计算。然后只需数到该值(减1)并将每个值转换为基数3。每个基数3的数字都是一个骰子的值(减1)。(你有备用的三面模具吗?我很难想象。)
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;
}
}
}