/// <summary> /// Obtém a matriz de compatiblidade através dos custos. /// </summary> /// <param name="costsMatrix">A matriz dos custos.</param> /// <param name="sortedElements"> /// O contentor para o conjunto de elementos ordenados por custo. /// </param> /// <returns> /// A matriz de compatibilidade ordenada de acordo como os elementos se encontram na matriz. /// </returns> private BitArraySymmetricMatrix GetCompatibilityMatrix( ILongSparseMathMatrix <CostsType> costsMatrix, InsertionSortedCollection <CoordsElement> sortedElements) { // Efectua a contagem de elementos atribuídos na matriz. var elementsNumber = 0; foreach (var line in costsMatrix.GetLines()) { foreach (var column in line.Value) { ++elementsNumber; } } var result = new BitArraySymmetricMatrix(elementsNumber, elementsNumber, false); var processedColumns = new List <CoordsElement> [costsMatrix.GetLength(0)]; for (int i = 0; i < processedColumns.Length; ++i) { processedColumns[i] = new List <CoordsElement>(); } var currentIndex = 0; foreach (var line in costsMatrix.GetLines()) { foreach (var column in line.Value) { if (column.Key != line.Key) { var element = new CoordsElement(line.Key, column.Key, currentIndex, column.Value); sortedElements.Add(element); var processedColumn = processedColumns[column.Key]; foreach (var processed in processedColumn) { result[processed.CompatibilityIndex, currentIndex] = true; } processedColumn.Add(element); // Processa o pivor processedColumn = processedColumns[line.Key]; foreach (var processed in processedColumn) { result[processed.CompatibilityIndex, currentIndex] = true; } ++currentIndex; } } } return(result); }
/// <summary> /// Permite obter a próxima variável não compatível associada à matriz de compatibilidade. /// </summary> /// <param name="last">A última variável a ser escolhida.</param> /// <param name="board">A intersecção de todas as linhas de compatibilidade.</param> /// <param name="compatibility">A matriz de compatibilidade.</param> /// <returns>O índice da próxima variável e -1 caso não seja possível obtê-lo.</returns> private int GetNextVariableIndex( int last, InsertionSortedCollection <CoordsElement> orderedCosts, BitArray board, BitArraySymmetricMatrix compatibility) { var result = -1; var length = compatibility.GetLength(0); for (int i = last + 1; i < length; ++i) { var lineIndex = orderedCosts[i].CompatibilityIndex; var isNotCompatible = true; for (int j = 0; j < i; ++j) { var columnIndex = orderedCosts[j].CompatibilityIndex; var compatibilityItem = compatibility[lineIndex, columnIndex]; if (compatibilityItem && board[columnIndex]) { isNotCompatible = false; j = i; } } if (isNotCompatible) { for (int j = i + 1; j < length; ++j) { var columnIndex = orderedCosts[j].CompatibilityIndex; var compatibilityItem = compatibility[lineIndex, columnIndex]; if (compatibilityItem && board[j]) { isNotCompatible = false; j = length; } } if (isNotCompatible) { result = i; board[orderedCosts[i].CompatibilityIndex] = true; i = length; } } } return(result); }
/// <summary> /// Inicializa todas as variáveis estabelecendo uma solução inicial. /// </summary> /// <param name="affectations">A matriz das afectações.</param> /// <param name="results">O vector que contém os resultados.</param> /// <returns>A solução inicial.</returns> /// <exception cref="ArgumentNullException">Se as afectações forem nulas.</exception> /// <exception cref="MathematicsException">Se não existirem elementos para afectar.</exception> public InsertionSortedCollection <int>[] Init( int[][] affectations, int[] results) { if (affectations == null) { throw new ArgumentNullException("affectations"); } if (affectations.Length == 0) { throw new MathematicsException("There's no elements to affect."); } results = new int[affectations.Length]; this.ClearResults(results); var innerAffectations = new InsertionSortedCollection <int> [affectations.Length]; for (int i = 0; i < affectations.Length; ++i) { if (affectations[i] == null) { throw new MathematicsException(string.Format( "Element {0} has no positions to be affected.", i)); } if (affectations[i].Length == 0) { throw new MathematicsException(string.Format( "Element {0} has no positions to be affected.", i)); } innerAffectations[i] = new InsertionSortedCollection <int>(Comparer <int> .Default, true); foreach (var item in affectations[i]) { innerAffectations[i].Add(item); } } return(innerAffectations); }
/// <summary> /// Corre o algoritmo. /// </summary> /// <returns>O resultado da execução.</returns> /// <param name="affectations">A lista de correspondências possíveis.</param> public HungarianMatchingOutput Run(int[][] affectations) { var results = default(int[]); var innerAffectations = this.Init(affectations, results); var maximumDomainAffected = default(int[]); var maximumTargetAffected = default(int[]); var hasSolution = false; InsertionSortedCollection <int> domainSubset = new InsertionSortedCollection <int>( Comparer <int> .Default, true); InsertionSortedCollection <int> targetSubset = new InsertionSortedCollection <int>( Comparer <int> .Default, true); InsertionSortedCollection <int> neighboursSubset = new InsertionSortedCollection <int>( Comparer <int> .Default, true); List <Tuple <int, int> > alternatingPath = new List <Tuple <int, int> >(); int domainFree = -1; int found = -1; int targetUncommon = -1; int state = 0; while (state != -1) { switch (state) { case 0: for (int i = 0; i < innerAffectations.Length; ++i) { if (innerAffectations[i].Count != 0) { int temp = innerAffectations[i].First; results[i] = temp; domainSubset.Add(i); i = innerAffectations.Length; // Termina o ciclo de forma elegante. } } state = 1; break; case 1: targetSubset.Clear(); domainSubset.Clear(); neighboursSubset.Clear(); alternatingPath.Clear(); domainFree = -1; for (int i = 0; i < results.Length; ++i) { if (results[i] == -1) { domainFree = i; domainSubset.Add(domainFree); i = results.Length; } } // Uma solução existe. if (domainFree == -1) { domainSubset.Clear(); neighboursSubset.Clear(); for (int i = 0; i < innerAffectations.Length; ++i) { domainSubset.Add(i); } foreach (var item in domainSubset) { foreach (var possibleAffectations in innerAffectations[item]) { neighboursSubset.Add(possibleAffectations); } } maximumDomainAffected = domainSubset.ToArray(); maximumTargetAffected = neighboursSubset.ToArray(); hasSolution = true; state = -1; } else { state = 2; } break; case 2: foreach (var item in domainSubset) { foreach (var possibleAffectations in innerAffectations[item]) { neighboursSubset.Add(possibleAffectations); } } targetUncommon = -1; if (!neighboursSubset.TryFindValueNotIn(targetSubset, out targetUncommon)) { maximumDomainAffected = domainSubset.ToArray(); maximumTargetAffected = neighboursSubset.ToArray(); state = -1; } else { found = -1; for (int i = 0; i < results.Length; ++i) { if (results[i] == targetUncommon) { found = i; domainSubset.Add(found); targetSubset.Add(targetUncommon); i = results.Length; } } if (found == -1) { results[domainFree] = targetUncommon; state = 1; } else { alternatingPath.Add(new Tuple <int, int>(domainFree, targetUncommon)); state = 3; } } break; case 3: foreach (var item in domainSubset) { foreach (var possibleAffectations in innerAffectations[item]) { neighboursSubset.Add(possibleAffectations); } } targetUncommon = -1; if (!neighboursSubset.TryFindValueNotIn(targetSubset, out targetUncommon)) { maximumDomainAffected = domainSubset.ToArray(); maximumTargetAffected = neighboursSubset.ToArray(); state = -1; } else { alternatingPath.Add(new Tuple <int, int>(found, targetUncommon)); found = -1; for (int i = 0; i < results.Length; ++i) { if (results[i] == targetUncommon) { found = i; domainSubset.Add(found); targetSubset.Add(targetUncommon); i = results.Length; } } if (found == -1) { foreach (var pair in alternatingPath) { results[pair.Item1] = pair.Item2; } state = 1; } } break; } } return(new HungarianMatchingOutput( results, maximumDomainAffected, maximumTargetAffected, hasSolution)); }
/// <summary> /// Implementa o algoritmo de compatibilidade sobre um conjunto de componentes conexas. /// </summary> /// <param name="mediansNumber">O número de medianas a serem escolhidas.</param> /// <param name="costsMatrix">A matriz de custos.</param> /// <returns>O resultado da execução do algoritmo.</returns> public GreedyAlgSolution <CostsType>[] Run(int mediansNumber, List <ILongSparseMathMatrix <CostsType> > costsMatrix) { if (mediansNumber < costsMatrix.Count) { throw new ArgumentException( "The number of medians to choose must be at least equal to the number of components."); } if (costsMatrix == null) { throw new ArgumentNullException("costsMatrix"); } else if (costsMatrix.Count == 0) { throw new ArgumentException("No costs matrix was provided."); } else { var nodesNumber = this.CountNodes(costsMatrix); List <InsertionSortedCollection <CoordsElement> > sortedCosts = new List <InsertionSortedCollection <CoordsElement> >(); List <BitArraySymmetricMatrix> compatibilityMatrices = new List <BitArraySymmetricMatrix>(); for (int i = 0; i < costsMatrix.Count; ++i) { var costsComparer = new CoordsElementComparer(this.costsComparer); var insertedSorted = new InsertionSortedCollection <CoordsElement>(costsComparer, false); var compatibilityMatrix = this.GetCompatibilityMatrix(costsMatrix[i], insertedSorted); compatibilityMatrices.Add(compatibilityMatrix); sortedCosts.Add(insertedSorted); } var chosenNumber = nodesNumber - mediansNumber; var chosenIndices = new List <CoordsElement> [costsMatrix.Count]; var currentBoards = new BitArray[costsMatrix.Count]; var currentIndices = new int[costsMatrix.Count]; // Obtém as primeiras variáveis for (int i = 0; i < compatibilityMatrices.Count; ++i) { var currentCompatibilityMatrix = compatibilityMatrices[i]; var matrixLines = currentCompatibilityMatrix.GetLength(0); var array = new BitArray(matrixLines); chosenIndices[i] = new List <CoordsElement>(); currentBoards[i] = array; currentIndices[i] = 0; array[sortedCosts[i][0].CompatibilityIndex] = true; } var resultCosts = new CostsType[costsMatrix.Count]; for (int i = 0; i < resultCosts.Length; ++i) { resultCosts[i] = this.costsRing.AdditiveUnity; } for (int i = 0; i < chosenNumber; ++i) { var j = 0; var found = currentIndices[j++]; while (j < currentIndices.Length && found == -1) { found = currentIndices[j++]; } if (found == -1) { return(this.GetSolution(resultCosts, chosenIndices, costsMatrix)); } else { var foundIndex = j - 1; var currentSorteCosts = sortedCosts[foundIndex]; var minCost = currentSorteCosts[found].Value; while (j < currentIndices.Length) { var currentIndex = currentIndices[j]; var currentCost = currentSorteCosts[currentIndex].Value; if (this.costsComparer.Compare(currentCost, minCost) < 0) { foundIndex = j; found = currentIndex; minCost = currentCost; } ++j; } // Actualiza os valores. chosenIndices[foundIndex].Add(currentSorteCosts[found]); resultCosts[foundIndex] = this.costsRing.Add(resultCosts[foundIndex], minCost); var currentCompatibility = compatibilityMatrices[foundIndex]; var currentBoard = currentBoards[foundIndex]; currentIndices[foundIndex] = this.GetNextVariableIndex( found, sortedCosts[foundIndex], currentBoard, currentCompatibility); } } return(this.GetSolution(resultCosts, chosenIndices, costsMatrix)); } }