日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

C#实现指派问题的匈牙利算法(运筹学)

發布時間:2023/12/14 C# 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C#实现指派问题的匈牙利算法(运筹学) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

代碼平臺

VS2019(32位) + Office2019(64位)

注意事項

1.運行過程中出現未注冊JET.OLEDB.12.0錯誤信息,需要下載AccessDatabaseEngine數據訪問組件,2010版本即可,下載時需要注意,AccessDatabaseEngine位數應該與office位數一致!!
2.本程序只解決行列相等的矩陣對應的指派問題,不考慮因行列不相等而引入的虛擬矩陣;
3.本程序的費用矩陣存儲于Excel,存儲格式為:

4.代碼可復制,根據具體情況,僅修改static void Main(string[] args)函數中的Excel表絕對路徑strPath和Sheet表名sheetName字段即可運行;
5.步驟1~4詳細列出,是為了防止小白不會用,對于大佬,完全可以讀懂程序,隨意修改;
6.本代碼不是本人原創,參考來源https://blog.csdn.net/weixin_33939380/article/details/85401031,在此感謝博主!
7.初心不變,以便以后查看。代碼若有錯誤之處,望路過的大佬指正,感謝!

代碼

using System; using System.Collections.Generic; using System.Drawing; using System.Data; using System.Data.OleDb;namespace Hungarian_Algorithm {class Program{static void Main(string[] args){//Excel表絕對路徑string strPath = @"E:\VS2019\C#\Winform\LuoPeng_Algorithm\obj\Debug\DataSheets.xlsx";//Excel表中的數據表名稱string sheetName = "ExpenseData";Console.WriteLine("程序開始");DataTable dt = new DataTable();DataFromExcel dataFromExcel = new DataFromExcel(sheetName, strPath);dt = dataFromExcel.dt;Hungarian H = new Hungarian(dt.Rows.Count, dt.Rows.Count, dt);H.Calculation();for (int i = 0; i < H.listResult.Count; i++){Console.WriteLine("{0}{1}", H.listResult[i].X, H.listResult[i].Y);Console.WriteLine();}Console.WriteLine("程序結束");Console.ReadLine();}}/// <summary>/// 匈牙利算法--運輸指派問題類/// </summary>class Hungarian{private double[,] expenseData;//費用矩陣public List<Point> listResult = new List<Point>();//最優解集private int rowX;//矩陣的行數private int rowY;//矩陣的列數/// <summary>/// 構造函數--初始化費用矩陣/// </summary>/// <param name="rowx">矩陣行數</param>/// <param name="rowy">矩陣列數</param>public Hungarian(int rowx, int rowy, DataTable dataTable){rowX = rowx;rowY = rowy;expenseData = new double[rowX, rowY];//填充費用矩陣for (int i = 0; i < dataTable.Rows.Count; i++){for (int j = 0; j < dataTable.Columns.Count-1; j++){expenseData[i, j] = Convert.ToDouble(dataTable.Rows[i][j+1]);}}}/// <summary>/// Hungarian算法迭代求解/// </summary>public void Calculation(){Step1();while (!Step2()){Step3();}}/// <summary>/// Hungarian算法第一步:行、列找最小值,然后行、列分別減去該最小值,得到等效矩陣/// </summary>private void Step1(){//行操作for (int x = 0; x < rowX; x++){double minX = double.MaxValue;//找到每行最小的值for (int y = 0; y < rowY; y++){if (expenseData[x, y] < minX){minX = expenseData[x, y];}}//讓該行所有元素減去該行最小值for (int y = 0; y < rowY; y++){expenseData[x, y] -= minX;}}//列操作for (int y = 0; y < rowY; y++){double minY = double.MaxValue;//找到每列最小的值for (int x = 0; x < rowX; x++){if (expenseData[x, y] < minY){minY = expenseData[x, y];}}//讓該列所有元素減去該列最小值for (int x = 0; x < rowX; x++){expenseData[x, y] -= minY;}}}/// <summary>/// Hungarian算法第二步:檢驗各行,對碰上的第一個零做True記號,同列其余零元素也做True記號;/// </summary>/// <returns>True:找到最優值; False:未找到最優值</returns>private bool Step2(){listResult.Clear();//最優解集合清零bool[,] isDelete = new bool[rowX, rowY];//標記元素是否為刪除狀態List<ZZeroNode> zeroNodes = new List<ZZeroNode>();//存儲包含零元素的各行索引及零元素個數//填充zeroNodesfor (int x = 0; x < rowX; x++){int zeroNum = 0;for (int y = 0; y < rowY; y++){if (expenseData[x, y] == 0){zeroNum++;}}if (zeroNum > 0){zeroNodes.Add(new ZZeroNode(x, zeroNum));}}//按零元素個數對zeroNodes進行排序zeroNodes.Sort(ZZeroNode.Cmp);//從零較少的行開始,尋找獨立零元素,填充listResultwhile (zeroNodes.Count > 0){ZZeroNode node = zeroNodes[0];if (node.ZeroNum <= 0){zeroNodes.RemoveAt(0);}else{for (int y = 0; y < rowY; y++){if (expenseData[node.X, y] == 0 && !isDelete[node.X, y]){listResult.Add(new Point(node.X, y));zeroNodes.RemoveAt(0);//刪除與該零在同一列的其他零for (int x = 0; x < rowX; x++){if (expenseData[x, y] == 0){isDelete[x, y] = true;for (int i = 0; i < zeroNodes.Count; i++){if (zeroNodes[i].X == x){zeroNodes[i].ZeroNum--;}}}}break;}}}zeroNodes.Sort(ZZeroNode.Cmp); //添加方法給委托,進行排序}return listResult.Count == rowX;}/// <summary>/// Hungarian算法第三步:找出最少數目的垂直與水平刪除線來包含所有的零至少一次/// </summary>private void Step3(){bool[,] isDelete = new bool[rowX, rowY];//意義同上for (int x = 0; x < rowX; x++){for (int y = 0; y < rowY; y++){if (expenseData[x, y] == 0 && !isDelete[x, y]){int xc = 0;//記錄y列零元素個數int yc = 0;//記錄x行零元素個數//y列中其余零元素個數之和for (int nx = 0; nx < rowX; nx++){if (nx != x && expenseData[nx, y] == 0){xc++;}}//x行中其余零元素個數之和for (int ny = 0; ny < rowY; ny++){if (ny != y && expenseData[x, ny] == 0){yc++;}}//將最多零個數的列標記為Trueif (xc > yc){for (int xx = 0; xx < rowX; xx++){isDelete[xx, y] = true;}}//將最多零個數的行標記為Trueelse{for (int yy = 0; yy < rowY; yy++){isDelete[x, yy] = true;}}}}}//找出未被劃線的元素中的最小值double k = double.MaxValue;for (int x = 0; x < rowX; x++){for (int y = 0; y < rowY; y++){if (!isDelete[x, y]){if (expenseData[x, y] < k){k = expenseData[x, y];}}}}//未被劃線各行所有元素減去最小值kfor (int x = 0; x < rowX; x++){for (int y = 0; y < rowY; y++){if (!isDelete[x, y]){for (int y1 = 0; y1 < rowY; y1++){expenseData[x, y1] -= k;}break;}}}//若造成負值,則將該列加上K,形成新矩陣后回到Step2for (int x = 0; x < rowX; x++){for (int y = 0; y < rowY; y++){if (expenseData[x, y] < 0){for (int x1 = 0; x1 < rowX; x1++){expenseData[x1, y] += k;}break;}}}}}/// <summary>/// 行零數量類/// </summary>class ZZeroNode{public int X;//行索引public int ZeroNum;//X行所含零的個數/// <summary>/// 構造函數/// </summary>/// <param name="x">行索引</param>/// <param name="zeroNum">零個數</param>public ZZeroNode(int x, int zeroNum){X = x;ZeroNum = zeroNum;}/// <summary>/// 比較函數(若a小于b,則比較結果返回小于0的值;若a等于b,則比較結果返回等于0的值;若a大于b,則比較結果返回大于0的值)/// </summary>/// <param name="a">比較值1</param>/// <param name="b">比較值2</param>/// <returns>比較結果(整數值)</returns>public static int Cmp(ZZeroNode a, ZZeroNode b){return a.ZeroNum.CompareTo(b.ZeroNum);}}/// <summary>/// 從Excel中讀取費用矩陣/// </summary>class DataFromExcel{public DataTable dt = new DataTable();//供外部調用的數據表private string sheetName;//DataSheets中的表名/// <summary>/// 連接EXCEL并讀取數據/// </summary>/// <param name="filePath">數據表路徑</param>/// <returns>數據集</returns>private DataTable LoadDataFormExcel(string filePath){string strConn;//連接字符串///Provider=Microsoft.Ace.OleDa.12.0表示數據源類型///Data Source:數據源絕對路徑///Extended Properties為Excel拓展參數:HDR表示第一行是否為字段名,HDR=1表示第一行為字段名,否則無字段名///IMEX表示對同一列中有混合數據類型的列,是統一按字符型處理,還是將個別不同類型的值讀為BDNull,1為混合,2為不混合strConn = "Provider=Microsoft.Ace.OleDb.12.0;Data Source=" + filePath + ";Extended Properties='Excel 12.0;HDR=Yes;IMEX=1;'";OleDbConnection oleConn = new OleDbConnection(strConn);DataTable oleDsExcel = new DataTable();try{oleConn.Open();string sql = "";OleDbDataAdapter oleDaExcel;sql = "SELECT * FROM [" + sheetName + "$] where 指標 is not null";oleDaExcel = new OleDbDataAdapter(sql, oleConn);oleDaExcel.Fill(oleDsExcel);}catch (Exception err){Console.WriteLine("數據綁定Excel失敗!!\n失敗原因:" + err.Message, "提示信息");}finally{oleConn.Close();}return oleDsExcel;}/// <summary>/// 構造函數/// </summary>public DataFromExcel(string _sheetName,string _filePath) {sheetName = _sheetName;dt = LoadDataFormExcel(_filePath);}} }

總結

以上是生活随笔為你收集整理的C#实现指派问题的匈牙利算法(运筹学)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。