算法分析期末作业:Capacitated Facility Location Problem

求解算法

  • 贪心算法求解
    贪心策略是不从整体最优上加以考虑,做出的是在某种意义上的局部最优解。这种方法能在比较短时间内得到问题的解,但只能对一些满足无后效性的问题求得全局最优解。而这个问题就具有后效性,而且暴力求解复杂度为n^m,n为顾客数量,m为工厂数量。
    本题的总费用由开厂费用和配送费用组成,基于这点贪心策略考虑的方向有两个,一个是每次开厂都优先用完容量满足尽可能多的顾客从而节省开厂费用,另一个是顾客都选择最近的厂从而节省配送费用。
    工厂费用优先算法步骤:
    (1).选择还没开启的工厂中开启费用最小的厂S加入开启的队列Open,如过没有可开启的工厂跳转(4)。
    (2).选取S附近未得到分配,且配送费用最小的顾客C,如果工厂内剩余容量足够则跳转(3),否则跳转(1)。
    (3).将C加入S的配送顾客队列Q(S),如果全部顾客得到分配则结束,否则跳转(2)。
    (4).分配结束,计算总费用
    顾客距离优先算法步骤:
    (1).遍历顾客列表,选择一个顾客C执行(2)
    (2).寻找顾客C最近的工厂S,如果工厂未开启则开启工厂,如果工厂的容量容量足够则将顾客C加入S的配送顾客队列Q(S)并跳转(1),否则重复(2)
    考虑了样例的特点是大部分样例的配送费用的比重是大于开厂的费用的,基于这点选择第二种效果更好,下面提供的也是第二种贪心策略的代码。(第一种贪心策略经过编码测试,只有很小部分样例结果优于第二种,所以不上传实现代码)
  1. 模拟退火迭代法求解
    模拟退火算法是基于迭代求解策略的一种随机寻优算法。算法从某一较高初温出发,然后温度参数不断下调,系统能量朝着减少的趋势变化。在解空间中随机寻找目标函数的解,在局部最优解能概率性地跳出并最终趋于全局最优。
    算法步骤:
    (1).将顾客随机分配到各工厂当中,生成一个初始解s0,设定初始温度temperature,降温速率ratio,迭代次数t,迭代次数变化率vt。
    (2).进行t次如下操作,给当前解一个随机的位移(即进行局部搜索寻找一个新解,本例中随机选取一个顾客换到一个离他较近的工厂),计算总费用变化offset。 如果总费用的变化offset<0则直接接受该解为当前解。如果offset>0,则以一个随温度变化而变化的概率exp(-offset/temperature)接受该解,否则不接受该解。
    (3).更新温度temperature = temperature * ratio 和迭代次数t = t * vt,如果温度下降到预定值LIMIT,算法结束,否则继续(2)

C++源代码

//贪心算法
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <cfloat>
#include <ctime>
#include <string>

using namespace std;

//将结果信息输出
void debug(int k, float totaltime, 
    vector<bool> isOpen, vector<int> toFacility, vector<float> usedCap, int minCost) {
    ofstream ofile;
    ofile.open("result1.txt", ios::app);

    ofile << "p" << k << "," << minCost << "," << totaltime << ","; 
    for(int i = 0; i < isOpen.size(); i++){
        ofile << isOpen[i];
        if (i == isOpen.size()-1) {
            ofile << ",";
        }
        else{
            ofile << " ";
        }
    }
    for(int i = 0; i < toFacility.size(); i++){
        ofile << toFacility[i];
        if (i == toFacility.size()-1) {
            ofile << "\n";
        }
        else{
            ofile << " ";
        }
    }

    ofile.close();    
}

//读取数据文件
void readdata(string filename, vector<float> &capacity, vector<float> &fixedCost, vector<float> &demand, vector<vector<float> > &disCost) {
    ifstream ifile;
    ifile.open(filename, ios::in);

    int fNum, cNum;
    ifile >> fNum >> cNum;

    capacity.assign(fNum,0);
    fixedCost.assign(fNum,0);
    for(int i = 0; i < fNum; i++){
        ifile >> capacity[i] >> fixedCost[i];
    }
    
    demand.assign(cNum,0);
    for(int i = 0; i < cNum; i++){
        ifile >> demand[i];
    }

    disCost.assign(fNum,vector<float>(cNum,0));
    for(int i = 0; i < fNum; i++){
        for(int j = 0; j < cNum; j++){
            ifile >> disCost[i][j];
        }
    }

    ifile.close();
}

