Greedy Algorithm 和 Hill Algorithm解决Capacitated Facility Location Problem

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 objective is to minimize the sum of the opening cost 

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

问题解决方法描述:

Capacitated Facility Location Problem (CFLP) is generalization of the Simple Plant Location Problem. In contrast to that we now suppose each facility can product limited quantity of produce. It is importance and quite prevalent assumption. And although mathematical models of those problems not  very differ, but solving methods for CFLP are more difficult. The most efficient  methods for CFPL are Lagrangen relaxation methods and the matrix column generation method.

Now we discrabe the mathematical model as integer programming problem. Let a set I = {1,..., I} give potential facility locations by production some uniform product. The number ci 0is the opening cost of facility at location  i,  Viis the utmost value of prodaction at this location.

A set ={1,…,J} assign clients that require service. For each pair (ij)  gijis the production and transportation costs and  pijis a value of product from fasility i needed to client j.

 Let us define the following notations:

Then the Capacitated Facility Location Problem may be written 

1、Greedy Algorithm:

一、算法思想

贪心法的基本思路:
——从问题的某一个初始解出发逐步逼近给定的目标,以尽可能快的地求得更好的解。当达到某算法中的某一步不能再继续前进时,算法停止。
该算法存在问题:
1. 不能保证求得的最后解是最佳的;
2. 不能用来求最大或最小解问题;
3. 只能求满足某些约束条件的可行解的范围。
4. greedy optimal control应该就是用这种算法求得的最优化控制。

在该情景中的应用:

1、遍历所有customer,对于每一个customer,将该customer安排到所有facility的cost进行从小到大排序

2、对排序好的cost进行遍历,假如该facility剩余的容量满足customer的要求,则将该customer安排到该facility

实现code:

Greedy.h:

#ifndef GREEDY_H
#define GREEDY_H

#include <vector>
using namespace std;  

class Greedy {
public:
   	Greedy(int facilityNum, int customerNum, vector<double> capacity, vector<double> cost, vector<double> demand, vector<vector<double>> assignment);
   	~Greedy();
   	double getResult();
   	int* get_assignment_list();
	void showResult();
private:
	double getCost(int facilityNum, int customerNum);
	int facilityNum;
	int customerNum;
	vector<double> capacity;
	vector<double> cost;
	vector<double> demand;
	vector<vector<double>> assignment;
	int *open_status;	
	int *assignment_list;
	double *remain_capacity;

};
#endif

Greedy.cpp:

#include "Greedy.h"
#include <iostream>
#include <cstring>
#include <limits.h>
using namespace std;

Greedy::Greedy(int facilityNum, int customerNum, vector<double> capacity, vector<double> cost,
						 vector<double> demand, vector<vector<double>> assignment) {
	this -> facilityNum = facilityNum;
	this -> customerNum = customerNum;
	this -> capacity = capacity;
	this -> cost = cost;
	this -> demand = demand;
	this -> assignment = assignment;
	this -> open_status = new int[facilityNum];
	this -> assignment_list = new int[customerNum];
	this -> remain_capacity = new double[facilityNum];
	for(int i = 0; i < facilityNum; i++) {
		open_status[i] = 0;
		remain_capacity[i] = capacity[i];
	}
	for(int i = 0; i < customerNum; i++)
		assignment_list[i] = 0;
}

Greedy::~Greedy() {
	delete open_status;
	delete assignment_list;
	delete remain_capacity;
}

