【问题标题】:Algorithm to evenly distribute values into containers?将值均匀分配到容器中的算法?
【发布时间】:2013-10-05 22:08:57
【问题描述】:

有没有人知道如何将数字平均分配到一组容器中,以确保容器的总值尽可能均匀?

编辑:“尽可能均匀”是指如果分布在 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.

但我不知道用代码表达这一点的巧妙方法。

想法?

【问题讨论】:

  • 只保留每个容器的总数,然后继续推到最小的那个?
  • @billz 这会将{1000,501,500,1} 分发到{1,501} {500,1000}....
  • 先把大的推进去,然后它们就更均匀了。它应该是 {1000}、{501}、{500,1}
  • 现在您只需要定义“尽可能”。你似乎认为这很明显,但事实并非如此。您也许可以尝试最小化每个容器总数与平均总数之间差异的绝对值之和。或者您可以最小化与平均总数的最大偏差。或者很多其他的东西。除非你能定义你的问题,否则你不会得到解决方案。
  • 这个(背包)问题具有指数复杂性:本质上,在这种情况下,上限(要尝试的组合)是 3^7;因为这七个项目中的每一个都可以属于容器 1,2 或 3;可以尝试找到一些启发式或“提前退出”算法来稍微提高复杂性——但它仍然是 a^b 的形式;

标签: algorithm


【解决方案1】:

您是否有一个大型数据集,对象的大小差异很大,并且您必须找到最佳解决方案的铸铁要求?如果是这样,这是不现实的。

但好消息是,许多理论上 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中的元素之和的差值最小化。

【讨论】:

    【解决方案2】:

    有趣。到目前为止,这个 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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-01-15
      • 1970-01-01
      • 1970-01-01
      • 2012-05-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多