Пример #1
0
 public static void AppendLine(this TextBox source, string value)
 {
     if (source.InvokeRequired)
     {
         AppendLineCallback a = new AppendLineCallback(AppendLine);
         source.Parent.Invoke(a, new object[] { source, value });
     }
     else
     {
         if (source.Text.Length == 0)
         {
             source.Text = value;
         }
         else
         {
             source.AppendText("\r\n" + value);
         }
     }
 }
Пример #2
0
        public bool Execute(ILinearMiniMax <T> minimax, ref IEnumerable <Vector <T> > optimalVectors,
                            ref IEnumerable <T> optimalValues, ITrace trace)
        {
            Debug.Assert(minimax.A.Rows == minimax.R.Count());
            Debug.Assert(minimax.A.Rows == minimax.B.Count());
            Debug.Assert(minimax.A.Columns == minimax.C.Count());

            AppendLineCallback appendLineCallback = trace != null ? trace.AppendLineCallback : null;
            ProgressCallback   progressCallback   = trace != null ? trace.ProgressCallback : null;
            CompliteCallback   compliteCallback   = trace != null ? trace.CompliteCallback : null;

            // Количество переменных
            int n = minimax.C.Count;

            if (progressCallback != null)
            {
                progressCallback(0, minimax.C.Count);
            }
            // Исходным выбранным частичным планом считается тот, базис которого пуст
            var list = new StackListQueue <BooleanTreePlan>(new BooleanTreePlan(
                                                                new BooleanVector(),
                                                                minimax));

            for (int k = 0;; k++)
            {
                if (appendLineCallback != null)
                {
                    appendLineCallback(string.Format("Количество подмножеств = {0}", list.Count));
                }
                if (progressCallback != null)
                {
                    progressCallback(k, minimax.C.Count);
                }
                // Удаляем подмножества с заведомо недопустимыми значениями
                if (appendLineCallback != null)
                {
                    appendLineCallback("Удаляем подмножества с заведомо недопустимыми значениями");
                }
                Debug.WriteLine("Удаляем подмножества с заведомо недопустимыми значениями");
                list.RemoveAll(item => item.ArgBound.Any(x => x < 0));
                if (appendLineCallback != null)
                {
                    appendLineCallback(string.Format("Количество подмножеств = {0}", list.Count));
                }
                Debug.WriteLine("Количество подмножеств = {0}", list.Count);

                if (list.Count == 0)
                {
                    return(false);
                }

                for (;;)
                {
                    double maxMin = list.Max(item => item.FuncMin);
                    double minMax = list.Min(item => item.FuncMax);

                    if (appendLineCallback != null)
                    {
                        appendLineCallback(string.Format("maxMin = {0} minMax = {1}", maxMin, minMax));
                    }
                    Debug.WriteLine("maxMin = {0} minMax = {1}", maxMin, minMax);

                    if (maxMin <= minMax)
                    {
                        break;
                    }

                    // Удаляем варианты подмножеств, для которых существует более лучшая оценка целевой функции
                    if (appendLineCallback != null)
                    {
                        appendLineCallback(
                            "Удаляем варианты подмножеств, для которых существует более лучшая оценка целевой функции");
                    }

                    switch (minimax.Target)
                    {
                    case Target.Maximum:
                        list.RemoveAll(item => item.FuncMax < maxMin);
                        break;

                    case Target.Minimum:
                        list.RemoveAll(item => item.FuncMin > minMax);
                        break;
                    }

                    if (appendLineCallback != null)
                    {
                        appendLineCallback(string.Format("Количество подмножеств = {0}", list.Count));
                    }
                    Debug.WriteLine("Количество подмножеств = {0}", list.Count);
                }

                if (k == n)
                {
                    break;
                }

                // Шаг деления множеств пополам (дописыванием нуля и единицы)
                if (appendLineCallback != null)
                {
                    appendLineCallback("Шаг деления множеств пополам (дописыванием нуля и единицы)");
                }
                var next = new StackListQueue <BooleanTreePlan>
                {
                    list.Select(item => new BooleanTreePlan(new BooleanVector(item.Vector)
                    {
                        false
                    }, minimax)),
                    list.Select(item => new BooleanTreePlan(new BooleanVector(item.Vector)
                    {
                        true
                    }, minimax))
                };
                list = next;
            }

            // Завершаем алгоритм и возвращаем найденное решение
            optimalVectors =
                new StackListQueue <Vector <T> >(
                    list.Select(item => new Vector <T>(item.Vector.Select(b => b ? (T)(dynamic)1 : default(T)))));
            optimalValues =
                new StackListQueue <T>(list.Select(item => (T)(dynamic)(item.FuncMin)));
            Debug.Assert(list.All(item => item.FuncMin <= item.FuncMax));
            if (compliteCallback != null)
            {
                compliteCallback();
            }
            return(true);
        }