double Greedy::getResult() {

	//int open_status[facilityNum] = {0};
	//int assignment_list[customerNum] = {0};
	//double remain_capacity[facilityNum];
	//for(int i = 0; i < capacity.size(); i++)
	//	remain_capacity[i] = capacity[i];
	
	// 按照贪心算法预估的cost值,选取最小的cost去安排设备
	for(int count = 0; count < customerNum; count++) {
		// 获取最高性价比,评估cost最小的分配
		double leastCost = INT_MAX;
		int fac = -1;
		int cus = -1;
		for(int j = 0; j < facilityNum; j++) {
			for(int i = 0; i < customerNum; i++) {
				// customer 已经分配
				if(assignment_list[i] != 0) continue;
				// 设备容量不足
				if(demand[i] > remain_capacity[j]) continue;
				// 有容量,获取贪心算法评估代价Cost
				double cost_j_i = getCost(j, i);
				//cout << j+1 << " " << i+1 << " " << cost_j_i << endl; 
				if(cost_j_i < leastCost) {
					leastCost = cost_j_i;
					fac = j+1;
					cus = i+1;
				}
			}
		}
		// 没有可进行的分配
		if(fac == -1 || cus == -1 || leastCost == INT_MAX) {
			cout << "[ERROR]" << endl;
		}
		else {
			// 进行分配
			open_status[fac-1] = 1;
			remain_capacity[fac-1] -= demand[cus-1];
			assignment_list[cus-1] = fac;
		}
	}
	// 计算总共费用
	double totalCost = 0;
	// 设备启动费用
	for(int j = 0; j < facilityNum; j++) {
		//printf("%d ", open_status[j]);
		if(open_status[j] == 1) {
			totalCost += cost[j];
		}
		//cout << remain_capacity[j] << " ";
	}
	//cout << endl;

	//printf("\n");
	// customer 费用
	for(int i = 0; i < customerNum; i++) {
		//printf("%d ", assignment_list[i]);
		totalCost += assignment[assignment_list[i]-1][i];
	}
	//printf("\n");
	
	return totalCost;
}

// 返回贪心算法中评估Cost的值
double Greedy::getCost(int facilityNum, int customerNum) {
	// 开启设备分摊平均占用费用
	double facilityCost = cost[facilityNum] * demand[customerNum] / capacity[facilityNum];
	// 消费者使用费用
	double assignmentCost = assignment[facilityNum][customerNum];
	// 返回总评估费用
	return facilityCost + assignmentCost;
}


int* Greedy::get_assignment_list() {
	return assignment_list;
}

void Greedy::showResult() {
	for(int j = 0; j < facilityNum; j++) {
		if(remain_capacity[j] != capacity[j]) {
			printf("1 ");
		}
		else printf("0 ");
	}
	printf("\n");
	/*for(int j = 0; j < facilityNum; j++) {
		printf("%0.lf ", remain_capacity[j]);
	}
	printf("\n");*/
	// customer 费用
	for(int i = 0; i < customerNum; i++) {
		printf("%d ", assignment_list[i]);
	}
	printf("\n");
}

main.cpp:

#include <fstream>  
#include <string>
#include <iostream>
#include <sstream>
#include <vector>
#include "Greedy.h"
#include "Greedy.cpp"
#include <time.h>
using namespace std;  
  
int main() {
	cout << "BEGIN" << endl;
	// 计数读取71个数据集
	int count = 1;
	// 文件路径
	string baseFileName = "../Instances/";
	

	// 循环读取71个数据集
	while(count <= 71) {
		// 获取文件名
		stringstream sstream;
		sstream << count;
		string pNum;
		sstream >> pNum;
		string fileName = "p" + pNum;

		// 流读取文件
	    ifstream in(baseFileName+fileName);
	    ofstream out("../result/result.txt");

	  	// 有该文件
	    if(in) {
	    	vector<double> sample;
	    	double d;
	    	while(in >> d) {
	    		sample.push_back(d);
	    	}
	    	
	    	
	    	// fac 和 cus 数量数据
	    	int facilityNum = sample[0];
	    	int customerNum = sample[1];

	    	// fac 的 capacity 和 cost 数据
	    	vector<double> capacity;
	    	vector<double> cost;
	    	int begin = 2;
	    	for(int i = 0; i < facilityNum; i++) {
	    		capacity.push_back(sample[2*i+begin]);
	    		cost.push_back(sample[2*i+begin+1]);
	    	}

	    	// cus 的 demand 数据
	    	vector<double> demand;
	    	begin = 2+facilityNum*2;
	    	for(int i = 0; i < customerNum; i++) 
	    		demand.push_back(sample[i+begin]);

	    	// cus 的 assignment 数据
	    	vector<vector<double>> assignment;
	    	begin = 2+facilityNum*2+customerNum;
	    	for(int j = 0; j < facilityNum; j++) {
	    		vector<double> temp;
	    		for(int i = 0; i < customerNum; i++)
	    			temp.push_back(sample[j*customerNum+i+begin]);
	    		assignment.push_back(temp);
	    	}
	    	// 方法类
			printf("------Solution1------%s------fac:%d------cus:%d---\n", fileName.c_str(), facilityNum, customerNum);
			Solution1 s1 = Solution1(facilityNum, customerNum, capacity, cost, demand, assignment);	
			clock_t start,stop;
    		start = clock();
	    	double result = s1.getResult();
	    	s1.showResult();
	    	stop = clock();
			double dur = (double)stop-start;
			printf("[Time]---%.0lfms\n", dur);
	    	printf("[Result]---%lf\n", result);
	    }
	    // 没有该文件
	    else cout <<"No Test Data Files" << endl;
	    count++;
	}

	cout << "END" << endl;
	
    return 0;
}