int main(int argc, char const *argv[]) {
    for(int k = 1; k <= 71; k++) {
        //程序开始计时
        clock_t start, end;
        start = clock();
        //读取数据文件
        string filename = "Instances/p" + to_string(k);
        vector<float> capacity, fixedCost, demand;
        vector<vector<float> > disCost;
        readdata(filename, capacity, fixedCost, demand, disCost);  
        //存储结果的数据结构
        int minCost = 0;
        vector<bool> isOpen(capacity.size(), false);
        vector<int> toFacility(demand.size(), -1);
        vector<float> usedCap(capacity.size(), 0);
        //贪心策略
        for(int i = 0; i < demand.size(); i++){
            vector<float> cost(capacity.size(), 0);

            for(int j = 0; j < capacity.size(); j++){
                cost[j] = disCost[j][i];
            }
            //每次取最近的能满足顾客要求的工厂
            vector<float>::iterator smallest;
            int index;
            while(true) {
                smallest = min_element(cost.begin(), cost.end());
                index = distance(cost.begin(), smallest);
                if(capacity[index] - usedCap[index] >= demand[i]) {
                    break;
                }
                else{
                    cost[index] = FLT_MAX;
                }
            }
            //记录分配结果
            if(!isOpen[index]) {
                minCost += fixedCost[index];
                isOpen[index] = true;
            }
            minCost += disCost[index][i];
            usedCap[index] += demand[i];
            toFacility[i] = index;
            
        }
        //结束程序计时
        end = clock();
        double totaltime = (double)(end - start) / CLOCKS_PER_SEC;
        debug(k,totaltime,isOpen, toFacility, usedCap, minCost);
    }
    
    return 0;
}
//模拟退火算法
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <cfloat>
#include <string>
#include <cmath>
#include <ctime>

using namespace std;

//模拟退火使用的参数
float TEMPERATURE = 100.0;  //初温
float T_RATIO = 0.98;       //降温速率
float LIMIT = 1.0;          //迭代停止温度条件
int ITERATION_TIME = 500;   //每个降温阶段的迭代次数
float ITERATION_RATIO = 1.01;//迭代次数随降温的提升比例

//将结果信息输出
void debug(int k, float totaltime, 
    vector<bool> isOpen, vector<int> toFacility, vector<float> usedCap, int minCost) {
    ofstream ofile;
    ofile.open("result2.txt", ios::app);

    ofile << "p" << k << "," << minCost << "," << totaltime << ","; 
    for(int i = 0; i < isOpen.size(); i++){
        ofile << isOpen[i];
        if (i == isOpen.size()-1) {
            ofile << ",";
        }
        else{
            ofile << " ";
        }
    }
    for(int i = 0; i < toFacility.size(); i++){
        ofile << toFacility[i];
        if (i == toFacility.size()-1) {
            ofile << "\n";
        }
        else{
            ofile << " ";
        }
    }

    ofile.close();    
}
//生成初始解
void init(vector<float> capacity, vector<float> fixedCost, vector<float> demand, vector<vector<float> > disCost, 
    vector<bool> &isOpen, vector<int> &toFacility, vector<float> &usedCap, int &minCost) {
    for(int i = 0; i < isOpen.size(); i++) {
        isOpen[i] = true;
        minCost += fixedCost[i];
    }

    for(int i = 0; i < demand.size(); i++) {
        int pos = rand() % capacity.size();
        for(int j = 0; j < capacity.size(); j++) {
            int index = (pos+j)%capacity.size();
            if(capacity[index] - usedCap[index] >= demand[index]) {
                usedCap[index] += demand[i];
                toFacility[i] = index;
                minCost += disCost[index][i];
                break;
            }
        }
    }
}
//模拟退火迭代函数
void iteration(vector<float> capacity, vector<float> fixedCost, vector<float> demand, vector<vector<float> > disCost, 
    vector<bool> &isOpen, vector<int> &toFacility, vector<float> &usedCap, int &minCost) {
    float temperature = TEMPERATURE;
    int iteration_time = ITERATION_TIME;
    while(temperature > LIMIT) {
        for(int i = 0; i < iteration_time; i++) {
            int pos_c = rand() % demand.size();
            float costbefore = disCost[toFacility[pos_c]][pos_c];
            if(usedCap[toFacility[pos_c]] == demand[pos_c]) {
                costbefore += fixedCost[toFacility[pos_c]];
            }
            
            int pos_f = rand() % capacity.size();
            float costafter = disCost[pos_f][pos_c];
            if(!isOpen[pos_f]) {
                costafter += fixedCost[pos_f];
            }

            if(pos_f != toFacility[pos_c] && capacity[pos_f] - usedCap[pos_f] >= demand[pos_c] && 
            (costafter < costbefore || 1.0*rand()/RAND_MAX < exp(-(costafter-costbefore) / temperature))) {
                minCost += costafter - costbefore;
                if(usedCap[toFacility[pos_c]] == demand[pos_c]) {
                    isOpen[toFacility[pos_c]] = false;
                }
                if(!isOpen[pos_f]) {
                    isOpen[pos_f] = true;
                }
                usedCap[toFacility[pos_c]] -= demand[pos_c];
                usedCap[pos_f] += demand[pos_c];
                toFacility[pos_c] = pos_f;
            }
        }
        iteration_time *= ITERATION_RATIO;
        temperature *= T_RATIO;
    }

}
 //读取数据文件
