最小化运输时间

| [底部的更新(包括解决方案源代码)] 我有一个具有挑战性的业务问题,计算机可以帮助解决。 沿着山区流淌着蜿蜒曲折的大河。在河流的某些部分,有一些对环境敏感的土地,适合种植需求量很大的特殊类型的稀有水果。一旦现场工作人员收获了水果,时钟就会开始滴答作响,将水果送到加工厂。尝试将水果运到上游或陆运或空运上非常昂贵。迄今为止,将它们运送到工厂的最具成本效益的机制是仅在河流恒流供电的集装箱下游。我们有能力建造10个加工厂,并且需要在河边安置这些加工厂,以最大程度地减少水果在运输中花费的总时间。然而,水果可能要花很长时间才能到达最近的下游工厂,但是这段时间直接损害了可以出售的价格。实际上,我们希望最小化到最近的各个下游工厂的距离之和。植物可以位于水果访问点下游仅0米的位置。 问题是:为了获得最大的利润,如果我们发现了32个水果种植区,而这些区距河床上游的距离(以米为单位),那么我们应该在河的上游建造10个加工厂: 10、40、90、160、250、360、490,...(n ^ 2)* 10 ... 9000、9610、10320? [希望解决该问题并创建类似问题和使用场景的所有工作都可以帮助提高人们对软件/商业方法专利的破坏性和窒息性的认识,并引起公众的抵制(无论在何种程度上相信这些专利在当地合法))。 更新 Update1:​​忘了补充:我相信这个问题是这个问题的特例。 Update2:我编写的一种算法可以在不到一秒钟的时间内给出答案,而且我认为还不错(但是它在样本值之间还不稳定)。稍后我将提供更多详细信息,但简短内容如下。将植物等距放置。循环遍历所有内部植物,在这些植物中,您可以通过测试两个邻居之间的每个位置来重新计算其位置,直到在该空间内解决问题为止(贪婪算法)。因此,您可以优化固定1和3的工厂2。然后固定3和2的工厂3固定...到达终点时,循环返回并重复直到完成一个完整的周期,每个加工工厂重新计算的位置都停止变化。同样在每个周期结束时,您尝试将彼此拥挤且彼此附近的水果堆都靠近的加工厂移到一个水果堆很远的地区。有很多方法可以改变细节,从而产生确切的答案。我还有其他候选算法,但都有故障。 [稍后我将发布代码。]正如下面的Mike Dunlavey所述,我们可能只希望“足够好”。 给出可能是“足够好”的结果的想法:
10010 total length of travel from 32 locations to plants at 
{10,490,1210,1960,2890,4000,5290,6760,8410,9610}
Update3:mhum首先给出了正确的准确解决方案,但是还没有发布程序或算法,所以我写了一个产生相同值的解决方案。
/************************************************************
This program can be compiled and run (eg, on Linux):
$ gcc -std=c99 processing-plants.c -o processing-plants
$ ./processing-plants
************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//a: Data set of values. Add extra large number at the end

int a[]={
10,40,90,160,250,360,490,640,810,1000,1210,1440,1690,1960,2250,2560,2890,3240,3610,4000,4410,4840,5290,5760,6250,6760,7290,7840,8410,9000,9610,10240,99999
};

//numofa: size of data set

int numofa=sizeof(a)/sizeof(int);

//a2: will hold (pt to) unique data from a and in sorted order.

int *a2;

//max: size of a2

int max;

//num_fixed_loc: at 10 gives the solution for 10 plants

int num_fixed_loc;

//xx: holds index values of a2 from the lowest error winner of each cycle memoized. accessed via memoized offset value. Winner is based off lowest error sum from left boundary upto right ending boundary.
//FIX: to be dynamically sized.

int xx[1000000];

//xx_last: how much of xx has been used up

int xx_last=0;

//SavedBundle: data type to \"hold\" memoized values needed (total traval distance and plant locations) 

typedef struct _SavedBundle {
    long e;
    int xx_offset;
} SavedBundle;

//sb: (pts to) lookup table of all calculated values memoized

SavedBundle *sb;  //holds winning values being memoized

//Sort in increasing order.

int sortfunc (const void *a, const void *b) {
    return (*(int *)a - *(int *)b);
}

/****************************
Most interesting code in here
****************************/