Пример #3
0
        /// <summary>
        ///     Решение задачи линейного программирования
        /// </summary>
        /// <param name="minimax"></param>
        /// <param name="optimalVectors"></param>
        /// <param name="optimalValues"></param>
        /// <param name="trace"></param>
        /// <returns></returns>
        public bool Execute(ILinearMiniMax <T> minimax, ref IEnumerable <Vector <T> > optimalVectors,
                            ref IEnumerable <T> optimalValues, ITrace trace)
        {
            // Реализуем алгоритм только для случая наращивания базиса по одной переменной
            Debug.Assert(H == 1);

            Debug.Assert(minimax.A.Rows == minimax.R.Count());
            Debug.Assert(minimax.A.Rows == minimax.B.Count());
            Debug.Assert(minimax.A.Columns == minimax.C.Count());

            AppendLineCallback appendLineCallback = trace != null ? trace.AppendLineCallback : null;
            ProgressCallback   progressCallback   = trace != null ? trace.ProgressCallback : null;
            CompliteCallback   compliteCallback   = trace != null ? trace.CompliteCallback : null;

            if (progressCallback != null)
            {
                progressCallback(0, minimax.C.Count);
            }

            // Количество переменных
            int n = minimax.C.Count;

            // Величине рекорда присваивается заведомо плохое решение
            double          r       = minimax.Target == Target.Maximum ? Double.MinValue : Double.MaxValue;
            BooleanEnumPlan optimal = null;

            // Исходным выбранным частичным планом считается тот, базис которого пуст
            for (
                var stack =
                    new StackListQueue <BooleanEnumPlan>(
                        new BooleanEnumPlan(
                            new BooleanVector(),
                            new Vector <int>(),
                            minimax));
                stack.Any();
                )
            {
                BooleanEnumPlan element = stack.Pop();
                Debug.WriteLine("element = {0}", element);
                // Генерируем все возможные соседние планы, связанные с введением в базис H переменных
                // и вычисляем их оценки
                if (appendLineCallback != null)
                {
                    appendLineCallback(
                        string.Format(
                            "Генерируем все возможные соседние планы, связанные с введением в базис {0} переменных",
                            H));
                }
                Debug.WriteLine(
                    "Генерируем все возможные соседние планы, связанные с введением в базис {0} переменных", H);
                var list = new StackListQueue <BooleanEnumPlan>(new[] { true, false }.Select(value =>
                                                                                             new BooleanEnumPlan(new BooleanVector(element.Vector)
                {
                    value
                },
                                                                                                                 new Vector <int>(element.Indeces)
                {
                    Enumerable.Range(0, n).Except(element.Indeces).First()
                },
                                                                                                                 minimax))
                                                                .Where(item => item.ArgBound.All(x => x >= 0.0)));
                stack.Push(list);
                if (appendLineCallback != null)
                {
                    appendLineCallback(string.Format("Количество подмножеств = {0}", stack.Count));
                }
                Debug.WriteLine("Количество подмножеств = {0}", stack.Count);

                for (; stack.Any();)
                {
                    // На множестве планов выбрать планы с наибольшим числом введёных переменных
                    // На выбраном множестве выбрать с наилучшей оценкой
                    if (appendLineCallback != null)
                    {
                        appendLineCallback(
                            "На множестве планов выбрать планы с наибольшим числом введёных переменных");
                    }
                    if (appendLineCallback != null)
                    {
                        appendLineCallback("На выбраном множестве выбрать с наилучшей оценкой");
                    }
                    Debug.WriteLine("На множестве планов выбрать планы с наибольшим числом введёных переменных");
                    Debug.WriteLine("На выбраном множестве выбрать с наилучшей оценкой");

                    int max1 = stack.Max(item => item.Indeces.Count);
                    list = new StackListQueue <BooleanEnumPlan>(stack.Where(item => item.Indeces.Count == max1));
                    double maxMax = list.Max(item => item.FuncMax);
                    double minMin = list.Min(item => item.FuncMin);
                    element = minimax.Target == Target.Maximum
                        ? list.First(item => item.FuncMax == maxMax)
                        : list.First(item => item.FuncMin == minMin);
                    stack.Remove(element);
                    Debug.WriteLine("element = {0}", element);

                    if (minimax.Target == Target.Maximum && r <= maxMax ||
                        minimax.Target == Target.Minimum && r >= minMin)
                    {
                        // Если оценка элемента лучше рекорда
                        if (appendLineCallback != null)
                        {
                            appendLineCallback("Оценка элемента лучше рекорда");
                        }
                        if (element.Indeces.Count == n)
                        {
                            // Если в базис введены все элементы
                            if (appendLineCallback != null)
                            {
                                appendLineCallback("В базис введены все элементы");
                            }
                            // Запоминаем в рекорд оценку (т.е. значение для этого вектора)
                            // и запоминаем этот план
                            if (appendLineCallback != null)
                            {
                                appendLineCallback("Запоминаем в рекорд оценку");
                            }
                            r       = minimax.Target == Target.Maximum ? element.FuncMax : element.FuncMin;
                            optimal = new BooleanEnumPlan(element);
                        }
                        else
                        {
                            // Иначе переходим к генерации соседних планов
                            stack.Push(element);
                            break;
                        }
                    }
                    // Выводим из базиса H последних переменных, отбросив остальные планы с таким же числом переменных
                    if (appendLineCallback != null)
                    {
                        appendLineCallback(
                            string.Format(
                                "Выводим из базиса {0} последних переменных, отбросив остальные планы с таким же числом переменных",
                                H));
                    }
                    element = new BooleanEnumPlan(
                        new BooleanVector(element.Vector.GetRange(0, element.Vector.Count - 1)),
                        new Vector <int>(element.Indeces.GetRange(0, element.Indeces.Count - 1)),
                        minimax);
                    stack.RemoveAll(item => item.Indeces.Count == element.Indeces.Count);

                    Debug.WriteLine("element = {0}", element);

                    if (appendLineCallback != null)
                    {
                        appendLineCallback(string.Format("Количество подмножеств = {0}", stack.Count));
                    }
                    Debug.WriteLine("Количество подмножеств = {0}", stack.Count);

                    if (element.Indeces.Count == 0)
                    {
                        break;
                    }
                }
            }

            // Завершаем алгоритм и возвращаем найденное решение
            if (optimal == null)
            {
                return(false);
            }

            optimalVectors =
                new StackListQueue <Vector <T> >(
                    new Vector <T>(optimal.Indeces.Select(index => optimal.Vector[index] ? (T)(dynamic)1 : default(T))));
            optimalValues = new StackListQueue <T>((T)(dynamic)r);
            if (compliteCallback != null)
            {
                compliteCallback();
            }
            return(true);
        }
