我正在尝试获取总计为28的数字(这只是一个示例,但是可以更改目标数字)。下面的代码大约需要4-5分钟。有没有一种方法可以使总和为“ 28”的数字组合更快一些 输出示例 0,0,0,0,1,1,1,2,2,3,3,3
private void Button1_Click(object sender, EventArgs e)
{
int up = 28;
int rows = 12;
int[] chosen = new int[rows + 1];
CalculateCombination(chosen, 0, rows, 0, up - 1);
}
public void CalculateCombination(int[] chosen, int index, int r, int start, int end)
{
if (index == r)
{
return;
}
for (int i = start; i <= end; i++)
{
chosen[index] = i;
CalculateCombination(chosen, index + 1, r, i, end);
if (chosen.Sum() + chosen.Length == 28)
{
var ch = string.Join(",", chosen.Take(chosen.Length - 1));
Debug.Write(ch);
Debug.WriteLine("\t");
}
}
return;
}
答案 0 :(得分:3)
首先,我假设您正在执行作业。 您需要引用可以帮助您的资源,而不是在学术背景下冒充我的工作,因此请确保在您获得任务帮助的文档中进行记录。不要只是复制解决方案并声明为您自己的解决方案。
更新:我从描述中误解了这个问题;我理解问题是要找到长度为12的单调非递减序列。问题已由原始发帖人在评论中阐明;该问题已得到解决。问题是要找到给定长度的所有非负序列,例如12,它们加起来等于给定值,例如28。有1676056044这样的序列,枚举它们都将花费一些时间。以下答案是针对我最初理解的问题。
是的,与您在此处所做的相比,可以更有效,更明确地解决此问题。让我们分解一下。
首先,每个递归问题都有一个或多个基本案例。这些情况是如此简单,以至于无法将其简化为一个更简单的问题。让我们做一个入口点,然后列出基本案例。我们想要什么?我们想要给定长度的所有序列,从最小到最大的顺序是这样,以使序列求和为特定值。
public static IEnumerable<IEnumerable<int>> SumsTo(int sum, int least, int length)
{
好的,基本情况是什么?
我们可以轻松实现以下规则:
if (length == 0)
{
if (sum == 0)
yield return Enumerable.Empty<int>();
yield break;
}
好的,处理长度为零。长度非零呢?那里有基本情况吗?是。如果least * length
大于sum
,那么将没有任何解决方案。
if (least * length > sum)
yield break;
好的。我们知道长度至少为1,并且肯定有解决方案。有什么解决方案?好吧,序列中必须有第一个数字,并且它可以是最小和总和之间的任何值,所以让我们编写一个循环。
for (int current = least; current <= sum; current += 1)
{
给定current
的解决方案是什么?它是current
,后跟一个大小为length-1
的序列,其中最小项为current
或更大,总计为sum - current
。但是我们可以递归地计算出来!
var head = Enumerable.Repeat(current, 1);
foreach(var tail in SumsTo(sum - current, current, length - 1))
yield return head.Concat(tail);
}
}
我们完成了:
Console.WriteLine(SumsTo(28, 0, 12).Count());
按预期方式打印3036,并花费一秒钟的时间;有3036种方法将12个数字从0到28到28求和。
锻炼:将类型Sum
设置为(1)为空,或(2)整数,称为head,后跟Sum
(为空或仅由较大的元素组成)称为尾巴。重写解决方案,使其返回IEnumerable<Sum>
而不是IEnumerable<IEnumerable<int>>
。
锻炼:正确设置Sum
类型的工具IEnumerable<int>
。
锻炼:我们一遍又一遍地产生一些款项;例如,在3036个长度为12的解决方案中,总和为28个问题,其中超过100个以“ 5,5”结尾,因此我们多次分配该对象。记住您的解决方案,以便在给定相同的参数时返回相同的Sum
对象。
锻炼:使用此备忘的解决方案计算您保存的大致计算量;与您使用的内存量进行对比。内存使用率上升还是下降?计算速度是上升还是下降?这告诉您关于此问题的记忆效率的什么信息?您可以提出更好的记忆算法吗?
锻炼:我想到了一个特殊的字符串模式;我们称它们为frob字符串。它是这样的:()
是一个字符串字符串,而(XY)
和X
被字符串字符串替换的Y
是字符串字符串。例如(()())
和(()(()()))
都是frob字符串。制作一个方法static IEnumerable<string> AllFrob(int s)
,该方法给出所有带有s
括号的frob字符串。因此AllFrob(1)
只是()
。 AllFrob(2)
为空;没有带有两对括号的字符串字符串。 AllFrob(3)
为(()())
,AllFrob(4)
为空,AllFrob(5)
为(()(()()))
和((()())())
。