/// <summary> /// Obtém a solução do problema a partir das variáveis de compatibilidade. /// </summary> /// <param name="resultCosts">Os custos por componente.</param> /// <param name="variables">A lista das variáveis escolhidas.</param> /// <param name="costsMatrices">As matrizes de custos.</param> /// <returns>A solução.</returns> private GreedyAlgSolution <CostsType>[] GetSolution( CostsType[] resultCosts, List <CoordsElement>[] variables, List <ILongSparseMathMatrix <CostsType> > costsMatrices) { var result = new GreedyAlgSolution <CostsType> [variables.Length]; for (int i = 0; i < result.Length; ++i) { var integerSequence = new IntegerSequence(); integerSequence.Add(0, costsMatrices[i].GetLength(0) - 1); var currentVariables = variables[i]; for (int j = 0; j < currentVariables.Count; ++j) { var column = currentVariables[j].Column; integerSequence.Remove(column); } var algSol = new GreedyAlgSolution <CostsType>(integerSequence); algSol.Cost = resultCosts[i]; result[i] = algSol; } return(result); }
/// <summary> /// Obtém uma solução a partir duma aproximação inicial. /// </summary> /// <param name="approximateMedians">As medianas.</param> /// <param name="costs">Os custos.</param> /// <param name="niter">O número máximo melhoramentos a serem aplicados à solução encontrada.</param> /// <returns>A solução construída a partir da aproximação.</returns> public GreedyAlgSolution <CoeffType> Run( CoeffType[] approximateMedians, ILongSparseMathMatrix <CoeffType> costs, int niter) { if (approximateMedians == null) { throw new ArgumentNullException("approximateMedians"); } else if (costs == null) { throw new ArgumentNullException("costs"); } else if (approximateMedians.Length != costs.GetLength(1)) { throw new ArgumentException("The number of medians must match the number of columns in costs matrix."); } else { var settedSolutions = new IntegerSequence(); var approximateSolutions = new List <int>(); var sum = this.coeffsField.AdditiveUnity; for (int i = 0; i < approximateMedians.Length; ++i) { var currentMedian = approximateMedians[i]; if (!this.coeffsField.IsAdditiveUnity(currentMedian)) { sum = this.coeffsField.Add(sum, approximateMedians[i]); if (this.converter.CanApplyDirectConversion(currentMedian)) { var converted = this.converter.DirectConversion(currentMedian); if (converted == 1) { settedSolutions.Add(i); } else { throw new OdmpProblemException(string.Format( "The median {0} at position {1} of medians array can't be converted to the unity.", currentMedian, i)); } } else { approximateSolutions.Add(i); } } } if (this.converter.CanApplyDirectConversion(sum)) { var convertedSum = this.converter.DirectConversion(sum); if (convertedSum <= 0 || convertedSum > approximateMedians.Length) { throw new IndexOutOfRangeException(string.Format( "The medians sum {0} is out of bounds. It must be between 1 and the number of elements in medians array.", convertedSum)); } var solutionBoard = new CoeffType[approximateMedians.Length]; var marked = new BitArray(approximateMedians.Length, false); if (settedSolutions.Count == convertedSum) { var result = new GreedyAlgSolution <CoeffType>(settedSolutions); result.Cost = this.ComputeCost(settedSolutions, costs, solutionBoard, marked); return(result); } else { // Partição das mediana em dois conjuntos: as que vão falzer parte da solução e as restantes // entre as soluções aproximadas. var recoveredMedians = new List <int>(); var unrecoveredMedians = new List <int>(); var innerComparer = new InnerComparer(approximateMedians, this.comparer); approximateSolutions.Sort(innerComparer); var count = convertedSum - settedSolutions.Count; var i = 0; for (; i < count; ++i) { recoveredMedians.Add(approximateSolutions[i]); settedSolutions.Add(approximateSolutions[i]); } for (; i < approximateSolutions.Count; ++i) { unrecoveredMedians.Add(approximateSolutions[i]); } var currentCost = this.ComputeCost(settedSolutions, costs, solutionBoard, marked); // Processa as melhorias de uma forma simples caso seja possível if (unrecoveredMedians.Count > 0 && niter > 0) { var exchangeSolutionBoard = new CoeffType[solutionBoard.Length]; var currentBestBoard = new CoeffType[solutionBoard.Length]; for (i = 0; i < niter; ++i) { var itemToExchange = -1; var itemToExchangeIndex = -1; var itemToExchangeWith = -1; var itemToExchangeWithIndex = -1; var minimumCost = this.coeffsField.AdditiveUnity; for (int j = 0; j < recoveredMedians.Count; ++j) { for (int k = 0; k < unrecoveredMedians.Count; ++k) { var replacementCost = this.ComputeReplacementCost( unrecoveredMedians[k], recoveredMedians[j], settedSolutions, costs, solutionBoard, exchangeSolutionBoard); if (this.comparer.Compare(replacementCost, minimumCost) < 0) { // Aceita a troca itemToExchange = recoveredMedians[j]; itemToExchangeIndex = j; itemToExchangeWith = unrecoveredMedians[k]; itemToExchangeWithIndex = k; minimumCost = replacementCost; var swapBestBoard = currentBestBoard; currentBestBoard = exchangeSolutionBoard; exchangeSolutionBoard = swapBestBoard; } } } if (itemToExchange == -1 || itemToExchangeWith == -1) { i = niter - 1; } else { // Efectua a troca var swapSolutionBoard = solutionBoard; solutionBoard = currentBestBoard; currentBestBoard = swapSolutionBoard; currentCost = this.coeffsField.Add(currentCost, minimumCost); settedSolutions.Remove(itemToExchange); settedSolutions.Add(itemToExchangeWith); var swap = recoveredMedians[itemToExchangeIndex]; recoveredMedians[itemToExchangeIndex] = unrecoveredMedians[itemToExchangeWithIndex]; unrecoveredMedians[itemToExchangeWithIndex] = swap; } } } return(new GreedyAlgSolution <CoeffType>(settedSolutions) { Cost = currentCost }); } } else { throw new OdmpProblemException("The sum of medians can't be converted to an integer."); } } }