Пример #4
0
        public bool Execute(ILinearMiniMax <T> minimax, ref IEnumerable <Vector <T> > optimalVectors,
                            ref IEnumerable <T> optimalValues, ITrace trace)
        {
            // Реализуем алгоритм только для случая наращивания базиса по одной переменной
            Debug.Assert(H == 1);

            Debug.Assert(minimax.A.Rows == minimax.R.Count());
            Debug.Assert(minimax.A.Rows == minimax.B.Count());
            Debug.Assert(minimax.A.Columns == minimax.C.Count());

            // Количество переменных
            int n = minimax.C.Count;

            AppendLineCallback appendLineCallback = trace != null ? trace.AppendLineCallback : null;
            ProgressCallback   progressCallback   = trace != null ? trace.ProgressCallback : null;
            CompliteCallback   compliteCallback   = trace != null ? trace.CompliteCallback : null;

            if (progressCallback != null)
            {
                progressCallback(0, minimax.C.Count);
            }
            // Исходным выбранным частичным планом считается тот, базис которого пуст
            var list =
                new StackListQueue <BooleanMultiPlan>(new[] { true, false }
                                                      .SelectMany(value => Enumerable.Range(0, n).Select(
                                                                      index => new BooleanMultiPlan(new BooleanVector(value),
                                                                                                    new Vector <int>(index), minimax))
                                                                  .Where(item => item.ArgBound.All(x => x >= 0.0))));

            for (int k = 1;; k++)
            {
                if (appendLineCallback != null)
                {
                    appendLineCallback(string.Format("Количество подмножеств = {0}", list.Count));
                }
                if (progressCallback != null)
                {
                    progressCallback(k, minimax.C.Count);
                }
                // Удаляем подмножества с заведомо недопустимыми значениями
                if (appendLineCallback != null)
                {
                    appendLineCallback("Удаляем подмножества с заведомо недопустимыми значениями");
                }
                Debug.WriteLine("Удаляем подмножества с заведомо недопустимыми значениями");
                list.RemoveAll(item => item.ArgBound.Any(x => x < 0));
                if (appendLineCallback != null)
                {
                    appendLineCallback(string.Format("Количество подмножеств = {0}", list.Count));
                }
                Debug.WriteLine("Количество подмножеств = {0}", list.Count);

                if (list.Count == 0)
                {
                    return(false);
                }

                for (;;)
                {
                    double maxMin = list.Max(item => item.FuncMin);
                    double minMax = list.Min(item => item.FuncMax);

                    if (appendLineCallback != null)
                    {
                        appendLineCallback(string.Format("maxMin = {0} minMax = {1}", maxMin, minMax));
                    }
                    Debug.WriteLine("maxMin = {0} minMax = {1}", maxMin, minMax);

                    if (maxMin <= minMax)
                    {
                        break;
                    }

                    // Удаляем варианты подмножеств, для которых существует более лучшая оценка целевой функции
                    if (appendLineCallback != null)
                    {
                        appendLineCallback(
                            "Удаляем варианты подмножеств, для которых существует более лучшая оценка целевой функции");
                    }

                    switch (minimax.Target)
                    {
                    case Target.Maximum:
                        list.RemoveAll(item => item.FuncMax < maxMin);
                        break;

                    case Target.Minimum:
                        list.RemoveAll(item => item.FuncMin > minMax);
                        break;
                    }

                    if (appendLineCallback != null)
                    {
                        appendLineCallback(string.Format("Количество подмножеств = {0}", list.Count));
                    }
                    Debug.WriteLine("Количество подмножеств = {0}", list.Count);
                }

                if (k == n)
                {
                    break;
                }

                // Генерируем все возможные соседние планы, связанные с введением в базис H переменных
                // и вычисляем их оценки
                if (appendLineCallback != null)
                {
                    appendLineCallback(
                        string.Format(
                            "Генерируем все возможные соседние планы, связанные с введением в базис {0} переменных",
                            H));
                }
                Debug.WriteLine(
                    "Генерируем все возможные соседние планы, связанные с введением в базис {0} переменных", H);
                var next = new SortedStackListQueue <BooleanMultiPlan>(new[] { true, false }
                                                                       .SelectMany(value => list.Select(
                                                                                       element => new BooleanMultiPlan(new BooleanVector(element.Vector)
                {
                    value
                },
                                                                                                                       new Vector <int>(element.Indeces)
                {
                    (element.Indeces.Last() + 1) % n
                }, minimax))
                                                                                   .Where(item => item.ArgBound.All(x => x >= 0.0))))
                {
                    Comparer = new BooleanMultiPlanComparer()
                };
                if (appendLineCallback != null)
                {
                    appendLineCallback("Удаляем дупликаты");
                }
                Debug.WriteLine("Удаляем дупликаты");
                list = new StackListQueue <BooleanMultiPlan>(next.Distinct());
                Debug.WriteLine("Количество подмножеств = {0}", list.Count);
            }

            // Завершаем алгоритм и возвращаем найденное решение
            optimalVectors =
                new StackListQueue <Vector <T> >(
                    list.Select(item =>
                                new Vector <T>(
                                    Enumerable.Range(0, n)
                                    .Select(index =>
                                            item.Vector[item.Indeces.IndexOf(index)] ? (T)(dynamic)1 : default(T)))));
            optimalValues =
                new StackListQueue <T>(list.Select(item => (T)(dynamic)(item.FuncMin)));
            Debug.Assert(list.All(item => item.FuncMin <= item.FuncMax));
            if (compliteCallback != null)
            {
                compliteCallback();
            }
            return(true);
        }
