Example #1
0
        public State(LazyState currentState, int removedRowIndex, int removedColumnIndex
			, LazyState newState)
        {
            CurrentState = currentState;
            RemovedRowIndex = removedRowIndex; RemovedColumnIndex = removedColumnIndex;
            NewState = newState;
        }
Example #2
0
        /// <summary>
        /// Решает задачу LP алгоритмом Seide
        /// Для финального вычисления использует Симплекс - метод
        /// </summary>
        /// <param name="x">Оптимальная точка</param>
        /// <param name="value">Оптимальное значение функционала</param>
        /// <returns></returns>
        public SimplexResult Solv(out decimal[] x, out decimal value)
        {
            // инициализация результата
            x = null;
            value = decimal.MinValue;
            var rowsNumber = A.Length;
            var a = new decimal[rowsNumber][];
            for (var i = 0; i < rowsNumber; ++i)
            {
                a[i] = A[i];
            }
            currentState_.SetState(a, b, c);

            var rnd = new Random();
            states_ = new Stack<State>(); //стек состояний, чтобы избавиться от явной рекурсии
            //есть лишние ограничения, которые нужно исключить
            for (;;)
            {
            #region симуляция рекурсивных вызовов
                var rows = currentState_.Rows;
                var columns = currentState_.Columns;
                if (rows <= columns || rows - columns < 3)
                {
                    // здесь у нас остались только базисные ограничения (или избыток мал (<3))
                    // просто решаем задачу симлекс-методом
                    // добавляем вспомогательные переменные, чтобы превратить неравенства в равенства
                    var eq = BuildSimplexProblem(currentState_);
                    Log_.DebugFormat("Solving subproblem of size {0}x{1}", eq.A.Length, eq.c.Length);
                    var result = eq.Solv();
                    if (result != SimplexResult.Optimal)
                    {
                        while (result == SimplexResult.FunctionalUnbound)
                        {
                            // делать нечего. придется возвращать предыдущие удаленные ограничения поштучно
                            // и решать задачу с ними
                            // если к unbound привело снятие самого первого ограничения,
                            // то нам зверски не повезло
                            // может быть есть какой-то более грамотный путь проверить ограниченность
                            // задачи с данным ограничением, но пока я его не придумал
                            RollbackStates(false);
                            if (states_.Count == 0)
                            {
                                return SimplexResult.FunctionalUnbound;
                            }
                            currentState_ = states_.Pop().CurrentState;
                            rows = currentState_.Rows;
                            columns = currentState_.Columns;
                            eq = BuildSimplexProblem(currentState_);
                            Log_.DebugFormat("Solving subproblem of size {0}x{1}", eq.A.Length, eq.c.Length);
                            result = eq.Solv();
                        }
                        if (result != SimplexResult.Optimal)
                        {
                            // если случился empty, то нужно возвращаться до состояния перед добавлением очередного ограничения
                            if (result != SimplexResult.Infeasible)
                            {
                                return result;
                            }
                            RollbackStates(true);
                            if (states_.Count == 0)
                            {
                                //увы, исходная задача неразрешима
                                return SimplexResult.Infeasible;
                            }
                            rows = currentState_.Rows;
                            columns = currentState_.Columns;
                        }
                    }
                    if (result == SimplexResult.Optimal)
                    {
                        value = eq.Value;
                        // убираем лишние переменные
                        x = new decimal[columns];
                        Array.Copy(eq.Solution, x, columns);
                        // возвращаемся на уровень выше
                    }
                    while (states_.Count > 0)
                    {
            #region возврат вверх по "стеку"
                        var previousState = states_.Pop();
                        var removedRowIndex = previousState.RemovedRowIndex;

                        var removedColumnIndex = previousState.RemovedColumnIndex;
                        if (removedColumnIndex < 0) //ранее удаляли строку
                        {
            #region удаляли строку
                            var removedRow = previousState.NewState.RemovedRow;
                            // проверяем, нарушает ли решение удаленное ограничение
                            var val = 0.0m;
                            if (result == SimplexResult.Optimal)
                            {
                                for (var i = 0; i < columns; ++i)
                                {
                                    val += removedRow[i] * x[i];
                                }
                            }
                            if (result != SimplexResult.Optimal || val - previousState.NewState.RemovedB > SimplexProblemBase.Epsilon)
                            {
                                // ограничение нарушено
                                // удаляем одну из переменных из неравенств, превратив удаленное ранее неравенство в равенство
                                // и добавив ограничение на положительность удаленной переменной
                                // (иначе легко огребаем неограниченность функционала)
                                var maxValue = Math.Abs(removedRow[0]);
                                var columnIndexToRemove = 0;
                                for (var i = 1; i < columns; ++i)
                                {
                                    var testValue = Math.Abs(removedRow[i]);
                                    if (testValue > maxValue)
                                    {
                                        maxValue = testValue;
                                        columnIndexToRemove = i;
                                    }
                                }
                                var tempRow = new decimal[columns];
                                Array.Copy(removedRow, tempRow, columns);
                                removedRow = tempRow;
                                if (maxValue < SimplexProblemBase.Epsilon)
                                {
                                    // тут возможны 3 варианта:
                                    // 1) result == Success. Значит правая часть < 0 и условие невыполнимо.
                                    // система с данным ограничением несовместна. нужно откручивать предыдущее закрепленное ограничение
                                    // 2) result != Success и правая часть < 0. условие невыполнимо
                                    // нужно откручивать предыдущее закрепленное ограничение
                                    // 3) правая часть >= 0. условие выполняется автоматом,
                                    // a) result == FunctionalUnbound нужно откручивать до предыдущего снятого ограничения
                                    // b) result == Infeasible. Ошибкой уже не считается, т.к. мы уже сняли одно ограничение,
                                    // которое возможно ее вызвало. нужно вернуться на предыдущее снятое ограничение
                                    result = result == SimplexResult.Optimal || previousState.NewState.RemovedB < 0
                                            ? SimplexResult.Infeasible : SimplexResult.FunctionalUnbound;
                                    RollbackStates(result == SimplexResult.Infeasible);
                                    if (states_.Count == 0)
                                    {
                                        return result; // не судьба
                                    }
                                    rows = currentState_.Rows;
                                    columns = currentState_.Columns;
                                    continue; // переход на открутку на предыдущий уровень
                                }
                                maxValue = removedRow[columnIndexToRemove]; //теперь уже со знаком
                                var bLeadValue = previousState.NewState.RemovedB / maxValue;
                                if (columns > 1)
                                {
                                    for (var j = 0; j < columns; ++j)
                                    {
                                        if (j == columnIndexToRemove)
                                        {
                                            continue;
                                        }
                                        var jValue = removedRow[j];
                                        if (jValue != 0)
                                        {
                                            jValue /= maxValue;
                                            removedRow[j] = jValue;
                                        }
                                    }
                                    var newA = new decimal[rows + 1][];
                                    var newB = new decimal[rows + 1];
                                    for (var i = 0; i < rows; ++i)
                                    {
                                        var newRow = new decimal[columns - 1];
                                        var row = currentState_.A[i];
                                        var rowLeadValue = row[columnIndexToRemove];
                                        var currentColumn = 0;
                                        for (var j = 0; j < columns; ++j)
                                        {
                                            if (j == columnIndexToRemove)
                                            {
                                                continue;
                                            }
                                            var rcValue = row[j];
                                            if (rowLeadValue != 0)
                                            {
                                                var jValue = removedRow[j];
                                                if (jValue != 0)
                                                {
                                                    rcValue -= jValue * rowLeadValue;
                                                }
                                            }
                                            newRow[currentColumn++] = rcValue;
                                        }
                                        newA[i] = newRow;
                                        var bValue = currentState_.b[i];
                                        if (bLeadValue != 0 && rowLeadValue != 0)
                                        {
                                            bValue -= bLeadValue * rowLeadValue;
                                        }
                                        newB[i] = bValue;
                                    }
                                    var newC = new decimal[columns - 1];
                                    var cLeadValue = currentState_.c[columnIndexToRemove];
                                    var newLastRow = new decimal[columns - 1];
                                    var newColumnIndex = 0;
                                    for (var j = 0; j < columns; ++j)
                                    {
                                        if (j == columnIndexToRemove)
                                        {
                                            continue;
                                        }
                                        var cValue = currentState_.c[j];
                                        if (cLeadValue != 0)
                                        {
                                            var jValue = removedRow[j];
                                            if (jValue != 0)
                                            {
                                                cValue -= jValue * cLeadValue;
                                            }
                                        }
                                        newC[newColumnIndex] = cValue;
                                        // новое ограничение на неотрицательность удаленной переменной
                                        newLastRow[newColumnIndex++] = removedRow[j];
                                    }
                                    newA[rows] = newLastRow;
                                    newB[rows] = bLeadValue;
                                    var newState = new LazyState();
                                    newState.SetState(newA, newB, newC);
                                    states_.Push(new State(previousState.CurrentState.Clone()
                                        , removedRowIndex, columnIndexToRemove
                                        , newState.Clone()));
                                    currentState_ = newState;
                                    break; // переход на внешний уровень for...
                                }
                                // значение считается "в лоб". Это bLeadValue
                                // проверяем на неотрицательность:
                                if (bLeadValue < 0) //система несовместна
                                {
                                    // !!! здесь мы сами зафиксировали ограничение, но не добавляли его в стек
                                    // поэтому оно уже откатилось. нужно только найти предыдущую точку
                                    // где было отброшено ограничение
                                    RollbackStates(false);
                                    if (states_.Count == 0)
                                    {
                                        return SimplexResult.Infeasible; // не судьба
                                    }
                                    result = SimplexResult.FunctionalUnbound;
                                    rows = currentState_.Rows;
                                    columns = currentState_.Columns;
                                    continue; // переход на открутку на предыдущий уровень
                                }
                                result = SimplexResult.Optimal;
                                x = new decimal[1];
                                x[0] = bLeadValue;
                                value = bLeadValue != 0 ? currentState_.c[0] * bLeadValue : 0;
                            }
                            // else //ограничение не нарушено. просто возвращаемся на предыдущий уровень
            #endregion удаляли строку
                        }
                        else
                        {
            #region удаляли переменную
                            // до этого удаляли переменную
                            // вычислим ее, новый вектор x и новое значение функционала
                            var removedRow = previousState.CurrentState.A[removedRowIndex];
                            var newX = new decimal[columns + 1];
                            var xRemoved = previousState.CurrentState.b[removedRowIndex];
                            decimal newVal = 0;
                            var xIndex = 0;
                            for (var i = 0; i < columns + 1; ++i)
                            {
                                if (i == removedColumnIndex)
                                {
                                    continue;
                                }
                                var xVal = x[xIndex++];
                                newX[i] = xVal;
                                xRemoved -= removedRow[i] * xVal;
                                newVal += previousState.CurrentState.c[i] * xVal;
                            }
                            xRemoved /= removedRow[removedColumnIndex];

                            newX[removedColumnIndex] = xRemoved;
                            newVal += previousState.CurrentState.c[removedColumnIndex] * xRemoved;
                            x = newX;
                            value = newVal;
            #endregion удаляли переменную
                        }
                        currentState_ = previousState.CurrentState;
                        rows = currentState_.Rows;
                        columns = currentState_.Columns;

                        // возвращаемся на предыдущий уровень
            #endregion возврат вверх по "стеку"
                    }

                    if (states_.Count == 0)
                    {
                        return result;
                    }
                    // переходим к следующей вложенной итерации
                }
                else
                {
                    // рандомно выбираем ограничение для исключения
                    var rowIndexToDelete = rnd.Next(rows);
                    var oldState = currentState_.Clone();
                    currentState_.UpdateState(rowIndexToDelete);
                    states_.Push(new State(oldState, rowIndexToDelete, -1
                        , currentState_.Clone()));
                }
            #endregion симуляция рекурсивных вызовов
            }
        }