Result table(给出部分结果):

result table
instancesresultstimes(ms)
P1934315
P279334
P3101088
P15131878
P161718723
P331585229

P1

P2

P3

P15

P16

P33

etc..

2、Hill Algorithm:

 其实就是the matrix column generation method.

算法解释:
从当前的节点开始,和周围的邻居节点的值进行比较。 如果当前节点是最大的,那么返回当前节点,作为最大值 (既山峰最高点);反之就用最高的邻居节点来,替换当前节点,从而实现向山峰的高处攀爬的目的。如此循环直到达到最高点。


算法优缺点
优点
避免遍历,通过启发选择部分节点,从而达到提高效率的目的。

缺点

因为不是全面搜索,所以结果可能不是最佳。

算法伪代码:

function HILL-CLIMBING(problem) returns a state that is a local maximum
inputs: problem, a problem
  local variables: current, a node
                         neighbor, a node
  current <- MAKE-NODE(INITIAL-STATE[problem])
  loop do
neighbor <- a highest-valued successor of current
if VALUE[neighbor]<= VALUE[current] then return STATE[current]
current <- neighbor

 

实现code:

main.cpp
 

#include "individual.hpp"
#include <iostream>
#include <ctime>
#include <string>
#include <vector>
#include <fstream>
#include <cstdlib>
#include <cstdio>

using namespace std;

int main() {
  // Log file stream initializing //
  ofstream csvOutput;
  ofstream detailOutput;
  csvOutput.open("result.csv");
  detailOutput.open("detail.txt");
  csvOutput << ",Result,Time(s)\n";

  // Timer //
  time_t start;
  // Status monitor //
  int count = 0, scoreRecord;
  int* facilities = NULL;
  // Case info //
  int consumerNum, facilityNum;
  // Directory path //
  string rootDir = "../Instances/p";
  // Load each file and execute algorithm //
  for (int number = 1; number < 72; ++number) {
  
    try {
      Individual::init(rootDir + to_string(number));
    }
    catch(string str) {
      cout << str;
      return 0;
    }
    catch(exception e) {
      cout << "Error while initializing: " << e.what() << endl;
      return 0;
    }
    // Initializing for case //
    count = 0;
    Individual *bestScore = new Individual;
    consumerNum = Individual::getCustomerNum();
    facilityNum = Individual::getFacilityNum();

    if (facilities != nullptr) delete[] facilities;
    facilities = new int[facilityNum];
    for (int i = 0; i < facilityNum; ++i) facilities[i] = 0;
    // start timer //
    start = time(nullptr);

    while ((scoreRecord = bestScore->getCost()) == INT32_MAX) {
      delete bestScore;
      bestScore = new Individual();
      cout << "*";
    }


    cout << "*";
    do {
      auto temp3 = bestScore->getNeighborsWithTryMin();
      
      for (int i = 0; i < consumerNum; ++i) {
        if (bestScore->getCost() > temp3[i]->getCost()) {
          bestScore = temp3[i];
          bestScore->getCost();
        }
        else {
          delete temp3[i];
        }
      }
      delete []temp3;
      // Are the best still him? //
      if (bestScore->getCost() == scoreRecord) {
        time_t timeSpent = time(nullptr) - start;
        csvOutput << "p" << number << "," << scoreRecord
          << "," <<  timeSpent<< "\n";
        detailOutput << "Testcase: p" << number 
          << " Final Cost: " << scoreRecord 
          << " Time Spent: " << timeSpent << " second(s)" << endl;
        int *solution = bestScore->getSolution();
        for (int t = 0; t < consumerNum; ++t) {
          facilities[solution[t]] = 1;
        }

        for (int t = 0; t < facilityNum; ++t) {
          detailOutput << facilities[t] << " ";
        }
        detailOutput << endl;
        for (int t = 0; t < consumerNum; ++t) {
          detailOutput << solution[t] << " ";
        }
        detailOutput << endl;
        cout << "p" << number << " Done" << endl;
        break;
        
      } else {
        // Oh! A new king! //
        scoreRecord = bestScore->getCost();
        cout << "New Best At " << count << " :" << scoreRecord << endl;
        count = 0;
      }

    } while (true);
  }
  if (facilities != nullptr) delete[] facilities;
  detailOutput.close();
  csvOutput.close();
  return 0;
}