Пример #5
0
        /// <param name="a">Параметр алгоритма для вычисления весовых коэффициентов</param>
        /// <param name="relax">
        ///     При использовании метода релаксации задействовано в два раза меньше памяти и вычисления производятся
        ///     на-месте. Для устанения коллизий с совместным доступом производится раскраска точек красное-чёрное для обработки
        ///     их по-очереди
        /// </param>
        /// <param name="epsilon">Точность вычислений</param>
        /// <param name="gridSize"></param>
        /// <param name="blockSize"></param>
        /// <param name="trace"></param>
        public static IEnumerable <double> ExecuteLaplaceSolver(double epsilon, double a, bool relax, int gridSize = 0,
                                                                int blockSize = 0,
                                                                ITrace trace  = null)
        {
            AppendLineCallback appendLineCallback = trace != null ? trace.AppendLineCallback : null;
            ProgressCallback   progressCallback   = trace != null ? trace.ProgressCallback : null;
            CompliteCallback   compliteCallback   = trace != null ? trace.CompliteCallback : null;

            if (gridSize > 0)
            {
                _gridSize3 = gridSize;
                _gridSize2 = blockSize;
                _gridSize1 = blockSize;
            }
            if (blockSize > 0)
            {
                _blockSize3 = blockSize;
                _blockSize2 = blockSize;
                _blockSize1 = blockSize;
            }

            Debug.Assert(_sizes.Length == _lengths.Length);
            Debug.Assert(_sizes.Aggregate(Int32.Mul) > 0);
            Debug.Assert(_lengths.Aggregate(Double.Mul) > 0.0);

            if (appendLineCallback != null)
            {
                appendLineCallback("Размер массива:");
            }
            for (int i = 0; i < _sizes.Length; i++)
            {
                if (appendLineCallback != null)
                {
                    appendLineCallback(string.Format("Размер массива по оси № {0}:\t{1}", i, _sizes[i]));
                }
            }

            // Степень дифференциального оператора
            // Реализовано только для оператора Лапласа
            // Для больших степеней надо использовать соответствующие полиномы большей степени
            // Для дифференциального оператора степени 2 (оператора Лапласа) полином имеет степень 1
            // Для дифференциального оператора степени rank полином имеет степень rank-1
            const int rank = 2;

            _extV    = new int[_sizes.Length + 1];
            _intV    = new int[_sizes.Length + 1];
            _intV[0] = _extV[0] = 1;

            for (int i = 1; i <= _sizes.Length; i++)
            {
                _extV[i] = _extV[i - 1] * _sizes[i - 1];
                _intV[i] = _intV[i - 1] * (_sizes[i - 1] - rank);
            }

            // Расчёт коэффициентов слагаемых
            if (appendLineCallback != null)
            {
                appendLineCallback("Расчёт коэффициентов слагаемых");
            }
            _w = new double[_sizes.Length + 1];
            double sum2 = 0;

            for (int i = 0; i < _sizes.Length; i++)
            {
                sum2 += (_sizes[i] - 1) * (_sizes[i] - 1) / (_lengths[i] * _lengths[i]);
            }
            for (int i = 0; i < _sizes.Length; i++)
            {
                _w[i] = (_sizes[i] - 1) * (_sizes[i] - 1) / (_lengths[i] * _lengths[i]) / sum2 / (1.0 + a);
            }
            _w[_sizes.Length] = (a - 1.0) / (1.0 + a);

            if (appendLineCallback != null)
            {
                appendLineCallback("Коэффициенты:");
            }
            for (int i = 0; i < _sizes.Length; i++)
            {
                if (appendLineCallback != null)
                {
                    appendLineCallback(string.Format("Коэффициенты по оси № {0} (у двух точек):\t{1}", i, _w[i]));
                }
            }
            if (appendLineCallback != null)
            {
                appendLineCallback(string.Format("Коэффициент у средней точки:\t{0}", _w[_sizes.Length]));
            }

            if (appendLineCallback != null && relax)
            {
                appendLineCallback("Используется релаксация");
            }

            CudafyModule km = CudafyTranslator.Cudafy();

            GPGPU gpu = CudafyHost.GetDevice();

            gpu.LoadModule(km);

            if (appendLineCallback != null)
            {
                appendLineCallback("Выделяем видеоресурсы");
            }

            //  При использовании метода релаксации задействовано в два раза меньше памяти и вычисления производятся
            //  на-месте. Для устанения коллизий с совместным доступом производится раскраска точек красное-чёрное для обработки
            //  их по-очереди
            double[][] devA = relax
                ? new[] { gpu.Allocate <double>(_a.Length) }
                : new[] { gpu.Allocate <double>(_a.Length), gpu.Allocate <double>(_a.Length) };

            double[][] devB =
            {
                gpu.Allocate <double>(_gridSize3 * _blockSize3),
                gpu.Allocate(_b)
            };
            double[][] devC =
            {
                gpu.Allocate <double>(_gridSize3 * _blockSize3),
                gpu.Allocate(_c)
            };
            int[]   devSizes = gpu.Allocate(_sizes);
            int[][] devV     =
            {
                gpu.Allocate(_extV),
                gpu.Allocate(_intV)
            };
            double[] devW = gpu.Allocate(_w);

            if (appendLineCallback != null)
            {
                appendLineCallback("Копируем данные в видеопамять");
            }
            gpu.CopyToDevice(_a, devA[0]);
            gpu.CopyToDevice(_sizes, devSizes);
            gpu.CopyToDevice(_extV, devV[0]);
            gpu.CopyToDevice(_intV, devV[1]);
            gpu.CopyToDevice(_w, devW);

            if (!relax)
            {
                if (appendLineCallback != null)
                {
                    appendLineCallback("Дублируем массив в видеопамяти");
                }
                gpu.Launch(_gridSize3, _blockSize3, "Copy", devA[0], devA[1]);
            }
            var queue = new StackListQueue <double>();

            for (int step = 0;; step++)
            {
                //if (AppendLineCallback != null) AppendLineCallback(string.Format("Шаг итерации № {0}", step));

                // Вычисляем среднее взвешенное соседних точек
                //if (AppendLineCallback != null) AppendLineCallback("Вычисляем среднее взвешенное соседних точек");
                if (!relax)
                {
                    gpu.Launch(_gridSize3, _blockSize3, "LaplaceSolver", devA[step & 1], devA[1 - (step & 1)],
                               devSizes,
                               devV[0], devV[1],
                               devW,
                               devB[0], devC[0]);
                }
                else
                {
                    gpu.Launch(_gridSize3, _blockSize3, "Clear", devB[0]);
                    gpu.Launch(_gridSize3, _blockSize3, "Clear", devC[0]);

                    for (int p = 0; p < 2; p++)
                    {
                        gpu.Launch(_gridSize3, _blockSize3, "LaplaceSolverWithRelax", devA[0],
                                   devSizes,
                                   devV[0], devV[1],
                                   devW,
                                   devB[0], devC[0],
                                   p);
                    }
                }

                // Суммируем амплитуды изменений, посчитанные в каждом процессе
                gpu.Launch(_gridSize1, _blockSize1, "Sum", devB[0], devB[1]);
                gpu.Launch(_gridSize1, _blockSize1, "Sum", devC[0], devC[1]);

                gpu.CopyFromDevice(devB[1], _b);
                gpu.CopyFromDevice(devC[1], _c);
                double deltaSum  = _b[0];
                double squareSum = _c[0];

                //if (AppendLineCallback != null)
                //    AppendLineCallback(string.Format("Амплитуда изменений = {0}/{1}", deltaSum, squareSum));

                queue.Enqueue(deltaSum / squareSum);

                if (deltaSum > epsilon * squareSum)
                {
                    continue;
                }

                if (appendLineCallback != null)
                {
                    appendLineCallback(string.Format("Потребовалось {0} итераций", step + 1));
                }

                // Если изменения меньше заданной величины, то возвращаем вычисленные значения
                if (appendLineCallback != null)
                {
                    appendLineCallback("Копируем массив из видеопамяти в массив на компьютере");
                }
                if (!relax)
                {
                    gpu.CopyFromDevice(devA[1 - (step & 1)], _a);
                }
                else
                {
                    gpu.CopyFromDevice(devA[0], _a);
                }

                break;
            }
            // free the memory allocated on the GPU
            if (appendLineCallback != null)
            {
                appendLineCallback("Освобождаем видеоресурсы");
            }
            gpu.FreeAll();
            return(queue);
        }
