/// <summary> /// Produces an N x 2 array of strings showing the best match-up of two lists of strings. /// Used to match schematic pins with SPICE pins, for example. /// </summary> /// <param name="firstPinNames">The first list of strings, typically schematic pin names.</param> /// <param name="secondPinNames">The second list of strings, typically SPICE pin names.</param> /// <returns>N x 2 array of strings, showing the best match-up between two lists of strings. /// The first column of the array has strings from the first list in a sorted order. /// The second column of the array has a permutation of strings from the second list, so that /// strings on the same row will tend to be close matches. /// If one list is longer than the other, the shorter one will be extended with "" strings.</returns> /// <remarks>This method uses a modified Levenshtein algorithm to tell how close two strings /// are to matching, and the Hungarian algorithm to optimize the overall matchup.</remarks> /// <seealso cref="http://en.wikipedia.org/wiki/Levenshtein_distance"/> /// <seealso cref="http://en.wikipedia.org/wiki/Hungarian_algorithm"/> static public string[,] GetPinMatches(List <string> firstPinNames, List <string> secondPinNames) { int len1 = firstPinNames.Count; int len2 = secondPinNames.Count; int pinListSize = Math.Max(len1, len2); // Copy the pin lists so the new lists can be extended, if needed, to the same size with empty strings. List <string> newS1 = new List <string>(firstPinNames); newS1.Sort(); while (newS1.Count < pinListSize) { newS1.Add(""); } List <string> newS2 = new List <string>(secondPinNames); newS2.Sort(); while (newS2.Count < pinListSize) { newS2.Add(""); } // Now, both new lists of pins have the same length. // Compute a square cost matrix for the pins based on their distances. int[,] costMatrix = new int[pinListSize, pinListSize]; for (int rows = 0; rows < pinListSize; rows++) { for (int cols = 0; cols < pinListSize; cols++) { costMatrix[rows, cols] = Costs.CostBetweenTwoPins(newS1[rows], newS2[cols]); } } // Find the least cost assignment of pins to pins int[] pinMap = HungarianAlgorithm.FindAssignments(costMatrix); Debug.Assert(pinMap.Count() == pinListSize); // Create the output list of matched pin strings string[,] rVal = new string[pinListSize, 2]; for (int i = 0; i < pinListSize; i++) { rVal[i, index1] = newS1[i]; rVal[i, index2] = newS2[pinMap[i]]; } return(rVal); }
public void test04HungarianAlgorithmCheck() { /* Example from Layman's Explanation, http://en.wikipedia.org/wiki/Hungarian_algorithm * * Clean bathroom Sweep floors Wash windows * Jim $1 $3 $3 * Steve $3 $2 $3 * Alan $3 $3 $2 */ int[,] costs = { { 1, 3, 3 }, { 3, 2, 3 }, { 3, 3, 2 } }; int[] expected = { 0, 1, 2 }; // Jim cleans the bathroom, Steve sweeps the floors and Alan washes the windows. int[] results = HungarianAlgorithm.FindAssignments(costs); Assert.Equal(expected, results); }