Pull to refresh

Многокритериальный выбор альтернатив с использованием правил нечеткого вывода. Реализация на Java. Часть 2/3: Основной алгоритм

Reading time6 min
Views4.9K
Перед прочтением крайне рекомендую ознакомиться с первой частью статьи:
Многокритериальный выбор альтернатив с использованием правил нечеткого вывода. Часть 1/3: Теория

Для осуществления выбора необходим набор правил (интерфейс Rule), которе включают в себя набор соответствующих им нечетких логических функций (реализующих интерфейс Function) и определенные входные формализованные данные (изначально представляющие собой двухмерный массив элементов типа double).

Для бОльшего удобства данные (одномерные массивы элементов типа double) внутри класса Rule хранятся в классе-обертке Skill.

Далее правила передаются классу Facade, создающего из них нечеткие подмножества, размеры которых зависят от количества исследуемых объектов и требуемой точности (задаваемой в статическом классе Variables).

После нахождения общего функционального решения этой системы подмножеств (метод calcD) применяется процедура сравнения полученных нечетких подмножеств в единичном интервале для получения наилучшего решения (метод calcC); для этого вычисляются мощности данных уровневых множеств и их точечные оценки (метод calcS); в данном алгоритме для облегчения требуемой сортировки элементов применяется класс-структура Num.


В конце работы для каждого объекта будет получены точечные оценки, бОльшее значение которых соответствует лучшему выбору (метод getBest).

1. Представление данных.

Размеры матрицы D, влияющие на точность вычислений, задаются классом Variables

package Variables;

public class Variables {
    private static int SIZE_OF_D     = 11;
    private static double STEP_OF_D  = (double)1/(SIZE_OF_D-1);

    public static int SIZE_OF_D();/*возврат значения SIZE_OF_D*/
    public static double STEP_OF_D();/*возврат значения STEP_OF_D*/
}


Класс num, облегчающий хитрую сортировку:

package Support;

public class Num {
    public double x;
    public double u;

    public Num(double x, double u); // инициализация x и u
}


Функции — ничего интересного, просто интерфейсы, содержащие один метод и идентификатор.

package Support;

public abstract class Function {
    public String ID;

    public abstract double getY(double x);
}


Аналогично выглядит класс Skill для описания параметров каждого объекта:

package Support;

public class Skill {
    public double[] skill;

    public Skill(double[] skill);/*инициалищация массива skill заданным значением*/
}

Для работы с матрицами написан класс Matrix:
package Support;

public class Matrix {

    private double matrix[][];
    public int ROWS;
    public int COLUMNS;
public Matrix(int rows, int columns); //конструктор для пустой матрицы
public Matrix(double[][] m); //конструктор для готового массива
public double[][] getMatrix(); // возврат матрицы в виде массива
public static Matrix findMin(Matrix[] Arr) throws Exception()/* поиск минимальных значений в массиве матриц, если размеры не совпадают — выкидывается исключение*/
{
        int rows = Arr[0].getMatrix().length;
        int cols = Arr[0].getMatrix()[0].length;

        for (int i=0;i<Arr.length;i++)
            if ((Arr[i].getMatrix().length != rows) || (Arr[i].getMatrix()[0].length != cols))
                throw new Exception("Incorrect size of matrix");

        double[][] min = new double[rows][cols];
        for (int i=0;i<rows;i++)
            for (int j=0;j<cols;j++)
                min[i][j]=Double.MAX_VALUE;

        for (int i=0;i<rows;i++)
            for (int j=0;j<cols;j++)
                for (int k=0;k<Arr.length;k++)
                    if (Arr[k].getMatrix()[i][j]<min[i][j])
                        min[i][j]=Arr[k].getMatrix()[i][j];

        return new Matrix(min);
    }

public double[] getRow(int row); //возврат нужной строки матрицы
public void output(); // вывод матрицы в консоль
}


Rule – описание правил:

package Support;

public class Rule{

    public Skill[] u;
    private Function function;
    public double[] m;

    public Rule(Skill[] u, Function function) throws Exception{
        this.u = u;
        this.function = function;
        m = findMin(u);
    }

    public double[] findMin(Skill[] arr) throws Exception /*вычисление минимальных значений одного из навыков каждого  объекта (пересечение нечетких множеств — нахождение минимума их функций принадлежности)*/
	{
        int size=arr[0].skill.length;

        for (int i=0;i<arr.length;i++)
            if (arr[i].skill.length != size)
                throw new Exception("Incorrect skill");

        double[] min = new double[size];
        for (int i=0;i<min.length;i++)
            min[i] = Integer.MAX_VALUE;

        for (int i=0;i<arr.length;i++)
            for (int j=0;j<size;j++)
                if (arr[i].skill[j]< min[j])
                    min[j] = arr[i].skill[j];

        return min;
    }

    public double getF(double x){ /*возврат функции, соответствующей данному правилу*/
        return function.getY(x);
    }
}


SubsetD – используется в формировании матриц D(i) для кажого из правил

package Support;

import Variables.Variables;

public class SubsetD {

    private final boolean DEBUG = false;

    public double[][] d_arr;

    public SubsetD(Rule rule) throws Exception{
        d_arr = makeD(rule);
    } /* если правила неверны - исключение*/