individual.h

#ifndef INDIVIDUAL_HPP
#define INDIVIDUAL_HPP
#include <string>

using std::string;


class Individual {
private:
  // Static variables from data file //
  static int    FACILITY_NUM;     // Number of facilities
  static int    CUSTOMER_NUM;     // Number of customers
  static int**  ASSIGN_COST;      // Matrix of customer's demand
  static int*   MIN_ASSIGN_COST;  // Minimal assign cost for each consumer
  static int*   MIN_ASSIGN;       // Minimal facility index
  static int*   CUSTOMER_DEMAND;  // Vector of facilities' assignment cost
  static int*   OPEN_COST;        // The cost of facility to open
  static int*   FACILITY_CAPACITY;// The capacity of each facility

  // Object's private data member //
  int*          gene;             // Gene, the strategy of assignment
  int           cost;

public:
  // Static methods //
  static void init(string filePath);              // Use data file to initialize static values
  static int  estimateCost(Individual &individual); // estimate grade for individual
  static void clearData();                        // Clear data and free memory space
  static bool isValid(int* gene);                 // Check whether a gene is valid

  static int getFacilityNum();
  static int getCustomerNum();

  // Constructors //
  Individual();
  ~Individual();
  Individual(int* gene);
  Individual(const Individual& individual);

  // Public methods //
  int*    getSolution();  // Get the solution
  int*    getGeneCopy();  // Get a copy of gene
  int     getCost();      // Get the cost
  Individual** getNeighborsWithTryMin();
  Individual& operator=(const Individual& individual);
};
#endif

individual.hpp

#include <fstream>
#include <cstdlib>
#include <algorithm>
#include "individual.hpp"

using std::ifstream;
using std::rand;
using std::swap;
#ifdef DEBUG_MODE
#include <iostream>
using std::cout;
using std::endl;
#endif

// Initialize static member variables in Individual //
int    Individual::FACILITY_NUM      = 0;     
int    Individual::CUSTOMER_NUM      = 0;     
int**  Individual::ASSIGN_COST       = nullptr;  
int*   Individual::CUSTOMER_DEMAND   = nullptr;      
int*   Individual::OPEN_COST         = nullptr; 
int*   Individual::FACILITY_CAPACITY = nullptr;
int*   Individual::MIN_ASSIGN_COST   = nullptr;
int*   Individual::MIN_ASSIGN        = nullptr;

