/// <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);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Escolhe a referência que maximiza o ganho.
        /// </summary>
        /// <param name="chosenReferences">As referências escolhidas anteriormente.</param>
        /// <param name="currentMatrix">A matriz dos custos.</param>
        /// <param name="currentLineBoard">A linha que contém a condensação dos custos das linhas escolhidas.</param>
        /// <returns>O índice da linha corresponde à próxima referência bem como o ganho respectivo.</returns>
        public Tuple <int, ElementType> Run(
            IntegerSequence chosenReferences,
            ILongSparseMathMatrix <ElementType> currentMatrix,
            ElementType[] currentLineBoard)
        {
            var result         = default(KeyValuePair <int, ILongSparseMatrixLine <ElementType> >);
            var lines          = currentMatrix.GetLines();
            var currentMaxGain = this.ring.AdditiveInverse(this.ring.MultiplicativeUnity);
            var foundRef       = false;

            Parallel.ForEach(lines,
                             line =>
            {
                if (!chosenReferences.Contains(line.Key))
                {
                    var sum = this.ring.AdditiveUnity;

                    foreach (var column in line.Value)
                    {
                        if (column.Key != line.Key)
                        {
                            var currentValue = column.Value;
                            if (this.comparer.Compare(currentValue, currentLineBoard[column.Key]) < 0)
                            {
                                var difference = this.ring.Add(
                                    currentLineBoard[column.Key],
                                    this.ring.AdditiveInverse(currentValue));
                                sum = this.ring.Add(sum, difference);
                            }
                        }
                        else
                        {
                            sum = this.ring.Add(sum, currentLineBoard[column.Key]);
                        }
                    }

                    lock (this.lockObject)
                    {
                        if (this.comparer.Compare(currentMaxGain, sum) < 0)
                        {
                            currentMaxGain = sum;
                            result         = line;
                            foundRef       = true;
                        }
                    }
                }
            });

            if (foundRef)
            {
                foreach (var column in result.Value.GetColumns())
                {
                    if (column.Key != result.Key)
                    {
                        if (this.comparer.Compare(column.Value, currentLineBoard[column.Key]) < 0)
                        {
                            currentLineBoard[column.Key] = column.Value;
                        }
                    }
                    else
                    {
                        currentLineBoard[column.Key] = this.ring.AdditiveUnity;
                    }
                }

                return(Tuple.Create(result.Key, currentMaxGain));
            }
            else
            {
                return(Tuple.Create(-1, currentMaxGain));
            }
        }
        /// <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.");
                }
            }
        }
        /// <summary>
        /// Permite obter o valor mínimo entre as colunas da matriz para as linhas escolhidas com excepção
        /// de uma delas.
        /// </summary>
        /// <param name="chosen">As linhas escolhidas.</param>
        /// <param name="exceptLine">
        /// A linha que não vai ser considerada.</param>
        /// <param name="column">A coluna sobre a qual é calculado o mínimo.</param>
        /// <param name="costs">A matriz dos custos.</param>
        /// <returns>O valor do custo mínimo.</returns>
        private CoeffType GetMinimumFromColumn(
            IntegerSequence chosen,
            int exceptLine,
            int column,
            ILongSparseMathMatrix <CoeffType> costs)
        {
            var result           = default(CoeffType);
            var chosenEnumerator = chosen.GetEnumerator();
            var state            = chosenEnumerator.MoveNext();
            var keep             = true;

            while (state && keep)
            {
                // Pesquisa pelo primeiro elemento.
                var currentChosen = chosenEnumerator.Current;
                if (currentChosen != exceptLine)
                {
                    var currentLine = default(ILongSparseMatrixLine <CoeffType>);
                    if (costs.TryGetLine(currentChosen, out currentLine))
                    {
                        var columnValue = default(CoeffType);
                        if (currentLine.TryGetColumnValue(column, out columnValue))
                        {
                            if (this.coeffsField.IsAdditiveUnity(columnValue))
                            {
                                return(columnValue);
                            }
                            else
                            {
                                result = columnValue;
                                keep   = false;
                            }
                        }
                    }
                }

                state = chosenEnumerator.MoveNext();
            }

            if (keep)
            {
                throw new OdmpProblemException("Something went very wrong. Check if costs matrix is valid.");
            }
            else
            {
                while (state)
                {
                    var currentChosen = chosenEnumerator.Current;
                    if (currentChosen != exceptLine)
                    {
                        var currentLine = default(ILongSparseMatrixLine <CoeffType>);
                        if (costs.TryGetLine(currentChosen, out currentLine))
                        {
                            var columnValue = default(CoeffType);
                            if (currentLine.TryGetColumnValue(column, out columnValue))
                            {
                                if (this.coeffsField.IsAdditiveUnity(columnValue))
                                {
                                    return(columnValue);
                                }
                                else
                                {
                                    if (this.comparer.Compare(columnValue, result) < 0)
                                    {
                                        result = columnValue;
                                    }
                                }
                            }
                        }
                    }

                    state = chosenEnumerator.MoveNext();
                }
            }

            return(result);
        }
        /// <summary>
        /// Calcula o custo da substituição de uma mediana por outra.
        /// </summary>
        /// <param name="replacementMedian">A mediana que substitui.</param>
        /// <param name="medianToBeReplaced">A mediana que é substituída.</param>
        /// <param name="existingMedians">As medianas que forma escolhidas.</param>
        /// <param name="costs">A matriz dos custos.</param>
        /// <param name="currentSolutionBoard">O quadro de solução actual.</param>
        /// <param name="replacementSolutionBoard">O quadro da solução substituída.</param>
        /// <returns>O valor do mínimo.</returns>
        private CoeffType ComputeReplacementCost(
            int replacementMedian,
            int medianToBeReplaced,
            IntegerSequence existingMedians,
            ILongSparseMathMatrix <CoeffType> costs,
            CoeffType[] currentSolutionBoard,
            CoeffType[] replacementSolutionBoard)
        {
            Array.Copy(currentSolutionBoard, replacementSolutionBoard, currentSolutionBoard.Length);
            var result = this.coeffsField.AdditiveUnity;

            var minimum = this.GetMinimumFromColumn(
                existingMedians,
                medianToBeReplaced,
                medianToBeReplaced,
                costs);

            replacementSolutionBoard[medianToBeReplaced] = minimum;
            result = this.coeffsField.Add(result, minimum);

            var boardValue = currentSolutionBoard[replacementMedian];

            replacementSolutionBoard[replacementMedian] = this.coeffsField.AdditiveUnity;
            result = this.coeffsField.Add(
                result,
                this.coeffsField.AdditiveInverse(boardValue));

            // Remove a linha a ser substituída.
            var currentRow = default(ILongSparseMatrixLine <CoeffType>);

            if (costs.TryGetLine(medianToBeReplaced, out currentRow))
            {
                foreach (var column in currentRow.GetColumns())
                {
                    if (column.Key != medianToBeReplaced && column.Key != replacementMedian)
                    {
                        if (this.coeffsField.Equals(column.Value, replacementSolutionBoard[column.Key]))
                        {
                            var current = replacementSolutionBoard[column.Key];
                            minimum = this.GetMinimumFromColumn(
                                existingMedians,
                                medianToBeReplaced,
                                column.Key,
                                costs);
                            replacementSolutionBoard[column.Key] = minimum;
                            var temp = this.coeffsField.Add(
                                minimum,
                                this.coeffsField.AdditiveInverse(current));
                            result = this.coeffsField.Add(result, temp);
                        }
                    }
                }
            }

            // Insere a linha a substituir.
            if (costs.TryGetLine(replacementMedian, out currentRow))
            {
                foreach (var column in currentRow.GetColumns())
                {
                    if (column.Key != replacementMedian)
                    {
                        var current = replacementSolutionBoard[column.Key];
                        if (this.comparer.Compare(column.Value, current) < 0)
                        {
                            replacementSolutionBoard[column.Key] = column.Value;
                            var temp = this.coeffsField.Add(
                                column.Value,
                                this.coeffsField.AdditiveInverse(current));
                            result = this.coeffsField.Add(result, temp);
                        }
                    }
                }
            }

            return(result);
        }
        /// <summary>
        /// Permite calcular o custo associado à escolha de um conjunto de medianas escolhidas.
        /// </summary>
        /// <param name="chosen">O conjunto de medianas escolhidas.</param>
        /// <param name="costs">A matriz dos custos.</param>
        /// <param name="solutionBoard">A linha que mantém os mínimos por mediana.</param>
        /// <returns>O valor do custo associado à escolha.</returns>
        private CoeffType ComputeCost(
            IntegerSequence chosen,
            ILongSparseMathMatrix <CoeffType> costs,
            CoeffType[] solutionBoard,
            BitArray marked)
        {
            var resultCost = this.coeffsField.AdditiveUnity;

            foreach (var item in chosen)
            {
                var currentCostLine = default(ILongSparseMatrixLine <CoeffType>);
                if (costs.TryGetLine(item, out currentCostLine))
                {
                    foreach (var column in currentCostLine.GetColumns())
                    {
                        if (column.Key == item)
                        {
                            if (marked[item])
                            {
                                resultCost = this.coeffsField.Add(
                                    resultCost,
                                    this.coeffsField.AdditiveInverse(solutionBoard[item]));
                            }
                            else
                            {
                                marked[item] = true;
                            }

                            solutionBoard[item] = this.coeffsField.AdditiveUnity;
                        }
                        else
                        {
                            if (marked[column.Key])
                            {
                                var boardCost = solutionBoard[column.Key];
                                if (this.comparer.Compare(column.Value, boardCost) < 0)
                                {
                                    solutionBoard[column.Key] = column.Value;
                                    var difference = this.coeffsField.Add(
                                        column.Value,
                                        this.coeffsField.AdditiveInverse(boardCost));
                                    resultCost = this.coeffsField.Add(
                                        resultCost,
                                        difference);
                                }
                            }
                            else
                            {
                                resultCost = this.coeffsField.Add(
                                    resultCost,
                                    column.Value);
                                solutionBoard[column.Key] = column.Value;
                                marked[column.Key]        = true;
                            }
                        }
                    }
                }
                else
                {
                    marked[item]        = true;
                    solutionBoard[item] = this.coeffsField.AdditiveUnity;
                }
            }

            for (int i = 0; i < marked.Length; ++i)
            {
                if (!marked[i] && !chosen.Contains(i))
                {
                    throw new OdmpProblemException("Not all nodes are covered by some median.");
                }
            }

            return(resultCost);
        }