    public double[][] makeD(Rule rule) throws Exception{
        d_arr = new double[rule.m.length][Variables.SIZE_OF_D()];
        
        for (int i=0;i<rule.m.length;i++)
            for (int j=0;j<Variables.SIZE_OF_D();j++){ /*вычисление элементов матрицы D, результат не может быть больше единицы — для контроля этого используется метод findMin*/
                d_arr[i][j]=findMin(1-rule.m[i]+rule.getF(j*Variables.STEP_OF_D()));
                if (DEBUG) System.out.print("m["+i+"]="+rule.m[i]+"\tj*Variables.STEP_OF_D()="+j*Variables.STEP_OF_D()+"\tY="+rule.getF(j*Variables.STEP_OF_D())+"\n");
            }
        
        return d_arr;
    }

    private double findMin(double x){
        if (x>1) return 1;
        return x;
    }
}


Один из основных классов, отвечающий за ввод данных — Input:

package Support;

public abstract class Input {
    
    protected Rule[] rl = null;
    protected Function[] func = null;
    public int PARAMS_CNT = -1;
    public int FUNC_CNT   = -1;

    public abstract Rule[] makeRules(double[][] arr) throws Exception; // инициализация правил
    public abstract void initFunc(Function[] func); // инициализация функций
    public Function[] getFunctions(); // возврат массива функий
}


2. Фасад

Венец алгоритма, производящий все требуемые вычисления.

package FLO_Engine;

import Variables.Variables;
import Support.Matrix;
import Support.Rule;
import Support.SubsetD;
import Support.Num;

public class Facade {

    boolean DEBUG_D  = false;
    boolean DEBUG_C  = false;
    boolean DEBUG_LM = false;

    Facade(boolean debug_d, boolean debug_c, boolean debug_lm){} /*конструктор с возможностью выставить нужные флаги для просмотра тех или иных результатов промежуточных вычислений*/

    public Matrix calcD(Rule[] rules) throws Exception{ /* метод цепляет массив используемый правил */
        //page 97
        
        Rule[] M = rules;

            //page 98
            SubsetD[] sD = new SubsetD[M.length];
            for (int i=0;i<M.length;i++)
                sD[i] = new SubsetD(M[i]);

            Matrix[] matr = new Matrix[sD.length];
            for (int i=0;i<sD.length;i++){
                matr[i]=new Matrix(sD[i].d_arr);
                if (DEBUG_D)
		; /*вывод матриц D(i)*/
            }
            Matrix min = Matrix.findMin(matr); /*вычисление общей матрицы D*/
            if (DEBUG_D) min.output();
            
            return min;
    }

    public double[] calcC(Matrix D){ /*подсчет точечных оценок удовлетворительности*/
        int len = D.COLUMNS;
        double[] c = new double[D.ROWS]; /* пустой массив оценок, размером соответствующий количеству альтернатив*/
        double[] x = getX(); /*получение значений X на протяжении единичного интервала*/

        for (int i=0;i<D.ROWS;i++){
            double[] u = D.getRow(i);
            
            Num[] arr = new Num[len];

            for (int j=0;j<len;j++)
                arr[j]= new Num(x[j],u[j]);

            for (int j=0;j<len;j++) /*сортировка массива элементов общего функционального решения, соответствующей i-ой альтернативе по возрастанию, при этом в такой же порядок перемещаются и соответсвующие точки единичного интервала — для удобного вычисления промежуточных данных при подсчете последнего интеграла*/
                for (int k=j;k<len;k++)
                    if (arr[k].u<arr[j].u){
                        Num temp = arr[k];
                        arr[k] = arr[j];
                        arr[j] = temp;
                    }
            c[i] = calcS(arr); /*вычисление точечной оценки удовлетворительности для i-ой альтернативы*/
        }

        return c;
    }

    public int getBest(double[] C){} // возврат номера лучшей альтернативы

    private double[] getX(){ /*вычисление значений X в соответствии с размерами матрицы D*/
        double[] temp = new double[Variables.SIZE_OF_D()];
        for (int i=0;i<temp.length;i++)
            temp[i]=Variables.STEP_OF_D()*i;
        return temp;
    }

    private double calcS(num[] arr){ /*вычисление точечной оценки удовлетворительности для данной альтернативы*/
        int size = arr.length;
        double[] x = new double[size];
        double[] u = new double[size];
        for (int i=0;i<size;i++){ /* более удобное представление пары элемента общего функционального решения и соответствующей ему точки единичного интервала*/		
            x[i]=arr[i].x;
            u[i]=arr[i].u;
        }

        double max = findMax(u); /* поиск максимального элемента общего функционального решения для текущей альтернативы */
        double s = 0.0;
        double[] m = new double[size];
        double[] lbd = new double[size];

        for (int i=0;i<size;i++){ /*перебор уровневых множеств с сопутствующими вычислениями*/
            if (i==0){
                lbd[i]=u[0];
                m[i]=calcM(x,0);
            }
            else{
                lbd[i] = u[i]-u[i-1];
                m[i] = calcM(x,i);
            }
        }

        for (int i=0;i<size;i++) /*вычисление интеграла для случая с дискретными величинами*/
            s+=(double)lbd[i]*m[i];

        return (double)s/max; //возврат точечной оценки
    }

    private double findMax(double[] arr){} /*поиск максимального элемента массива*/

    private double calcM(double[] x, int first){ /*вычисление мощности уровневого множества*/
        double s = 0.0;
        for (int i=first;i<x.length;i++)
            s+=x[i];
        return (double)s/(x.length-first);
    }

}


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

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

Многокритериальный выбор альтернатив с использованием правил нечеткого вывода. Реализация на Java. Часть 3/3: Пример
Tags:
Hubs:
Total votes 28: ↑25 and ↓3+22
Comments2

Articles