1. 程式人生 > >線性規劃網路流 :工廠最大效益——單純形演算法【超詳+題解】

線性規劃網路流 :工廠最大效益——單純形演算法【超詳+題解】

問題:某食品加工廠一共有三個車間,第一車間用 1 個單位的原料 N 可以加工 5 個單位的產品 A 或 2 個單位的產品 B。產品 A 如果直接售出,售價為 10 元,如果在第二車間繼續加工,則需要額外加工費 5 元,加工後售價為 19 元。產品 B 如果直接售出,售價 16 元,如果在第三車間繼續加工,則需要額外加工費 4 元,加工後售價為 24 元。原材料 N 的單位購入價為 5 元,每工時的資是15 元,第一車間加工一個單位的 N,需要 0.05 個工時,第二車間加工一個單位需要 0.1 工時,第三車間加工一個單位需要 0.08工時。每個月最多能得到 12000 單位的原材料 N,工時最多為 1000 工時。如何安排生產,才能使工廠的效益最大呢?

這種題先想想,它就是一道高中數學題,所以先不管程式碼怎麼設計,先把目標函式和約束條件推出來啦~

先來設變數、;

x1:產品 A 的售出量。 x2:產品 A 在第二車間加工後的售出量。 x3:產品 B 的售出量。 x4:產品 B 在第三車間加工後的售出量。 x5:第一車間所用原材料數量。 目標函式和約束條件如下:

maxz=10x1+12.5x2+16x3+18.8x4-5.75x5;

約束條件:

   x1+x2-5x5=0;

{  x3+x4-2x5=0;

   x5≤12000;

  0.05x5+0.1x3+0.08x4≤1000;

  xi(i=1, 2, 3, 4, 5)

好了,最重要的都能寫出來後面就是演算法和設計程式碼的問題了。

解決這種線性規劃問題一般都採用單純形演算法,即用一個單純形表來儲存上面推出來的條件。

首先在儲存之前需要將線性規劃轉化為標準型。

把不等式變為等式,如果目標函式是要求最小值,則需要轉化為最大值,即新增一個負號,在最好求出來的最優解新增一個負號。這道題不需要這樣的操作。

約束條件中等式中只出現一個等式的變數為基本變數,其餘為非基本變數,在目標函式中的非基本變數的係數為檢驗數,目標函式若有基本變數,則將基本變數用非基本變數來表示:

maxz=2.5x2+2.8x4+76.5x5;

約束條件:

   x1+x2-5x5=0;

   x3+x4-2x5=0;

 { x5+x6=12000;

  0.05x5+0.1x3+0.08x4+x7=1000;

  xi≥0(i=1,2,3,4,5,6,7)

單純形表 :

 (1)判斷是否得到最優解:

①:若所有的檢驗數都小於零,則已獲得最優解,演算法結束。

②:若檢驗數中存在正數,而某一正數所對應的列向量的各分量都小於等於零,則線性規劃問題無界,演算法結束。

③:若檢驗數中有正數,且他們所對應的列向量有正數,則繼續下面的計算。

(2)選入基變數

選取所有正檢驗數中最大的一個,記為 Ce,其對應的非基本變數為 Xe稱為入基變數,Xe對應的列向量[a1e,a2e,…,ame]^T.為入基列.

(3) 選離基變數

選離基變數 選取“常數列元素/入基列元素”正比值的最小者,所對應的非基本變數 xk為離基變數。xk對應的行向量[ak1,ak2,…,akn]為離基行。

(4)換基變換

(5)換基變換 在單純形表上將入基變數和離基變數互換位置,即 x3 和 x5 交換位置,換基變換之後如圖 7-5 所示。

(6)計算新的單純形表

按以下方法計算新的單純形表: 4 個特殊位置如下: • 入基列=−原值/交叉位值(不包括交叉位)。 • 離基行=原值/交叉位值(不包括交叉位)。 • 交叉位=原值取倒數。 • c0 位=原值+同行入基列元素*同列離基行元素/交叉位值。

一般位置元素=原值−同行入基列元素*同列離基行元素/交叉位值。

新表:

然後繼續從(1)開始,直到 找到最優解或者判定無界停止。

程式碼:

#include<iostream>
#include<math.h>
#include<iomanip>
#include<stdio.h>
using namespace std;
float kernel[100][100];
char FJL[100] = {};
char JL[100] = {};
int n, m;
void print()
{
	cout << endl;
	cout << "---------單純形表如下:----------" << endl;
	cout << "   ";
	cout << setw(7) << "b ";
	for (int i = 1; i <= m; i++)
		cout << setw(7) << "X" << FJL[i];
	cout << endl;
	cout << "c ";
	for (int i = 0; i <= n; i++)
	{
		if (i >= 1)
		{
			cout << "X" << JL[i];
		}
		for (int j = 0; j <= m; j++)
			cout << setw(7) << kernel[i][j] << " ";
		cout << endl;
	}
}
void DCXA()
{
	float max1, max2, min;
	int e = -1, k = -1;
	while (1)
	{
		max1 = 0; max2 = 0;
		min = 99999999.9;
		//入基變數
		for (int i = 1; i <= m; i++)
		{
			if (max1 < kernel[0][i])
			{
				max1 = kernel[0][i];
				e = i;
			}
		}
		if (max1 <= 0)
		{
			cout << endl;
			cout << "獲得最優解: " << kernel[0][0] << endl;
			break;
		}
		for (int i = 1; i <= n; i++)
		{
			if (max2 < kernel[i][e])
			{
				max2 = kernel[i][e];
			}
			float temp = kernel[i][0] / kernel[i][e];
			if (temp > 0 && temp < min)
			{
				min = temp;
				k = i;
			}
		}
		cout << "入基變數" << "X" << FJL[e] << " ";
		cout << "離基變數" << "X" << JL[k] << " ";
		if (max2 == 0)
		{
			cout << "解無屆" << endl;
			break;
		}
		//變基變換
		char temp = FJL[e];
		FJL[e] = JL[k];
		JL[k] = temp;
		//更新非入基列和非離基行的所有位置的元素
		for (int i = 0; i <= n; i++)
		{
			if (i != k)
			{
				for (int j = 0; j <= m; j++)
				{
					if (j != e)
					{
						if (i == 0 && j == 0)
							kernel[i][j] = kernel[i][j] + kernel[i][e] * kernel[k][j] / kernel[k][e];
						else
							kernel[i][j] = kernel[i][j] - kernel[i][e] * kernel[k][j] / kernel[k][e];
					}
				}
			}
		}
		//更新入基列元素(不包括交叉為)
		for (int i = 0; i <= n; i++)
		{
			if (i != k)
			{
				kernel[i][e] = -kernel[i][e] / kernel[k][e];
			}
		}
		//更新離基行元素(不包括交叉位)
		for (int i = 0; i <= m; i++)
		{
			if (i != e)
			{
				kernel[k][i] = kernel[k][i] / kernel[k][e];
			}
		}
		kernel[k][e] = 1 / kernel[k][e];
		print();
	}
}
int main()
{
	//非基本變數個數和非基本變數下標
	cin >> m;
	for (int i = 1; i <= m; i++)
		cin >> FJL[i];
	//基本變數個數和基本變數下標
	cin >> n;
	//標準型初始單純形表引數
	for (int i = 1; i <= n; i++)
		cin >> JL[i];
	for (int i = 0; i <= n; i++)
	{
		for (int j = 0; j <= m; j++)
			cin >> kernel[i][j];
	}
	print();
	DCXA();
	return  0;
}