这是一种方法,类似于您已经尝试过的代码。这个想法是:
- 确定
“理想值”,将项目的总和除以我们想要将它们分成的部分数 (
resultCount)
- 使用
resultCount 索引创建result 数组
- 创建一个
resultIndex 计数器,以跟踪我们要将当前item 添加到result 数组中的哪个索引
- 在
foreach 循环中,遍历数组,将每个项目添加到result 数组中的当前索引,或者,如果这会使我们超过idealValue,则首先增加我们的索引。李>
例如:
public static int[] Split(int[] input, int resultCount)
{
if (input == null) throw new ArgumentNullException(nameof(input));
if (resultCount > input.Length || resultCount < 1)
throw new ArgumentOutOfRangeException(nameof(resultCount));
var idealValue = input.Sum() / resultCount;
var result = new int[resultCount];
var resultIndex = 0;
foreach (var item in input)
{
// Move to the next index in our result array if the
// next sum puts us over our expected average amount
if (result[resultIndex] > 0 &&
result[resultIndex] + item > idealValue &&
resultIndex < result.Length - 1)
resultIndex++;
result[resultIndex] += item;
}
return result;
}
问题在于,如果所有项目都大于我们的“理想值”,那么 result 数组中的最后一个索引将太大,因为所有内容都被推到那里。为了解决这个问题,我们可能需要遍历数组两次,第一次找出每个项目的值是什么,第二次用理想的和填充结果。
考虑输入:
new [] { 1, 1, 1, 1, 1, 1, 1, 1, 10 }, 3 // Split the array into 3 parts
在上面的示例中,sum / parts = 18,所以idealValue = 6,但实际上我们最终会得到一个看起来像:{ 6, 2, 10 } 而不是{ 4, 4, 10 },后者分布更均匀。
帮助解决此问题的一种方法是删除“异常值”(那些值大于理想值的项目),然后重新计算新的“理想”值。这让我们更接近完美:
public static int[] Split(int[] input, int resultCount)
{
if (input == null) throw new ArgumentNullException(nameof(input));
if (resultCount > input.Length || resultCount < 1)
throw new ArgumentOutOfRangeException(nameof(resultCount));
var idealValue = input.Sum() / resultCount;
var result = new int[resultCount];
var resultIndex = 0;
// Recalculate idealValue by removing items over the ideal
var itemsUnder = input.Where(item => item <= idealValue).ToList();
idealValue = itemsUnder.Sum() / (resultCount - (input.Length - itemsUnder.Count));
foreach (var item in input)
{
// Move to the next index in our result array if the
// next sum puts us over our expected average amount
if (result[resultIndex] > 0 &&
result[resultIndex] + item > idealValue &&
resultIndex < result.Length - 1)
resultIndex++;
result[resultIndex] += item;
}
return result;
}
现在输入:
new [] { 1, 1, 1, 1, 1, 1, 1, 1, 10 }, 3 // Split the array into 3 parts
我们首先计算idealValue = 6,然后我们忽略10,然后除以(3 - 1),得到一个新 idealValue = 4,我们最终得到一个结果看起来像{ 4, 4, 10 },分布更均匀。
这仍然无助于我们最终得到大量较小的数字并被汇总到最后一个索引中的情况(因为没有其他地方可以放置它们)。
例如,如果我们有一个像{80, 99, 60, 200, 50, 70, 90} 这样的输入,我们将首先计算一个idealValue = 216(因为没有一个项目超过这个数量,所以它在第二次重新计算)。但是当我们通过第一次迭代运行它时,我们最终得到了结果集:{179, 60, 410}。请注意,接近末尾的所有较小值都被转储到最后一个位置。
处理那个问题的一种方法是在我们生成第一个结果集后重新计算idealValue再次。如果需要通过检查小于idealValue 的项目之间是否有足够大的差异我们可以修复来重新计算结果,我们只想这样做:
public static int[] Split(int[] input, int resultCount)
{
if (input == null) throw new ArgumentNullException(nameof(input));
if (resultCount > input.Length || resultCount < 1)
throw new ArgumentOutOfRangeException(nameof(resultCount));
var idealValue = input.Sum() / resultCount;
var result = new int[resultCount];
var resultIndex = 0;
// Recalculate idealValue by removing items over the ideal
var itemsUnder = input.Where(item => item <= idealValue).ToList();
idealValue = itemsUnder.Sum() / (resultCount - (input.Length - itemsUnder.Count));
foreach (var item in input)
{
// Move to the next index in our result array if the
// next sum puts us over our expected average amount
if (result[resultIndex] > 0 &&
result[resultIndex] + item > idealValue &&
resultIndex < result.Length - 1)
resultIndex++;
result[resultIndex] += item;
}
// Try to determine if the calculated results can be averaged better
var resultsOver = result.Where(item => item > idealValue).ToList();
var resultsUnder = result.Except(resultsOver).ToList();
if (resultsUnder.Max() - resultsUnder.Min() > resultsUnder.Count)
{
// Recalculate idealValue again based on the final results
// by getting the sum of the difference of the items that are over
// and dividing it by the total number of items
idealValue += resultsOver.Select(item => item - idealValue).Sum() / result.Length;
// Reset our results
result = new int[resultCount];
resultIndex = 0;
foreach (var item in input)
{
// Move to the next index in our result array if the
// next sum puts us over our expected average amount
if (result[resultIndex] > 0 &&
result[resultIndex] + item > idealValue &&
resultIndex < result.Length - 1)
resultIndex++;
result[resultIndex] += item;
}
}
return result;
}
所以现在当我们使用输入 {80, 99, 60, 200, 50, 70, 90} 时,在我们计算出第一个结果集 ({179, 60, 410}) 之后,我们通过获取项目结束的数量、获取平均值来重新计算 idealValue,然后将其添加到我们的idealValue,例如:410 - 216 = 194, 194 / 3 = 64, idealValue = 216 + 64 = 280。然后我们重置结果并再次运行它,最终得到:
{239, 250, 160}