void readdata(string filename, vector<float> &capacity, vector<float> &fixedCost, vector<float> &demand, vector<vector<float> > &disCost) {
    ifstream ifile;
    ifile.open(filename, ios::in);

    int fNum, cNum;
    ifile >> fNum >> cNum;

    capacity.assign(fNum,0);
    fixedCost.assign(fNum,0);
    for(int i = 0; i < fNum; i++){
        ifile >> capacity[i] >> fixedCost[i];
    }
    
    demand.assign(cNum,0);
    for(int i = 0; i < cNum; i++){
        ifile >> demand[i];
    }

    disCost.assign(fNum,vector<float>(cNum,0));
    for(int i = 0; i < fNum; i++){
        for(int j = 0; j < cNum; j++){
            ifile >> disCost[i][j];
        }
    }

    ifile.close();
}

int main(int argc, char const *argv[]) {
    srand((unsigned int)time(NULL));
    for(int k = 1; k <= 71; k++) {
        //程序开始计时
        clock_t start, end;
        start = clock(); 
        //读取数据文件
        string filename = "Instances/p" + to_string(k);
        vector<float> capacity, fixedCost, demand;
        vector<vector<float> > disCost;
        readdata(filename, capacity, fixedCost, demand, disCost);
        //存储结果的数据结构
        int minCost = 0;
        vector<bool> isOpen(capacity.size(), false);
        vector<int> toFacility(demand.size(), -1);
        vector<float> usedCap(capacity.size(), 0);
        //生成初始解
        init(capacity, fixedCost, demand, disCost, isOpen, toFacility, usedCap, minCost);
        //进入模拟退火迭代
        iteration(capacity, fixedCost, demand, disCost, isOpen, toFacility, usedCap, minCost);
        //结束程序计时
        end = clock();
        double totaltime = (double)(end - start) / CLOCKS_PER_SEC;
        debug(k,totaltime,isOpen, toFacility, usedCap, minCost);
    }
    return 0;
}

结果表格

表格格式为

Problems(样例)Results(结果)Time(用时)Open(工厂状态)Facility(顾客分配情况)
p188480.0300 1 0 1 0 0 1 1 0 1…1 3 6 7 9…

贪心策略结果
https://blog.youkuaiyun.com/m0_37779608/article/details/85226459
模拟退火算法结果
https://blog.youkuaiyun.com/m0_37779608/article/details/85226480

可以看到两种算法用时都在几十毫秒,模拟退火算法用时平均较长,但求解结果相比贪心算法要好很多。