Example #3
0
 /// <summary>Клонирование</summary>
 public LazyState Clone()
 {
     var state = new LazyState
     {
         removedRows_ = removedRows_,
         removedRowsCount_ = removedRowsCount_,
         A_ = A_,
         b_ = b_,
         c_ = c_,
         leftRows_ = leftRows_,
         removedRow_ = removedRow_,
         removedB_ = removedB_
     };
     return state;
 }
Example #4
0
 /// <summary>Откручивает состояния до ближайшего снятого ограничения</summary>
 private void RollbackStates(bool resultEmpty)
 {
     while (states_.Count > 0)
     {
         var st = states_.Peek();
         if (resultEmpty)
         {
             // нужно выбрать в том числе и ближайшее
             // закрепленное ограничение
             if (st.RemovedColumnIndex >= 0)
             {
                 resultEmpty = false; // после того, как его выбрали, ищем следующее удаленное
             }
         }
         else
         {
             // останавливаемся на первом удаленном ограничении
             if (st.RemovedColumnIndex == -1)
             {
                 // здесь нужно восстановить текущий стейт по тому состоянию, к "после которого" возвращаемся
                 // ибо при откручивании вверх по переполнению возможно,
                 // что текущий state будет не соответствовать тому, который был после перехода
                 currentState_ = st.NewState;
                 break;
             }
         }
         states_.Pop();
     }
 }
Example #5
0
 /// <summary>Создает задачу LP для симплекс-метода по заданному состоянию</summary>
 private SimplexProblem BuildSimplexProblem(LazyState state)
 {
     var rows = state.Rows;
     var columns = state.Columns;
     var extendedColumns = columns + rows;
     var A1 = new decimal[rows][];
     for (var i = 0; i < rows; ++i)
     {
         var srcRow = state.A[i];
         var tgtRow = new decimal[extendedColumns];
         Array.Copy(srcRow, tgtRow, columns);
         A1[i] = tgtRow;
         tgtRow[columns + i] = 1;
     }
     var c1 = new decimal[extendedColumns];
     Array.Copy(state.c, c1, columns);
     return new SimplexProblem(A1, state.b, c1);
 }