算法project——Capacitated Facility Location Problem

Capacitated Facility Location Problem是一个NP难的问题,为了在有限的时间内解决这个问题,我采用了两种启发式算法来解决它。第一种是模拟退火算法,第二种是禁忌搜索算法。两种算法应该说各有千秋。

下面是Capacitated Facility Location Problem的大致内容:

 Suppose there are n facilities and m customers. We wish to choose:

(1) which of the n facilities to open

(2) the assignment of customers to facilities

(3) The total demand assigned to a facility must not exceed its capacity

The objective is to minimize the sum of the opening cost and the assignment cost. 

简单来说就是每个工厂会有一个开启费用(没有顾客分配到这个工厂,则这个工厂就不会开启),然后把一个客户分配到一个工厂会有一个费用。问如何分配所有顾客使费用和最小。

除了上面所说的以外,每个工厂(设施)会有一个容量,然后每个客户会有一个容量的需求。然后分配到这个工厂的客户的容量需求之和不能超过这个工厂的容量。

数据集格式:

There are currently 71 data files. The format of these data files is:

 

|J| |I|

s_1 f_1

s_2 f_2

  ...

s_|J| f_|J|

d_1 d_2 d_3 … d_|I|

c_{11} c_{12} c_{13} … c_{1|I|}

c_{21} c_{22} c_{23} … c_{2|I|}

   ...    ...    ...   ....

c_{|J|1} c_{|J|2} c_{|J|3} … c_{|J||I|}

 

where:

 

|J| is the number of potential facility locations;

|I| is the number of customers;

s_j (j=1,...,|J|) is the capacity of facility j;

f_j (j=1,...,|J|) is the fixed cost of opening facility j;

d_i (i=1,...,|I|) is the demand of customer i;

c_{ji} (j=1,...,|J|), (i=1,...,|I|) is the cost of allocating all the demand of customer i to facility j.

 

首先我采用的是模拟退火算法:

首先随机分配初始解,并且判断其合法性。

我的模拟退火算法主要采用了两种邻域操作:

(1)将一个顾客随机分到另一种工厂去。

(2)将两个顾客所在的工厂交换。

当然,这两种操作必须保证容量。其实只要工厂容量允许,那么两次第一种操作就可以达到第二种操作的效果,但是我也想不到更多简单的邻域操作了。。。。。其他的邻域操作写起来都不容易。

我设计的初温t=1500,然后每次*0.97,外循环迭代3000次,这是为了让开始的动荡大一些。然后内循环次数将随着数据集的大小来改变,定为工厂数*顾客数/10。

为了防止陷入局部最优,我采用了重升温的操作,如果每30次外循环得到相同的总费用cost,那么将进行重升温操作,这里我定义了一个重升温参数ncnt, 重升温将把当前的温度乘ncnt,知道当前得到的cost改变。实际上,通过观察总费用cost的迭代过程,我观察到如果定义一个常数ncnt,那么每次的重升温仍有可能跳不出当前的局部最优而再次收敛。所以ncnt将随重升温次数的增多而变大。如果历史最优值成功更新(即得到了更好的结果),那么ncnt参数将重置为3。

因为需要重升温,所以必须保存历史最优值。

可以看到随着迭代次数增加,费用陷入局部最优9256,但是通过重升温,它找到了更优的局部最小值9135。 

 

坑点:在做第一种局部操作的时候,一定要注意如果选到的客户刚好在选到的工厂中,要如何操作。

下面给出代码:

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sstream>
#include <cstdlib>
#include <cmath>
#include <ctime>


using namespace std;

struct factory{
    int cost;
    int capacity;
    int cnt;
    factory(int c1, int c2, int c3):cost(c1), capacity(c2), cnt(c3){
    }
};

struct customer
{
    int fac;
    int demand;
    vector<int> costs;
};

vector<factory> factorys;
vector<customer> customers;
vector<factory> bfactorys;
vector<customer> bcustomers;