Пример #6
0
            /// <summary>
            ///     Применение алгоритма симлекс-метода для решения задачи линейного программирования
            /// </summary>
            /// <param name="tr">
            ///     Признак проверки данных в индексной строке или индексном столбце.
            ///     Transform.ByColumns:
            ///     все элементы индексной строки отрицательные или ноль - при минимизации
            ///     все элементы индексной строки положительные или ноль - при максимизации
            ///     Transform.ByRows:
            ///     все элементы индексного столбца положительные или ноль
            ///     Transform.ByBoth:
            ///     все элементы индексной строки отрицательные или ноль - при минимизации
            ///     все элементы индексной строки положительные или ноль - при максимизации
            ///     все элементы индексного столбца положительные или ноль
            /// </param>
            /// <param name="target">
            ///     Признак решаемой задачи - поиск максимума или поиск минимума линейного функционала
            /// </param>
            public void SimplexMethod(Transform tr, Target target)
            {
                AppendLineCallback appendLineCallback = AppendLineCallback;
                ProgressCallback   progressCallback   = ProgressCallback;
                CompliteCallback   compliteCallback   = CompliteCallback;

                var rowsIndex    = new StackListQueue <int>(RowsIndex);
                var columnsIndex = new StackListQueue <int>(ColumnsIndex);

                // Подготовка данных для шага симплекс метода
                for (;;)
                {
                    // Выбор ведущих строки и столбца.
                    if (appendLineCallback != null)
                    {
                        appendLineCallback("Выбор ведущих строки и столбца");
                    }
                    Debug.WriteLine("Выбор ведущих строки и столбца");

                    // В качестве ведущего выберем столбец, соответствующий переменной, так как наибольший коэффициент по модулю.
                    // Из отрицательных коэффициентов индексной строки выбирается наибольший по абсолютной величине.
                    // Затем элементы столбца свободных членов симплексной таблицы делит на элементы того же знака ведущего столбца.

                    // Если tr != Transform.ByBoth то ищется допустимое решение
                    // Если tr == Transform.ByBoth то ищется допустимое решение и оптимальное решение

                    var enumerable = from r in Enumerable.Range(1, rowsIndex.Count)
                                     from c in columnsIndex
                                     where Math.Abs(this[r][c]) > Tolerance
                                     where this[r][0] < -Tolerance && this[r][0] * this[r][c] > Tolerance || tr != Transform.ByRows
                                     where
                                     this[0][c] * (double)target > Tolerance && this[0][c] * this[r][c] * (double)target > Tolerance ||
                                     tr != Transform.ByColumns
                                     select
                                     new
                    {
                        row   = r,
                        col   = c,
                        value =
                            Math.Abs((tr == Transform.ByRows)
                                        ? this[0][c] / this[r][c]
                                        : this[r][0] / this[r][c])
                    };

                    Debug.WriteLine(enumerable.Count());

                    if (!enumerable.Any())
                    {
                        break;
                    }

                    // В задаче минимизации вводимой переменной должно соответствовать наименьшее из указанных
                    // отношений, а в задаче максимизации – отношение, наименьшее по абсолютной величине

                    double min   = enumerable.Min(p => p.value);
                    var    first = enumerable.First(p => Math.Abs(p.value - min) <= Tolerance);
                    int    col   = first.col;
                    int    row   = first.row;

                    Debug.WriteLine(string.Join(",",
                                                rowsIndex.Select(x => x.ToString(CultureInfo.InvariantCulture))));
                    Debug.WriteLine(string.Join(",",
                                                columnsIndex.Select(x => x.ToString(CultureInfo.InvariantCulture))));
                    if (appendLineCallback != null)
                    {
                        appendLineCallback(string.Format("строка = {0} столбец = {1}", row, col));
                    }
                    Debug.WriteLine("строка = {0} столбец = {1}", row, col);

                    //Пересчет симплекс-таблицы. Выполняем преобразования симплексной таблицы методом Жордано-Гаусса
                    if (appendLineCallback != null)
                    {
                        appendLineCallback(
                            "Пересчет симплекс-таблицы. Выполняем преобразования симплексной таблицы методом Жордано-Гаусса");
                    }
                    Debug.WriteLine(
                        "Пересчет симплекс-таблицы. Выполняем преобразования симплексной таблицы методом Жордано-Гаусса");

                    GaussJordanStep(Matrix <double> .Transform.TransformByRows, row, col);

                    columnsIndex[columnsIndex.IndexOf(col)] = rowsIndex[row - 1];
                    rowsIndex[row - 1] = col;
                    RowsIndex          = rowsIndex;
                    ColumnsIndex       = columnsIndex;

                    if (appendLineCallback != null)
                    {
                        appendLineCallback("Текущий опорный план:");
                    }
                    if (appendLineCallback != null)
                    {
                        appendLineCallback(ToString());
                    }
                    Debug.WriteLine("Текущий опорный план:");
                    Debug.WriteLine(ToString());
                }
            }