long full_memh(int l, int n) {
    long e;
    long e_min=-1;
    int ti;

    if (sb[l*max+n].e) {
        return sb[l*max+n].e;  //convenience passing
    }
    for (int i=l+1; i<max-1; i++) {
        e=0;
        //sum first part
        for (int j=l+1; j<i; j++) {
            e+=a2[j]-a2[l];
        }
        //sum second part
        if (n!=1) //general case, recursively
            e+=full_memh(i, n-1);
        else      //base case, iteratively
            for (int j=i+1; j<max-1; j++) {
                e+=a2[j]-a2[i];
            }
        if (e_min==-1) {
            e_min=e;
            ti=i;
        }
        if (e<e_min) {
            e_min=e;
            ti=i;
        }
    }
    sb[l*max+n].e=e_min;
    sb[l*max+n].xx_offset=xx_last;
    xx[xx_last]=ti;      //later add a test or a realloc, etc, if approp
    for (int i=0; i<n-1; i++) {
        xx[xx_last+(i+1)]=xx[sb[ti*max+(n-1)].xx_offset+i];
    }
    xx_last+=n;
    return e_min;
}

/*************************************************************
Call to calculate and print results for given number of plants
*************************************************************/

int full_memoization(int num_fixed_loc) {
    char *str;
    long errorsum;  //for convenience

    //Call recursive workhorse
    errorsum=full_memh(0, num_fixed_loc-2);
    //Now print
    str=(char *) malloc(num_fixed_loc*20+100);
    sprintf (str,\"\\n%4d %6d {%d,\",num_fixed_loc-1,errorsum,a2[0]);
    for (int i=0; i<num_fixed_loc-2; i++)
        sprintf (str+strlen(str),\"%d%c\",a2[ xx[ sb[0*max+(num_fixed_loc-2)].xx_offset+i ] ], (i<num_fixed_loc-3)?\',\':\'}\');
    printf (\"%s\",str);
    return 0;
}

/**************************************************
Initialize and call for plant numbers of many sizes
**************************************************/

int main (int x, char **y) {
    int t;
    int i2;

    qsort(a,numofa,sizeof(int),sortfunc);
    t=1;
    for (int i=1; i<numofa; i++)
        if (a[i]!=a[i-1])
            t++;
    max=t;
    i2=1;
    a2=(int *)malloc(sizeof(int)*t);
    a2[0]=a[0];
    for (int i=1; i<numofa; i++)
        if (a[i]!=a[i-1]) {
            a2[i2++]=a[i];
        }
    sb = (SavedBundle *)calloc(sizeof(SavedBundle),max*max);
    for (int i=3; i<=max; i++) {
        full_memoization(i);
    }
    free(sb);
    return 0;
}
    
已邀请:
除非我犯了一个错误,否则这里是确切的解决方案(通过动态编程方法获得):
N  Dist  Sites
2  60950 {10,4840}
3  40910 {10,2890,6760}
4  30270 {10,2250,4840,7840}
5  23650 {10,1690,3610,5760,8410}
6  19170 {10,1210,2560,4410,6250,8410}
7  15840 {10,1000,2250,3610,5290,7290,9000}
8  13330 {10,810,1960,3240,4410,5760,7290,9000}
9  11460 {10,810,1690,2890,4000,5290,6760,8410,9610}
10 9850  {10,640,1440,2250,3240,4410,5760,7290,8410,9610}
11 8460  {10,640,1440,2250,3240,4410,5290,6250,7290,8410,9610}
12 7350  {10,490,1210,1960,2890,3610,4410,5290,6250,7290,8410,9610}
13 6470  {10,490,1000,1690,2250,2890,3610,4410,5290,6250,7290,8410,9610}
14 5800  {10,360,810,1440,1960,2560,3240,4000,4840,5760,6760,7840,9000,10240}
15 5190  {10,360,810,1440,1960,2560,3240,4000,4840,5760,6760,7840,9000,9610,10240}
16 4610  {10,360,810,1210,1690,2250,2890,3610,4410,5290,6250,7290,8410,9000,9610,10240}
17 4060  {10,360,810,1210,1690,2250,2890,3610,4410,5290,6250,7290,7840,8410,9000,9610,10240}
18 3550  {10,360,810,1210,1690,2250,2890,3610,4410,5290,6250,6760,7290,7840,8410,9000,9610,10240}
19 3080  {10,360,810,1210,1690,2250,2890,3610,4410,5290,5760,6250,6760,7290,7840,8410,9000,9610,10240}
20 2640  {10,250,640,1000,1440,1960,2560,3240,4000,4840,5290,5760,6250,6760,7290,7840,8410,9000,9610,10240}
21 2230  {10,250,640,1000,1440,1960,2560,3240,4000,4410,4840,5290,5760,6250,6760,7290,7840,8410,9000,9610,10240}
22 1860  {10,250,640,1000,1440,1960,2560,3240,3610,4000,4410,4840,5290,5760,6250,6760,7290,7840,8410,9000,9610,10240}
23 1520  {10,250,490,810,1210,1690,2250,2890,3240,3610,4000,4410,4840,5290,5760,6250,6760,7290,7840,8410,9000,9610,10240}
24 1210  {10,250,490,810,1210,1690,2250,2560,2890,3240,3610,4000,4410,4840,5290,5760,6250,6760,7290,7840,8410,9000,9610,10240}
25 940   {10,250,490,810,1210,1690,1960,2250,2560,2890,3240,3610,4000,4410,4840,5290,5760,6250,6760,7290,7840,8410,9000,9610,10240}
26 710   {10,160,360,640,1000,1440,1690,1960,2250,2560,2890,3240,3610,4000,4410,4840,5290,5760,6250,6760,7290,7840,8410,9000,9610,10240}
27 500   {10,160,360,640,1000,1210,1440,1690,1960,2250,2560,2890,3240,3610,4000,4410,4840,5290,5760,6250,6760,7290,7840,8410,9000,9610,10240}
28 330   {10,160,360,640,810,1000,1210,1440,1690,1960,2250,2560,2890,3240,3610,4000,4410,4840,5290,5760,6250,6760,7290,7840,8410,9000,9610,10240}
29 200   {10,160,360,490,640,810,1000,1210,1440,1690,1960,2250,2560,2890,3240,3610,4000,4410,4840,5290,5760,6250,6760,7290,7840,8410,9000,9610,10240}
30 100   {10,90,250,360,490,640,810,1000,1210,1440,1690,1960,2250,2560,2890,3240,3610,4000,4410,4840,5290,5760,6250,6760,7290,7840,8410,9000,9610,10240}
31 30    {10,90,160,250,360,490,640,810,1000,1210,1440,1690,1960,2250,2560,2890,3240,3610,4000,4410,4840,5290,5760,6250,6760,7290,7840,8410,9000,9610,10240}
32 0     {10,40,90,160,250,360,490,640,810,1000,1210,1440,1690,1960,2250,2560,2890,3240,3610,4000,4410,4840,5290,5760,6250,6760,7290,7840,8410,9000,9610,10240}
    
让我给您一个Metropolis-Hastings算法的简单示例。 假设您有一个状态向量x和一个拟合优度函数P(x),它可以是您想写的任何函数。 假设您有一个随机分布Q,可用于修改矢量,例如
x\' = x + N(0, 1) * sigma
,其中N是一个简单的正态分布,大约为0,而sigma是您选择的标准偏差。
p = P(x);
for (/* a lot of iterations */){
  // add x to a sample array
  // get the next sample
  x\' = x + N(0,1) * sigma;
  p\' = P(x\');
  // if it is better, accept it
  if (p\' > p){
    x = x\';
    p = p\';
  }
  // if it is not better
  else {
    // maybe accept it anyway
    if (Uniform(0,1) < (p\' / p)){
      x = x\';
      p = p\';
    }
  }
}
通常,它的老化时间约为1000个周期,然后开始收集样品。再过10,000次循环后,样本的平均值即为答案。 它需要诊断和调整。通常情况下会绘制样本,而您要查找的是“模糊毛毛虫”图,该图稳定(不会移动太多)并且接受率很高(非常模糊)。可以使用的主要参数是sigma。 如果sigma太小,则图将变得模糊,但会四处徘徊。 如果太大,则图将不会模糊-它将具有水平线段。 通常,起始向量x是随机选择的,并且经常选择多个起始向量,以查看它们是否在同一位置结束。 不必同时改变状态向量x的所有分量。您可以循环浏览它们,一次更改一个,或使用类似的方法。 另外,如果您不需要诊断图,则可能不必保存样本,而只需即时计算平均值和方差即可。 在我熟悉的应用程序中,P(x)是概率的量度,通常在对数空间中,因此它可以从0到负无穷大变化。 然后执行“也许接受”步骤是
(exp(logp\' - logp))
    

要回复问题请先登录注册