int SA_search()
{
    int i, j, cnt = 0, cost = 0, pastcost = 0, bestcost = 1000000, past = 0;
    int lcost = 0;
    float ncnt = 0;
    float t;
    for(i=0;i<customers.size();i++)
    {
        int ran = rand()%factorys.size();
        if(factorys[ran].capacity - factorys[ran].cnt < customers[i].demand)
        {
            i--;
            continue;
        }
        factorys[ran].cnt += customers[i].demand;
        customers[i].fac = ran;
        cost += (customers[i].costs)[ran];
    }
    for(i=0;i<factorys.size();i++)
    {
        if(factorys[i].cnt!=0)
            cost += factorys[i].cost;
    }
    cout<<cost<<endl;
    t = 1500;
    cnt = 0;
    for(i=0;i<3000;i++)
    {
        for(j=0;j< customers.size() * factorys.size() / 10;j++)
        {
            int ran = rand()%10;
            if(ran<5)
            {
                int changedCus = rand()%customers.size();
                int changedFac = rand()%factorys.size();
                if(changedFac == customers[changedCus].fac)
                    continue;
                if(factorys[changedFac].capacity - factorys[changedFac].cnt < customers[changedCus].demand)
                    continue;
                customer cc = customers[changedCus];
                //past = cc.fac;
                int changedCost = - (cc.costs)[cc.fac] + (cc.costs)[changedFac];
                if(factorys[changedFac].cnt==0)
                    changedCost += factorys[changedFac].cost;
                if(factorys[cc.fac].cnt == cc.demand)
                    changedCost -= factorys[cc.fac].cost;
                int r1 = rand() % 32767;
                float r2 = r1 / 32767.0;

                if(exp(-changedCost/t)> r2)
                {
                    factorys[cc.fac].cnt -=cc.demand;
                    customers[changedCus].fac = changedFac;
                    factorys[changedFac].cnt +=cc.demand;
                    cost += changedCost;
                }
            }
            else
            {
                int changedCus1 = rand()% customers.size();
                int changedCus2 = rand()% customers.size();
                customer cc1 = customers[changedCus1];
                customer cc2 = customers[changedCus2];
                if(cc1.demand>cc2.demand)
                {
                    if(factorys[cc2.fac].capacity-factorys[cc2.fac].cnt<cc1.demand-cc2.demand)
                        continue;
                }
                else
                {
                    if(factorys[cc1.fac].capacity-factorys[cc1.fac].cnt<cc2.demand-cc1.demand)
                        continue;
                }
                int changedCost = (cc1.costs)[cc2.fac] + (cc2.costs)[cc1.fac];
                changedCost -= ((cc1.costs)[cc1.fac] + (cc2.costs)[cc2.fac]);
                int r1 = rand() % 32767;
                float r2 = r1  / 32767.0;
                if(exp(-changedCost/t)> r2)
                {
                    factorys[cc1.fac].cnt -= cc1.demand;
                    factorys[cc1.fac].cnt += cc2.demand;
                    factorys[cc2.fac].cnt -= cc2.demand;
                    factorys[cc2.fac].cnt += cc1.demand;
                    int tmp = cc1.fac;
                    customers[changedCus1].fac = cc2.fac;
                    customers[changedCus2].fac = tmp;
                    cost += changedCost;
                }
            }
        }

        t*=0.97;
        if(i%5==0)
        {
            if(cost==pastcost)
                cnt+=1;
            else
                cnt=0;
            if(cnt>=6)
            {
                t *= ncnt;
                ncnt++;
            }
            pastcost = cost;
            cout<<i<<" cost: "<<cost<<" t: "<<t<<" ncnt:"<<ncnt<<endl;
        }
        if(cost<bestcost)
        {
            bfactorys = factorys;
            bcustomers = customers;
            bestcost = cost;
            ncnt = 3;
        }
    }
    lcost = 0;
    for(i=0;i<bcustomers.size();i++)
    {
        lcost += (bcustomers[i].costs)[bcustomers[i].fac];
    }

    for(i=0;i<bfactorys.size();i++)
    {
        if(bfactorys[i].cnt!=0)
            lcost += bfactorys[i].cost;
    }
    cout<<lcost<<endl;
    return bestcost;
}

接下来是禁忌搜索:

禁忌搜索其实就是在爬山法上加入禁忌表,减少陷入局部最优的可能性。

我只采用了一种领域操作,就是上面模拟退火的第一种领域操作。通过1000次全局搜索,选择不在禁忌表中的最优值。禁忌表中存的是选择的客户和工厂。

禁忌表长度为10+工厂个数。

当搜索道德最优值小于历史最优值,则有可能出现破除禁忌的操作。即无视禁忌表取最优值。禁忌表以滑动数组的方式存放(因为stl中的queue不提供遍历操作)。

下面给出代码:

