有没有人知道如何将数字均匀地分配到一定数量的容器中,确保容器的总值尽可能均匀?
编辑:“尽可能”,我的意思是如果按X容器分配,每个容器的总数将接近总平均值。
现在我只是对数字数组进行排序(降序),然后将它们的值无关地分配到容器中。分配到三个容器中的一组1000,200,20,1000等于[2000],[200],[20]。
我想做的是:
Example
Set of numbers: 10 30 503 23 1 85 355
If I were to distribute these into three containers I would just pick the highest first and then distribute them as I go, like this:
Cont 1 = 503
Cont 2 = 355
Cont 3 = 85 + 30 + 23 + 10 + 1
This will give the best possible distribution that you can get with the values provided.
但我不知道在代码中表达这一点的简洁方法。
想法?
答案 0 :(得分:3)
您是否拥有大型数据集,对象大小差异很大,以及您必须找到最佳解决方案的铸铁要求?如果是这样,这是不现实的。
但好消息是,在理论上很多NP完全的问题在现实世界中非常容易!如果您的数据点数量相对较少,那么您可以进行智能(但仍然彻底)搜索并找到全局最优解决方案。
此外,如果值的方差非常小,如果你有一个表现良好的数据集,你可能会很快发现一个完全均匀地填充所有容器的解决方案。如果是这样,那么这显然是最好的答案。即使在非常大的数据集上,这也可以很好地工作。 (我认为你想要的是一个包含许多小值的数据集,可以用来在最后轻松整理东西。)。
所以,不要放弃!首先,对数据进行排序,并考虑从最大到最小的数据点。在每个阶段,将下一个值分配给当前最小的容器。这可能无法在所有情况下为您提供最佳解决方案,但在实践中可能是非常合理的。
排序1000, 200, 20, 1000
会给你1000, 1000, 200, 20
。然后这个算法会给你:
1000 = 1000
1000 = 1000
200 +20 = 220
这恰好是最佳解决方案,但情况并非总是如此。
====
如果您愿意并且能够尝试更复杂的算法,请查看the partition problem:
虽然分区问题是NP完全的,但有一个 伪多项式时间动态规划解决方案,并有 在许多情况下解决问题的启发式算法,无论是最佳的 或大约。因此,它被称为“最简单的 难题“。
存在分区问题的优化版本,即将多集S分成两个子集S1,S2,使得S1中的元素之和与S2中的元素之和之间的差异最小化。
答案 1 :(得分:1)
有趣。到目前为止,这个C程序似乎给出了预期的结果。它首先对数据进行排序,然后对于 n 容器,立即在每个容器中存储 n 最高数字。 (实际上,您可以省略该步骤。)然后,从最大的剩余数字到最小的数字,它找到容器,其中添加该数字与最佳平均值的差异最小。因为它从高到低运行,每个数字都放在最佳容器中 - 所有其他数字都较低,因此它们的差异甚至会更大。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
int sort_numeric (const void *a, const void *b)
{
return *((int *)a) - *((int *)b);
}
int main (int argc, char **argv)
{
int list[] = { 10, 30, 503, 23, 1, 85, 355 };
int i,j, nnumber, ncontainer, total, avgPerContainer, nextError, smallestError, containerForSmallest;
int *containers, *errors;
ncontainer = 3;
nnumber = sizeof(list)/sizeof(list[0]);
qsort (list, nnumber, sizeof(list[0]), sort_numeric);
containers = (int *)malloc(ncontainer * sizeof(int));
for (i=0; i<ncontainer; i++)
containers[i] = 0;
errors = (int *)malloc(ncontainer * sizeof(int));
for (i=0; i<ncontainer; i++)
errors[i] = 0;
printf ("input:");
for (i=0; i<nnumber; i++)
{
printf (" %d", list[i]);
}
printf ("\n");
// how much is to be in each container?
total = 0;
for (i=0; i<nnumber; i++)
total += list[i];
// this will result in a fraction:
// avgPerContainer = total/ncontainer;
// so instead we'll use 'total' and *keeping in mind*
// that the number needs to be divided by ncontainer
avgPerContainer = total;
printf ("per container: ~%d\n", (2*avgPerContainer+ncontainer)/(2*ncontainer) );
// start by putting highest values into each container
for (i=0; i<ncontainer; i++)
containers[i] += list[nnumber-ncontainer+i];
// .. remove from list ..
nnumber -= ncontainer;
// print current totals
for (i=0; i<ncontainer; i++)
{
errors[i] = containers[i]*ncontainer - avgPerContainer;
printf ("#%d: %d, error = %d/%d ~ %+d\n", i, containers[i], errors[i], ncontainer, (2*errors[i]+ncontainer)/(2*ncontainer) );
}
printf ("remaining:");
for (i=0; i<nnumber; i++)
{
printf (" %d", list[i]);
}
printf ("\n");
// add the remainders
for (i=nnumber-1; i>=0; i--)
{
smallestError = INT_MAX;
containerForSmallest = 0;
for (j=0; j<ncontainer; j++)
{
nextError = (containers[j] + list[i]) - avgPerContainer;
if (nextError < smallestError)
{
containerForSmallest = j;
smallestError = nextError;
printf ("error for %d, %d + %d, is %+d\n", j, containers[j], list[i], smallestError);
}
}
printf ("we put %d into #%d\n", list[i], containerForSmallest);
containers[containerForSmallest] += list[i];
}
for (i=0; i<ncontainer; i++)
{
printf ("#%d: %d, error = %d/%d ~ %+d\n", i, containers[i], containers[i]*ncontainer - avgPerContainer, ncontainer, (2*(containers[i]*ncontainer - avgPerContainer)+ncontainer)/(2*ncontainer) );
}
return 0;
}