// Clear all data and recycle memory space allocated //
void Individual::clearData() {
  if (ASSIGN_COST != nullptr){
    for (int i = 0; i < FACILITY_NUM; ++i) {
      delete[] ASSIGN_COST[i];
    }
    delete[] ASSIGN_COST;
    ASSIGN_COST = nullptr;
  }
  if (MIN_ASSIGN_COST != nullptr) {
    delete[] MIN_ASSIGN_COST;
    MIN_ASSIGN_COST = nullptr;
  }
  if (MIN_ASSIGN != nullptr) {
    delete[] MIN_ASSIGN;
    MIN_ASSIGN = nullptr;
  }
  if (CUSTOMER_DEMAND != nullptr) {
    delete[] CUSTOMER_DEMAND;
    CUSTOMER_DEMAND = nullptr;
  }
  if (OPEN_COST != nullptr) {
    delete[] OPEN_COST;
    OPEN_COST = nullptr;
  }
  if (FACILITY_CAPACITY != nullptr) {
    delete[] FACILITY_CAPACITY;
    FACILITY_CAPACITY = nullptr;
  }
}

// Initialize case info with the given file //
void Individual::init(string filePath) {
  // Create a input file stream //
  ifstream is;
  is.open(filePath); 
  double temp = 0;
  
  // If the file is not open properly //
  if (!is.is_open()) { 
    throw string("E: Unable To Open File");
    return;
  }

  // Clear existed data and free memory space
  clearData();
  
  // Number of facilities and customers //
  is >> FACILITY_NUM >> CUSTOMER_NUM;
  
  // Capacity and Open Cost //
  FACILITY_CAPACITY = new int[FACILITY_NUM];
  OPEN_COST = new int [FACILITY_NUM];
  for (int i = 0; i < FACILITY_NUM; ++i) {
    is >> FACILITY_CAPACITY[i] >> OPEN_COST[i];
  }

  // Demand of customers //
  CUSTOMER_DEMAND = new int[CUSTOMER_NUM];
  for (int i = 0; i < CUSTOMER_NUM; ++i) {
    is >> temp;
    CUSTOMER_DEMAND[i] = temp;
  }

  // Assign Cost //
  MIN_ASSIGN_COST = new int[CUSTOMER_NUM];
  MIN_ASSIGN = new int[CUSTOMER_NUM];
  for (int i = 0; i < CUSTOMER_NUM; ++i) MIN_ASSIGN_COST[i] = INT32_MAX;
  ASSIGN_COST = new int*[FACILITY_NUM];
  for (int i = 0; i < FACILITY_NUM; ++i) {
    ASSIGN_COST[i] = new int[CUSTOMER_NUM];
    for (int t = 0; t < CUSTOMER_NUM; ++t) {
      is >> temp;
      ASSIGN_COST[i][t] = temp;
      if (temp < MIN_ASSIGN_COST[t]){
        MIN_ASSIGN_COST[t] = temp;
        MIN_ASSIGN[t] = i;
      }
    }
  }

  // Debug info //
  #ifdef DEBUG_MODE
    cout << "Status after reading data:" << endl
      << "Facility Number: " << FACILITY_NUM << endl
      << "Customer Number: " << CUSTOMER_NUM << endl
      << "Assign Cost: " << endl;
    for (int i = 0; i < FACILITY_NUM; ++i) {
      for (int t = 0; t < CUSTOMER_NUM; ++t) {
        cout << ASSIGN_COST[i][t] << " ";
      }
      cout << endl;
    }
    cout << "Facility Capacities: " << endl;
    for (int i = 0; i < FACILITY_NUM; ++i) {
      cout << FACILITY_CAPACITY[i] << " ";
    }
    cout << endl
      << "Facility Open Cost: " << endl;
    for (int i = 0; i < FACILITY_NUM; ++i) {
      cout << OPEN_COST[i] << " ";
    } 
    cout << endl
      << "Customers' demand: " << endl;
    for (int i = 0; i < CUSTOMER_NUM; ++i) {
      cout << CUSTOMER_DEMAND[i] << " ";
    }
    cout << endl;
  #endif
}