Пример #7
0
        public bool Execute(ILinearMiniMax <T> minimax, ref IEnumerable <Vector <T> > optimalVectors,
                            ref IEnumerable <T> optimalValues, ITrace trace)
        {
            Debug.Assert(minimax.A.Rows == minimax.R.Count());
            Debug.Assert(minimax.A.Rows == minimax.B.Count());
            Debug.Assert(minimax.A.Columns == minimax.C.Count());

            int n = Math.Min(minimax.A.Columns, minimax.C.Count()); // Количество переменных в уравнении

            AppendLineCallback appendLineCallback = trace != null ? trace.AppendLineCallback : null;
            ProgressCallback   progressCallback   = trace != null ? trace.ProgressCallback : null;
            CompliteCallback   compliteCallback   = trace != null ? trace.CompliteCallback : null;

            // 1. Составление псевдоплана.
            // Систему ограничений исходной задачи приводят к системе неравенств смысла >=.
            if (appendLineCallback != null)
            {
                appendLineCallback("Составление псевдоплана");
            }
            Debug.WriteLine("Составление псевдоплана");

            var simplexMatrix = new SimplexMatrix
            {
                AppendLineCallback = appendLineCallback,
                CompliteCallback   = compliteCallback,
                ProgressCallback   = progressCallback,
            };

            //     1.	Решаем задачу симплексным методом без учета условия целочисленности. Если все компоненты оптимального плана
            //     целые, то он является оптимальным и для задачи целочисленного программирования. Если обнаруживается неразрешимость
            //     задачи, то и неразрешима задача целочисленного программирования.

            if (appendLineCallback != null)
            {
                appendLineCallback("Решается задача линейного программирования без учета целочисленности.");
            }
            Debug.WriteLine("Решается задача линейного программирования без учета целочисленности.");

            bool b = simplexMatrix.DoubleSimplexMethod(minimax);

            if (!b)
            {
                return(false);
            }

            if (appendLineCallback != null)
            {
                appendLineCallback("Текущий опорный план:");
            }
            if (appendLineCallback != null)
            {
                appendLineCallback(simplexMatrix.ToString());
            }
            Debug.WriteLine("Текущий опорный план:");
            Debug.WriteLine(simplexMatrix.ToString());


            //     2.	Если среди компонент оптимального решения есть нецелые, то к ограничениям задачи добавляем новое ограничение,
            //     обладающее следующими свойствами:
            //     - оно должно быть линейным;
            //     - должно отсекать найденный оптимальный нецелочисленный план;
            //     - не должно отсекать ни одного целочисленного плана.
            //     Для построения ограничения выбираем компоненту оптимального плана с наибольшей дробной частью и по соответствующей
            //     этой компоненте k-й строке симплексной таблицы записываем ограничение Гомори.

            for (IndexColumnVector indexColumnVector = simplexMatrix.GetIndexColumnVector();
                 indexColumnVector.HasFraction(n, simplexMatrix.RowsIndex, simplexMatrix.ColumnsIndex);
                 indexColumnVector = simplexMatrix.GetIndexColumnVector())
            {
                // В полученном оптимальном плане переменная имеет дробную часть числа.
                if (appendLineCallback != null)
                {
                    appendLineCallback("В полученном оптимальном плане переменная имеет дробную часть числа.");
                }
                Debug.WriteLine("В полученном оптимальном плане переменная имеет дробную часть числа.");

                if (appendLineCallback != null)
                {
                    appendLineCallback(
                        "Составляется дополнительное ограничение для переменной, которая в оптимальном плане имеет максимальное дробное значение, хотя должна быть целой.");
                }
                Debug.WriteLine(
                    "Составляется дополнительное ограничение для переменной, которая в оптимальном плане имеет максимальное дробное значение, хотя должна быть целой.");

                // Метод Гомори.
                // Составляется дополнительное ограничение для переменной,
                // которая в оптимальном плане имеет максимальное дробное значение, хотя должна быть целой.

                // Находим переменную с максимальной дробной частью
                if (appendLineCallback != null)
                {
                    appendLineCallback("Находим переменную с максимальной дробной частью");
                }
                Debug.WriteLine("Находим переменную с максимальной дробной частью");

                var rowsIndex1 = new StackListQueue <int>(simplexMatrix.RowsIndex);

                var withFraction =
                    new StackListQueue <int>(
                        Enumerable.Range(1, indexColumnVector.Count - 1)
                        .Where(
                            i =>
                            rowsIndex1[i - 1] <= n &&
                            Math.Abs(indexColumnVector[i] - Math.Round(indexColumnVector[i])) > Tolerance));

                var fractions =
                    new StackListQueue <double>(
                        withFraction.Select(i => Math.Abs(indexColumnVector[i] - Math.Floor(indexColumnVector[i]))));
                int k = withFraction[fractions.IndexOf(fractions.Max())]; // 1-based variable index

                Debug.WriteLine(Math.Abs(indexColumnVector[k] - Math.Floor(indexColumnVector[k])));

                // Дополнительное ограничение составляем по строке k.
                if (appendLineCallback != null)
                {
                    appendLineCallback(string.Format("Дополнительное ограничение составляем по строке {0}", k));
                }
                Debug.WriteLine("Дополнительное ограничение составляем по строке {0}", k);

                rowsIndex1.Add(simplexMatrix.Columns);
                var vector = new Vector <double>(simplexMatrix[k].Select(a => Math.Floor(a) - a))
                {
                    1
                };
                foreach (var r in simplexMatrix)
                {
                    r.Add(0);
                }
                simplexMatrix.Add(vector);
                simplexMatrix.RowsIndex = rowsIndex1;

                if (appendLineCallback != null)
                {
                    appendLineCallback("Текущий опорный план:");
                }
                if (appendLineCallback != null)
                {
                    appendLineCallback(simplexMatrix.ToString());
                }
                Debug.WriteLine("Текущий опорный план:");
                Debug.WriteLine(simplexMatrix.ToString());

                //     Transform.ByColumns:
                //     все элементы индексной строки отрицательные или ноль - при минимизации
                //     все элементы индексной строки положительные или ноль - при максимизации
                //     Transform.ByRows:
                //     все элементы индексного столбца положительные или ноль

                // Ищем допустимое и оптимальное решение
                simplexMatrix.SimplexMethod(SimplexMatrix.Transform.ByRows, minimax.Target);

                if (appendLineCallback != null)
                {
                    appendLineCallback("Текущий опорный план:");
                }
                if (appendLineCallback != null)
                {
                    appendLineCallback(simplexMatrix.ToString());
                }
                Debug.WriteLine("Текущий опорный план:");
                Debug.WriteLine(simplexMatrix.ToString());
            }
            double value = simplexMatrix[0][0];

            // Завершаем алгоритм и возвращаем найденное решение
            if (appendLineCallback != null)
            {
                appendLineCallback("Завершаем алгоритм и возвращаем найденное решение");
            }
            Debug.WriteLine("Завершаем алгоритм и возвращаем найденное решение");
            var rowsIndex = new StackListQueue <int>(simplexMatrix.RowsIndex);

            optimalVectors =
                new StackListQueue <Vector <T> >(
                    new Vector <T>(
                        Enumerable.Range(1, n)
                        .Select(
                            i =>
                            (rowsIndex.Contains(i)
                                        ? (T)(dynamic)simplexMatrix[1 + rowsIndex.IndexOf(i)][0]
                                        : default(T)))));
            optimalValues = new StackListQueue <T>((T)(dynamic)value);
            if (compliteCallback != null)
            {
                compliteCallback();
            }
            return(true);
        }
