说我有一个这样的课程:
public class Work
{
public string Name;
public double Time;
public Work(string name, double time)
{
Name = name;
Time = time;
}
}
我有一个List<Work>
,其中包含约20个值:
List<Work> workToDo = new List<Work>();
// Populate workToDo
是否有任何可能的方法可以将workToDo
分组到每个段的时间总和为特定值的段中?说workToDo
的值如此:
Name | Time
A | 3.50
B | 2.75
C | 4.25
D | 2.50
E | 5.25
F | 3.75
如果我希望时间总和为7,则每个段或List<Work>
应该有一堆值,其中所有Times的总和为7或接近它。这甚至可以远程实现,还是只是一个愚蠢的问题/想法?我使用此代码将workToDo
分成4个部分:
var query = workToDo.Select(x => x.Time)
.Select((x, i) => new { Index = i, Value = x})
.GroupBy(y => y.Index / 4)
.ToList();
但我不知道如何根据时代来做这件事。
答案 0 :(得分:2)
这是一个查询,它将您的数据分组,其中时间接近7但未结束:
Func<List<Work>,int,int,double> sumOfRange = (list, start, end) => list
.Skip(start)
.TakeWhile ((x, index) => index <= end)
.ToList()
.Sum (l => l.Time);
double segmentSize = 7;
var result = Enumerable.Range(0, workToDo.Count ())
.Select (index => workToDo
.Skip(index)
.TakeWhile ((x,i) => sumOfRange(workToDo, index, i)
<= segmentSize));
示例数据集的输出为:
A 3.5
B 2.75
total: 6.25
B 2.75
C 4.25
total: 7
C 4.25
D 2.5
total: 6.75
D 2.5
total: 2.5
E 5.25
total: 5.25
F 3.75
total: 3.75
如果您想允许一个段总数超过七个,那么您可以将segmentSize
变量增加25%左右(即使其为8.75)。
答案 1 :(得分:1)
您所描述的是packing problem(任务被打包到7小时的容器中)。虽然可以在这个问题的解决方案中使用LINQ语法,但我知道LINQ中没有固有的解决方案。
答案 2 :(得分:1)
此解决方案会递归所有组合并返回其总和足够接近目标总和的组合。
这是一个漂亮的前端方法,可让您指定工作列表,目标总和以及总和必须接近的程度:
public List<List<Work>> GetCombinations(List<Work> workList,
double targetSum,
double threshhold)
{
return GetCombinations(0,
new List<Work>(),
workList,
targetSum - threshhold,
targetSum + threshhold);
}
这是完成所有工作的递归方法:
private List<List<Work>> GetCombinations(double currentSum,
List<Work> currentWorks,
List<Work> remainingWorks,
double minSum,
double maxSum)
{
// Filter out the works that would go over the maxSum.
var newRemainingWorks = remainingWorks.Where(x => currentSum + x.Time <= maxSum)
.ToList();
// Create the possible combinations by adding each newRemainingWork to the
// list of current works.
var sums = newRemainingWorks
.Select(x => new
{
Works = currentWorks.Concat(new [] { x }).ToList(),
Sum = currentSum + x.Time
})
.ToList();
// The initial combinations are the possible combinations that are
// within the sum range.
var combinations = sums.Where(x => x.Sum >= minSum).Select(x => x.Works);
// The additional combinations get determined in the recursive call.
var newCombinations = from index in Enumerable.Range(0, sums.Count)
from combo in GetCombinations
(
sums[index].Sum,
sums[index].Works,
newRemainingWorks.Skip(index + 1).ToList(),
minSum,
maxSum
)
select combo;
return combinations.Concat(newCombinations).ToList();
}
此行将获得总和为7 +/- 1的组合:
GetCombinations(workToDo, 7, 1);