int Tabu_search()
{
    int tabulength = 10 + factorys.size();
    vector<int> tabuList;
    vector<int> tabuList2;
    int i, j, k, q, cnt = 0, cost = 0, bestcost = 1000000, mincost = 0,tfront = 0, minj = 0, mink = 0;
    for(i=0;i<customers.size();i++)
    {
        int ran = rand()%factorys.size();
        if(factorys[ran].capacity - factorys[ran].cnt < customers[i].demand)
        {
            i--;
            continue;
        }
        factorys[ran].cnt += customers[i].demand;
        customers[i].fac = ran;
        cost += (customers[i].costs)[ran];
    }
    for(i=0;i<factorys.size();i++)
    {
        if(factorys[i].cnt!=0)
            cost += factorys[i].cost;
    }
    cout<<cost<<endl;
    for(i=0;i<1000;i++)
    {
        mincost = 1000000;
        for(j=0;j<customers.size();j++)
        {
            for(k=0;k<factorys.size();k++)
            {
                int changedCus = j;
                int changedFac = k;
                if(changedFac == customers[changedCus].fac)
                    continue;
                if(factorys[changedFac].capacity - factorys[changedFac].cnt < customers[changedCus].demand)
                    continue;
                customer cc = customers[changedCus];
                int changedCost = - (cc.costs)[cc.fac] + (cc.costs)[changedFac];
                if(factorys[changedFac].cnt==0)
                    changedCost += factorys[changedFac].cost;
                if(factorys[cc.fac].cnt == cc.demand)
                    changedCost -= factorys[cc.fac].cost;
                if(cost+changedCost<bestcost)
                {
                    minj = j;
                    mink = k;
                    mincost = changedCost;
                }
                for(q=tfront;q<tabuList.size();q++)
                {
                    if(j!=tabuList[q])
                        continue;
                    if(k!=tabuList2[q])
                        continue;
                    break;
                }
                if(q!=tabuList.size())
                {
                    //cout<<i<<" "<<q<<" "<<tabuList.size();
                    continue;
                }
                if(changedCost < mincost)
                {
                    minj = j;
                    mink = k;
                    mincost = changedCost;
                }
            }
        }

        factorys[customers[minj].fac].cnt -=customers[minj].demand;
        customers[minj].fac = mink;
        factorys[mink].cnt +=customers[minj].demand;
        cost += mincost;
        tabuList.push_back(minj);
        tabuList2.push_back(mink);
        if(tabuList.size()-tfront>tabulength)
            tfront++;
        if(cost<bestcost)
        {
            bfactorys = factorys;
            bcustomers = customers;
            bestcost = cost;

        }
    }
    return bestcost;
}

 最后给出样例结果表格:

随着数据集增大,花费的时间增多。

 SAtimeTabutime
p188760.24990380.461
p279200.23580100.456
p393140.253100100.42
p4111210.242119750.365
p588380.22695030.385
p678550.23381430.309
p796990.232100990.412
p8114360.236121890.381
p985610.26690400.537
p1077040.25977260.566
p1190270.29397260.575
p12108260.285109050.546
p1382520.63187621.326
p1472190.64472151.313
p1593170.64596351.328
p16114820.655121061.454
p1789300.64891291.433
p1871250.6671251.277
p1992950.63997901.354
p20115730.65110601.259
p2181690.67889191.491
p2273890.66979331.479
p2395020.68101291.514
p24107560.671118361.505
p25130045.0211325912.4
p26113063.601134557.986
p27135063.644150377.832
p28157063.632183087.662
p29135753.514157237.518
p30118893.562127177.443
p31141023.535164137.442
p32163953.536214007.456
p33127963.647151267.846
p34112853.617118227.843
p35136503.833164209.08
p36150044.011164398.198
p37122003.709151268.94
p38111494.006127678.751
p39129844.135139318.478
p40149843.75199838.32
p4170980.51171120.939
p4270801.13880922.245
p4366561.62891233.935
p4471330.50472340.771
p4571271.5982513.959
p4668641.62683473.63
p4762490.44865550.727
p4861891.5281493.348
p4970821.7580583.653
p5092390.51596630.867
p5181781.3796862.884
p5293690.507104770.741
p5397111.293108482.655
p5492730.50195620.694
p5581431.32195402.833
p56227464.662356010.443
p57299934.6243288210.414
p58451174.6965388210.579
p59355574.7033912110.239
p60235324.842388210.784
p61300685.0243226011.114
p62454904.8545256011.059
p63327754.7263742310.955
p64231144.8572345810.602
p65302104.7963179910.851
p66436324.8155256010.835
p67346885.1253967110.509
p68228105.1082363010.614
p69303964.8253226010.701
p70447164.9635256011.388
p71349134.793923210.586

下面列出前几个样例的输出:

p1

p2

p3

 

github地址:

https://github.com/smiletomisery/Capacitated-Facility-Location-Problem 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值