Пример #8
0
            /// <summary>
            ///     Рассмотрим следующую задачу линейного программирования:
            ///     c^Tx -> max, Ax Le b, x Ge 0, b Ge 0.
            ///     Любая общая задача ЛП может быть приведена к канонической форме.
            ///     Приведение общей задачи ЛП к канонической форме достигается путем введения новых
            ///     (их называют дополнительными) переменных.
            ///     Двухфазный симплекс-метод
            ///     Причины использования
            ///     Если в условии задачи линейного программирования не все ограничения представлены неравенствами типа «≤», то далеко
            ///     не всегда нулевой вектор будет допустимым решением. Однако каждая итерация симплекс-метода является переходом от
            ///     одной вершины к другой, и если неизвестно ни одной вершины, алгоритм вообще не может быть начат.
            ///     Процесс нахождения исходной вершины не сильно отличается от однофазного симплекс-метода, однако может в итоге
            ///     оказаться сложнее, чем дальнейшая оптимизация.
            ///     Модификация ограничений
            ///     Все ограничения задачи модифицируются согласно следующим правилам:
            ///     ограничения типа «≤» переводятся на равенства созданием дополнительной переменной с коэффициентом «+1». Эта
            ///     модификация проводится и в однофазном симплекс-методе, дополнительные переменные в дальнейшем используются как
            ///     исходный базис.
            ///     ограничения типа «≥» дополняются одной переменной с коэффициентом «−1». Поскольку такая переменная из-за
            ///     отрицательного коэффициента не может быть использована в исходном базисе, необходимо создать ещё одну,
            ///     вспомогательную, переменную. Вспомогательные переменные всегда создаются с коэффициентом «+1».
            ///     ограничения типа «=» дополняются одной вспомогательной переменной.
            ///     Соответственно, будет создано некоторое количество дополнительных и вспомогательных переменных. В исходный базис
            ///     выбираются дополнительные переменные с коэффициентом «+1» и все вспомогательные. Осторожно: решение, которому
            ///     соответствует этот базис, не является допустимым.
            ///     После того, как было модифицировано условие, создаётся вспомогательная целевая функция
            ///     Если вспомогательные переменные были обозначены, как yi, i∈{1, .., k},
            ///     то вспомогательную функцию определим, как
            ///     z' = \sum_{i=1}^k y_i -> min
            ///     После этого проводится обыкновенный симплекс-метод относительно вспомогательной целевой функции.
            ///     Поскольку все вспомогательные переменные увеличивают значение z', в ходе алгоритма
            ///     они будут поочерёдно выводится из базиса, при этом после каждого перехода новое решение
            ///     будет всё ближе к множеству допустимых решений.
            ///     Когда будет найдено оптимальное значение вспомогательной целевой функции, могут возникнуть две ситуации:
            ///     оптимальное значение z' больше нуля. Это значит, что как минимум одна из вспомогательных переменных осталась в
            ///     базисе.
            ///     В таком случае можно сделать вывод, что допустимых решений данной задачи линейного программирования не существует.
            ///     оптимальное значение z' равно нулю. Это означает, что все вспомогательные переменные были выведены из базиса,
            ///     и текущее решение является допустимым.
            ///     Во втором случае мы имеем допустимый базис, или, иначе говоря, исходное допустимое решение. Можно проводить
            ///     дальнейшую оптимизацию с учётом исходной целевой функции, при этом уже не обращая внимания на вспомогательные
            ///     переменные. Это и является второй фазой решения.
            /// </summary>
            public bool DoubleSimplexMethod(ILinearMiniMax <T> minimax)
            {
                Debug.Assert(minimax.A.Rows == minimax.R.Count());
                Debug.Assert(minimax.A.Rows == minimax.B.Count());
                Debug.Assert(minimax.A.Columns == minimax.C.Count());

                AppendLineCallback appendLineCallback = AppendLineCallback;
                ProgressCallback   progressCallback   = ProgressCallback;
                CompliteCallback   compliteCallback   = CompliteCallback;

                // количество исходныx переменныx
                // количество неравенств==количество дополнительных переменных
                // количество вспомогательных переменных
                int n      = Math.Min(minimax.A.Columns, minimax.C.Count());
                int count  = Math.Min(minimax.A.Count(), minimax.B.Count());
                int count1 = minimax.R.Count(r => r == Comparer.Ge);

                var rowsIndex    = new StackListQueue <int>(Enumerable.Range(1 + n + count1, count + 1));
                var columnsIndex = new StackListQueue <int>(Enumerable.Range(1, n + count1));

                Debug.WriteLine("count = " + count);
                Debug.WriteLine((double)minimax.Target);

                //     Модификация ограничений
                //     Все ограничения задачи модифицируются согласно следующим правилам:
                //    ограничения типа «≤» переводятся на равенства созданием дополнительной переменной с коэффициентом «+1». Эта
                //     модификация проводится и в однофазном симплекс-методе, дополнительные переменные в дальнейшем используются как
                //     исходный базис.
                //     ограничения типа «≥» дополняются одной переменной с коэффициентом «−1». Поскольку такая переменная из-за
                //     отрицательного коэффициента не может быть использована в исходном базисе, необходимо создать ещё одну,
                //     вспомогательную, переменную. Вспомогательные переменные всегда создаются с коэффициентом «+1».
                //     ограничения типа «=» дополняются одной вспомогательной переменной.
                //     Соответственно, будет создано некоторое количество дополнительных и вспомогательных переменных. В исходный базис
                //     выбираются дополнительные переменные с коэффициентом «+1» и все вспомогательные. Осторожно: решение, которому
                //     соответствует этот базис, не является допустимым.
                //После того, как было модифицировано условие, создаётся вспомогательная целевая функция
                //Если вспомогательные переменные были обозначены, как yi, i∈{1, .., k},
                //то вспомогательную функцию определим, как
                //z' = \sum_{i=1}^k y_i -> min
                //После этого проводится обыкновенный симплекс-метод относительно вспомогательной целевой функции.
                //Поскольку все вспомогательные переменные увеличивают значение z', в ходе алгоритма
                //они будут поочерёдно выводится из базиса, при этом после каждого перехода новое решение
                //будет всё ближе к множеству допустимых решений.
                //Когда будет найдено оптимальное значение вспомогательной целевой функции, могут возникнуть две ситуации:
                //оптимальное значение z' больше нуля. Это значит, что как минимум одна из вспомогательных переменных осталась в базисе.
                //В таком случае можно сделать вывод, что допустимых решений данной задачи линейного программирования не существует.
                //оптимальное значение z' равно нулю. Это означает, что все вспомогательные переменные были выведены из базиса,
                //и текущее решение является допустимым.
                var random = new Random();
                var vector =
                    new Vector <double>(0)
                {
                    Enumerable.Repeat(0.0, n),
                    Enumerable.Range(0, count1)
                    .Select(x => 100000000000000.0 + 10000000000000.0 * random.NextDouble()),
                    Enumerable.Repeat(0.0, count),
                    0,
                };

                Add(vector);

                int i1 = 1 + n + count1;
                int i2 = 1 + n;

                for (int i = 0; i < count; i++)
                {
                    var value = new Vector <double>();
                    switch (minimax.R.ElementAt(i))
                    {
                    case Comparer.Le:
                        // ограничения типа «≤» переводятся на равенства созданием дополнительной переменной с коэффициентом «+1»
                        value.Add(Convert.ToDouble(minimax.B.ElementAt(i)));
                        value.Add(minimax.A.ElementAt(i).Select(x => Convert.ToDouble(x)));
                        value.Add(Enumerable.Repeat(0.0, count + count1 + 1));
                        value[i1++] = 1;
                        break;

                    case Comparer.Eq:
                        // ограничения типа «=» дополняются одной вспомогательной переменной
                        // Вспомогательные переменные всегда создаются с коэффициентом «+1»
                        value.Add(Convert.ToDouble(minimax.B.ElementAt(i)));
                        value.Add(minimax.A.ElementAt(i).Select(x => Convert.ToDouble(x)));
                        value.Add(Enumerable.Repeat(0.0, count + count1 + 1));
                        value[i1++] = 1;
                        break;

                    case Comparer.Ge:
                        // ограничения типа «≥» дополняются одной переменной с коэффициентом «−1»
                        // Поскольку такая переменная из-за отрицательного коэффициента не может быть использована
                        // в исходном базисе, необходимо создать ещё одну, вспомогательную, переменную
                        // Вспомогательные переменные всегда создаются с коэффициентом «+1»
                        value.Add(-Convert.ToDouble(minimax.B.ElementAt(i)));
                        value.Add(minimax.A.ElementAt(i).Select(x => - Convert.ToDouble(x)));
                        value.Add(Enumerable.Repeat(0.0, count + count1 + 1));
                        value[i1++] = 1;
                        value[i2++] = 1;
                        break;
                    }
                    Add(value);
                }

                Add(new Vector <double>(0)
                {
                    minimax.C.Select(x => - Convert.ToDouble(x)),
                    Enumerable.Repeat(0.0, count1),
                    Enumerable.Repeat(0.0, count),
                    1,
                });

                RowsIndex    = rowsIndex;
                ColumnsIndex = columnsIndex;

                if (appendLineCallback != null)
                {
                    appendLineCallback("Текущий опорный план:");
                }
                if (appendLineCallback != null)
                {
                    appendLineCallback(ToString());
                }
                Debug.WriteLine("Текущий опорный план:");
                Debug.WriteLine(ToString());

                //После этого проводится обыкновенный симплекс-метод относительно вспомогательной целевой функции.
                //Поскольку все вспомогательные переменные увеличивают значение z', в ходе алгоритма
                //они будут поочерёдно выводится из базиса, при этом после каждого перехода новое решение
                //будет всё ближе к множеству допустимых решений.
                //Когда будет найдено оптимальное значение вспомогательной целевой функции, могут возникнуть две ситуации:
                //оптимальное значение z' больше нуля. Это значит, что как минимум одна из вспомогательных переменных осталась в базисе.
                //В таком случае можно сделать вывод, что допустимых решений данной задачи линейного программирования не существует.
                //оптимальное значение z' равно нулю. Это означает, что все вспомогательные переменные были выведены из базиса,
                //и текущее решение является допустимым.

                //     Transform.ByColumns:
                //     все элементы индексной строки отрицательные или ноль - при минимизации

                // Ищем допустимое решение (все элементы индексной строки отрицательные или ноль)
                SimplexMethod(Transform.ByColumns, Target.Minimum);

                Debug.WriteLine("Текущий опорный план:");
                Debug.WriteLine(ToString());

                if (this[0][0] > 0)
                {
                    return(false);
                }

                this[0] = this[this.Count]; this.RemoveAt(this.Count - 1);

                rowsIndex = new StackListQueue <int>(RowsIndex);
                rowsIndex.Pop();
                RowsIndex = rowsIndex;

                columnsIndex = new StackListQueue <int>(ColumnsIndex);
                ColumnsIndex = new StackListQueue <int>(columnsIndex.Except(Enumerable.Range(1 + n + count, count1 + 1)));

                Debug.WriteLine("Текущий опорный план:");
                Debug.WriteLine(ToString());

                // Ищется допустимое и оптимальное решение
                // (все элементы индексной строки отрицательные или ноль - при минимизации
                // все элементы индексной строки положительные или ноль - при максимизации
                // все элементы индексного столбца положительные или ноль)

                //     Transform.ByColumns:
                //     все элементы индексной строки отрицательные или ноль - при минимизации
                //     все элементы индексной строки положительные или ноль - при максимизации
                //     Transform.ByRows:
                //     все элементы индексного столбца положительные или ноль

                // Ищем допустимое решение (все элементы индексной строки отрицательные или ноль)
                // Ищем допустимое и оптимальное решение
                SimplexMethod(Transform.ByColumns, minimax.Target);
                SimplexMethod(Transform.ByRows, minimax.Target);

                return(true);
            }