// Estimate the cost of current solution //
int Individual::estimateCost(Individual& individual) {
  // If this solution is not a legal one, return the max cost //
  if (!isValid(individual.gene))
    return INT32_MAX;
  
  // Store the status of each facility //
  bool* facilities = new bool[FACILITY_NUM];
  int cost = 0;
  for (int i = 0; i < FACILITY_NUM; ++i) {
    facilities[i] = false;
  }

  // Count the cost //
  for (int i = 0; i < CUSTOMER_NUM; ++i) {
    if (facilities[individual.gene[i]] == false) {
      // Open the facility and count the cost //
      cost += OPEN_COST[individual.gene[i]];
      facilities[individual.gene[i]] = true;
    }
    // Count assignment cost //
    cost += ASSIGN_COST[individual.gene[i]][i];
  }

  // Free allocated memory space //
  delete []facilities;
  // Update and return the cost //
  return individual.cost = cost;
}

Individual::Individual() {
  // Brand new gene //
  this->gene = new int[CUSTOMER_NUM];
  
  // Sequence used to check the facility capacity //
  // int* facilities = new int[FACILITY_NUM];
  // for (int i = 0; i < FACILITY_NUM; ++i) {
  //   facilities[i] = 0;
  // }

  
  for (int i = 0; i < CUSTOMER_NUM; ++i) {
    // Random until valid //
    // do {
      // Assign a random facility to each customer //
      gene[i] = rand() % FACILITY_NUM;
    // } while (CUSTOMER_DEMAND[i] + 
    //   facilities[gene[i]] > FACILITY_CAPACITY[gene[i]]);
  }
  
  // Estimate the cost //
  cost = estimateCost(*this);
  // Free allocated memory space //
  // delete[] facilities;
}

Individual::~Individual() {
  // Free allocated memory space //
  if (gene != nullptr) delete[] gene;
}

int* Individual::getSolution() {
  return gene;
}

// Check whether this solution match requirement //
bool Individual::isValid(int* gene) {
  // Count demand //
  int* facilities = new int[FACILITY_NUM];
  bool flag = true;
  for (int i = 0; i < FACILITY_NUM; ++i) {
    facilities[i] = 0;
  }

  for (int i = 0; i < CUSTOMER_NUM; ++i) {
    facilities[gene[i]] += CUSTOMER_DEMAND[i];
    if (facilities[gene[i]] > FACILITY_CAPACITY[gene[i]]) {
      // Overflow! Not a valid solution //
      flag = false;
    }
  }

  // Free allocated memory //
  delete[] facilities;
  return flag;
}

Individual::Individual(const Individual& individual) {
  gene = new int[CUSTOMER_NUM];
  for (int i = 0; i < CUSTOMER_NUM; ++i) {
    // Copy gene //
    gene[i] = individual.gene[i];
  }
  // Copy cost //
  cost = individual.cost;
}

// Similar to copy constructor //
Individual& Individual::operator=(const Individual& individual) {
  if (gene == nullptr) gene = new int[CUSTOMER_NUM];
  for (int i = 0; i < CUSTOMER_NUM; ++i) {
    gene[i] = individual.gene[i];
  }
  cost = individual.cost;
  return *this;
}

// Get cost //
int Individual::getCost() {
  return cost;
}

// Get case info //
int Individual::getFacilityNum() {
  return FACILITY_NUM;
}
int Individual::getCustomerNum() {
  return CUSTOMER_NUM;
}

Individual** Individual::getNeighborsWithTryMin() {
  Individual** result = new Individual*[CUSTOMER_NUM];
  for (int i = 0; i < CUSTOMER_NUM; ++i) {
    auto temp = getGeneCopy();
    temp[i] = MIN_ASSIGN[i];
    result[i] = new Individual(temp);
  }
  return result;
}

// Get a copy of gene //
int* Individual::getGeneCopy() {
  int *copy = new int[CUSTOMER_NUM];
  for (int i = 0; i < CUSTOMER_NUM; ++i)
    copy[i] = gene[i];
  return copy;
}

Individual::Individual(int* gene) {
  // Copy gene //
  this->gene = gene;
  // Get Cost //
  cost = estimateCost(*this);
}

Result table(给出部分结果):

result table
instancesresultstimes(ms)
P1954624
P2889036
p161792256
p241832211
P301500121
P431191344

P1

P2

P16

P24

p30

p43

etc..

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值