期末作业基于python的足球运动员数据分析源码+数据集(高分项目),个人经导师指导并认可通过的高分设计项目,评审分98分,项目中的源码都是经过本地编译过可运行的,都经过严格调试,确保可以运行!主要针对计算机相关专业的正在做大作业、毕业设计的学生和需要项目实战练习的学习者,资源项目的难度比较适中,内容都是经过助教老师审定过的能够满足学习、使用需求,如果有需要的话可以放心下载使用。 期末作业基于python的足球运动员数据分析源码+数据集(高分项目)期末作业基于python的足球运动员数据分析源码+数据集(高分项目)期末作业基于python的足球运动员数据分析源码+数据集(高分项目)期末作业基于python的足球运动员数据分析源码+数据集(高分项目)期末作业基于python的足球运动员数据分析源码+数据集(高分项目)期末作业基于python的足球运动员数据分析源码+数据集(高分项目)期末作业基于python的足球运动员数据分析源码+数据集(高分项目)期末作业基于python的足球运动员数据分析源码+数据集(高分项目)期末作业基于python的足球运动员数据分析源码+数据集(高分项目)期末作业基于python的足球运动员数据分析源码+数据集(高分项目)期末作业基于python的足球运动员数据分析源码+数据集(高分项目)期末作业基于python的足球运动员数据分析源码+数据集(高分项目)期末作业基于python的足球运动员数据分析源码+数据集(高分项目)期末作业基于python的足球运动员数据分析源码+数据集(高分项目)期末作业基于python的足球运动员数据分析源码+数据集(高分项目)期末作业基于python的足球运动员数据分析源码+数据集(高分项目)期末作业基于python的足球运动员数据分析源码+数据集(高分项目)期末作业基于pyth
Here is a basic implementation of CVRP in Python using the Google OR-Tools library: ```python from ortools.constraint_solver import routing_enums_pb2 from ortools.constraint_solver import pywrapcp def create_data_model(): """Stores the data for the problem.""" data = {} data['distance_matrix'] = [ [0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502], [548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594], [776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278], [696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514], [582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400], [274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 810], [502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1016], [194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468], [308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810], [194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 650], [536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878], [502, 594, 1278, 514, 400, 810, 1016, 468, 810, 650, 878, 0] ] data['num_vehicles'] = 3 data['vehicle_capacities'] = [100, 100, 100] data['depot'] = 0 return data def print_solution(data, manager, routing, solution): """Prints solution on console.""" total_distance = 0 total_load = 0 for vehicle_id in range(data['num_vehicles']): index = routing.Start(vehicle_id) plan_output = 'Route for vehicle {}:\n'.format(vehicle_id) route_distance = 0 route_load = 0 while not routing.IsEnd(index): node_index = manager.IndexToNode(index) route_load += data['demands'][node_index] plan_output += ' {} Load({}) -> '.format(node_index, route_load) previous_index = index index = solution.Value(routing.NextVar(index)) route_distance += routing.GetArcCostForVehicle( previous_index, index, vehicle_id) plan_output += ' {} Load({})\n'.format(manager.IndexToNode(index), route_load) plan_output += 'Distance of the route: {}m\n'.format(route_distance) plan_output += 'Load of the route: {}\n'.format(route_load) print(plan_output) total_distance += route_distance total_load += route_load print('Total distance of all routes: {}m'.format(total_distance)) print('Total load of all routes: {}'.format(total_load)) def main(): """Entry point of the program.""" data = create_data_model() manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']), data['num_vehicles'], data['depot']) routing = pywrapcp.RoutingModel(manager) def distance_callback(from_index, to_index): """Returns the distance between the two nodes.""" from_node = manager.IndexToNode(from_index) to_node = manager.IndexToNode(to_index) return data['distance_matrix'][from_node][to_node] transit_callback_index = routing.RegisterTransitCallback(distance_callback) routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index) dimension_name = 'Capacity' routing.AddDimension( transit_callback_index, 0, # no slack 100, # vehicle maximum capacities True, # start cumul to zero dimension_name) capacity_dimension = routing.GetDimensionOrDie(dimension_name) for i, demand in enumerate(data['demands']): index = manager.NodeToIndex(i) capacity_dimension.SetDemand(index, demand) for vehicle_id in range(data['num_vehicles']): index = routing.Start(vehicle_id) capacity_dimension.CumulVar(index).SetRange(data['vehicle_capacities'][vehicle_id], data['vehicle_capacities'][vehicle_id]) search_parameters = pywrapcp.DefaultRoutingSearchParameters() search_parameters.first_solution_strategy = ( routing_enums_pb2.FirstSolutionStrategy.PARALLEL_CHEAPEST_INSERTION) solution = routing.SolveWithParameters(search_parameters) if solution: print_solution(data, manager, routing, solution) if __name__ == '__main__': main() ``` Note that this is just a basic implementation and can be modified to suit specific requirements and constraints of individual problem instances.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值