faqs.org.ru

 Главная > Программирование > Общие алгоритмы и методики >

FAQ по мягким вычислениям

Секция 3 из 3 - Предыдущая - Следующая
Все секции - 1 - 2 - 3


              По Заде:

                         Ma1(x)->Ma2(x) = (1-Ma1(x)) \/ Ma2(x)

      НЕЧЕТКОЕ РАВЕНСТВО: степень нечеткого равенства

                         R(A1,A2) = V(A1,A2) & V(A2,A1)
******************************************************************************

>Библиотека операций над нечеткими множествами.
>(c) Alexander Stolyarov AKA Centurn & Yuri Burger AKA Kruger

// модуль IFUNC.H

extern IM POW(IM &s,double y);  // Степень
extern IM CON(IM &s);           // Концентрация (2)
extern IM DIN(IM &s);           // Растяжение (.5)
extern IM SUPP(IM &s);          // Носитель
extern double SUP(IM &s);       // Точная верхняя грань
extern IM NORM(IM &s);          // Нормализованное
extern IM CUT(IM &s,double a);  // Альфа-срез
extern double V(IM &s1,IM &s2); // Степень нечеткого включения
extern double M(IM &s1,IM &s2); // Степень нечеткого равенства
extern IM Sub(IM &s1,IM &s2);   // Дополнительное вычитание
extern IM Div(IM &s1,IM &s2);   // Дополнительное деление
extern IM Inter(IM &s1,IM &s2); // Интерполяция

// модуль LEXAN.H

#define THE_END 0
#define SLASH_  1007 // /
#define OBRACE_ 1011 // {
#define CBRACE_ 1012 // }
#define COMMA_  1021 // ,
#define LESS_   1024 // <
#define MORE_   1025 // >
#define NUMBER_ 10002
#define ERROR_  -1
class LexAn{
  char *SrcStr;
  int curpos;
public:
  LexAn(char *str);
  int GetLex(char *resstr);
  int GetCurPos();
};

// модуль ILIB.H

class Atom
{
public:
        double X;       // элемент
        double M;       // степень принадлежности
        Atom();
        Atom(double x,double m);
        Atom(Atom *a);
        ~Atom();
        void Print();
                void sPrint(char *s);
};
class IM
{
public:
        Atom **A;       // множество пар X/M
        long   N;       // количество пар
        IM();
        IM(IM *s);
        IM(IM& a);
        ~IM();
        void Set(IM *s);
        void   Clear();
        int    SetFromStr(char *str);
        void   sPrint(char *s);
        void   Sort();
        double GetMin();
        double GetMax();
        int    LoadFromFile(const char *filname);
        int    SaveToFile(const char *filname);
        IM  operator + (const IM &s);           // сложение
        IM  operator * (const IM &s);           // умножение
        void Print();                           // вывод
        Atom *FindAtom(double x);               // поиск элемента
        void   SetAtom(double  x,double  m);    // установить с заменой
        void SetAtom(Atom *s);
        void NewAtom(double x,double m);        // добавить новый
        void NewAtom(Atom *s);
// если add==1 то элемент будет создан (объединение)
// если add==0 то не будет создан (пересечение)
        void SetMinAtom(double x,double m,char add);
        void SetMaxAtom(double x,double m,char add);
        IM &operator = (const IM &s);
        IM &operator = (const Atom &s);
        IM  operator | (const IM &s);                  // объединение
        IM  operator & (const IM &s);                  // пересечение
        IM  operator ! ();                             // дополнение
        IM  operator - (const IM &s);                  // разность
        IM  operator * (double n);                     // умнож. на число
};

// модуль LEXAN.CPP

#include "stdafx.h"
#include "LexAn.h"
LexAn::LexAn(char *str){
  SrcStr=str;
  curpos=0;
};
int LexAn::GetLex(char *resstr){
  static char c;
  while(1){
    switch(SrcStr[curpos++]){
      case 0:return(THE_END);
      case '{':return(OBRACE_);
      case '}':return(CBRACE_);
      case '/':return(SLASH_);
      case '<':return(LESS_);
      case '>':return(MORE_);
      case ',':return(COMMA_);
      case 9:
      case 13:
      case 10:
      case ' ':break;
      default:{
        c=SrcStr[curpos-1];
        if(((c>='0')&&(c<='9'))||(c=='.')){
          static int t;t=0;
          do{
            resstr[t++]=c;
            c=SrcStr[curpos++];
          }while(((c>='0')&&(c<='9'))||c=='.');
          curpos--;
          resstr[t]=0;
          //resint=atol(resstr);
          return(NUMBER_);
        }else
          return(ERROR_);
      };
    };
  };
};
int LexAn::GetCurPos(){
  return(curpos);
};

// модуль ILIB.CPP

#include "stdafx.h"
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include "ILib.h"
Atom::Atom()
{
        X=0;
        M=0;
}
Atom::Atom(double x,double m)
{
        X=x;
        M=m;
}
Atom::Atom(Atom *a)
{
        X=a->X;
        M=a->M;
}
Atom::~Atom()
{
}
void Atom::Print()
{
        printf("<%g/%g>",X,M);
}
IM::IM()
{
        A=NULL;
        N=0;
}
IM::IM(IM *s)
{
        long t;
        N=s->N;
        A=(Atom **)calloc(N,sizeof(Atom *));
        for(t=0;t<N;t++)
        {
                A[t]=new Atom(s->A[t]);
        }
}
IM::IM(IM& a)
{
        A=NULL;
        N=0;
        Set(&a);
};
void IM::Set(IM *s)
{
        long t;
        if(A!=NULL)
        {
                for(t=0;t<N;t++)
                {
                        delete A[t];
                }
                free(A);
        }
        N=s->N;
        A=(Atom **)calloc(N,sizeof(Atom *));
        for(t=0;t<N;t++)
        {
                A[t]=new Atom(s->A[t]);
        }
}
IM::~IM()
{
        if(A!=NULL)
        {
                for(long t=0;t<N;t++)
                {
                        delete A[t];
                }
                free(A);
        }
}
void IM::Print()
{
        printf("{");
        for(long t=0;t<N;t++)
        {
                A[t]->Print();
                if(t!=N-1)printf(",");
        }
        printf("}");
}
Atom *IM::FindAtom(double x)
{
        for(long t=0;t<N;t++)
        {
                if(A[t]->X==x)return A[t];
        }
        return NULL;
}
void IM::SetAtom(double x,double m)
{
        Atom *a=FindAtom(x);
        if(a!=NULL)
        {
                a->M=m;
        }
        else
        {
                N++;
                A=(Atom **)realloc(A,N*sizeof(Atom *));
                A[N-1]=new Atom(x,m);
        }
}
void IM::SetAtom(Atom *s)
{
        Atom *a=FindAtom(s->X);
        if(a!=NULL)
        {
                a->M=s->M;
        }
        else
        {
                N++;
                A=(Atom **)realloc(A,N*sizeof(Atom *));
                A[N-1]=new Atom(s);
        }
}
void IM::NewAtom(double x,double m)
{
        N++;
        A=(Atom **)realloc(A,N*sizeof(Atom *));
        A[N-1]=new Atom(x,m);
}
void IM::NewAtom(Atom *s)
{
        N++;
        A=(Atom **)realloc(A,N*sizeof(Atom *));
        A[N-1]=new Atom(s);
}
void IM::SetMinAtom(double x,double m,char add)
{
        Atom *a=FindAtom(x);
        if(a!=NULL)
        {
                if(a->M>m)a->M=m;
        }
        else
        {
                if(add==1)
                {
                        N++;
                        A=(Atom **)realloc(A,N*sizeof(Atom *));
                        A[N-1]=new Atom(x,m);
                }
        }
}
void IM::SetMaxAtom(double x,double m,char add)
{
        Atom *a=FindAtom(x);
        if(a!=NULL)
        {
                if(a->M<m)a->M=m;
        }
        else
        {
                if(add==1)
                {
                        N++;
                        A=(Atom **)realloc(A,N*sizeof(Atom *));
                        A[N-1]=new Atom(x,m);
                }
        }
}
IM &IM::operator = (const IM &s)
{
        long t;
        if(A!=NULL)
        {
                for(t=0;t<N;t++)
                {
                        delete A[t];
                }
                free(A);
        }
        N=s.N;
        A=(Atom **)calloc(N,sizeof(Atom *));
        for(t=0;t<N;t++)
        {
                A[t]=new Atom(s.A[t]);
        }
        return *this;
}
IM &IM::operator = (const Atom &s)
{
        NewAtom(s.X,s.M);
        return *this;
}
IM  IM::operator | (const IM &s)
{
        IM q;
        long t;

        q.Set(this);
        for(t=0;t<s.N;t++)
        {
                q.SetMaxAtom(s.A[t]->X,s.A[t]->M,1);
        }

        return q;
}
IM IM::operator & (const IM &s)
{
        IM q;
        long t,r;

        for(t=0;t<N;t++)
        {
                for(r=0;r<s.N;r++)
                {
                        if(A[t]->X == s.A[r]->X)
                        {
                                if(A[t]->M < s.A[r]->M)
                                {
                                        q.NewAtom(A[t]);
                                }
                                else
                                {
                                        q.NewAtom(s.A[r]);
                                }
                        }
                }
        }
        return q;
}
IM  IM::operator ! ()
{
        IM q;
        q.Set(this);
        for(long t=0;t<N;t++)
        {
          q.A[t]->M=1-q.A[t]->M;
        }
        return q;
}
IM  IM::operator - (const IM &s)
{
        IM q;
        long t,r;

        for(t=0;t<N;t++)
        {
                for(r=0;r<s.N;r++)
                {
                        if(A[t]->X == s.A[r]->X)
                        {
                                if(A[t]->M < s.A[r]->M)
                                {
                                        q.NewAtom(A[t]->X,0);
                                }
                                else
                                {
                                   q.NewAtom(s.A[r]->X,A[t]->M - s.A[r]->M);
                                }
                        }
                }
        }
        return q;
}
IM  IM::operator * (double n)
{
        IM q;
        q.Set(this);
        for(long t=0;t<N;t++)
        {
                q.A[t]->M*=n;
        }
        return q;
}
IM POW(IM &s,double y)
{
        IM q;
        q.Set(&s);
        for(long t=0;t<s.N;t++)
        {
                q.A[t]->M=pow(q.A[t]->M,y);
        }
        return q;
}
IM CON(IM &s)
{
        IM q;
        q=POW(s,2);
        return q;
}
IM DIN(IM &s)
{
        IM q;
        q=POW(s,0.5);
        return q;
}
IM SUPP(IM &s)
{
        IM q;
        for(long t=0;t<s.N;t++)
        {
                if(s.A[t]->M > 0)
                {
                        q.NewAtom(s.A[t]);
                }
        }
        return q;
}
double SUP(IM &s)
{
        double r=-1;
        for(long t=0;t<s.N;t++)
        {
                if(s.A[t]->M > r)r=s.A[t]->M;
        }
        return r;
}
IM NORM(IM &s)
{
        IM q;
        long t;
        q.Set(&s);
        double n=SUP(s);
        if(n!=0)
        {
                for(t=0;t<s.N;t++)
                {
                        q.A[t]->M/=n;
                }
        }
        return q;
}
IM CUT(IM &s,double a)
{
        IM q;
        for(long t=0;t<s.N;t++)
        {
                if(s.A[t]->M>=a)
                {
                        q.NewAtom(s.A[t]);
                }
        }
        return q;
}
double V(IM &s1,IM &s2)
{
        long x,y;
        double m1,m2,t,r=1;
        for(x=0;x<s1.N;x++)
        {
                m1=s1.A[x]->M;
                m2=0;
                for(y=0;y<s2.N;y++)
                {
                        if(s1.A[x]->X==s2.A[y]->X)
                        {
                                m2=s2.A[y]->M;
                        }
                }
                t=1-m1+m2;
                if(t>1)t=1;
                if(r>t)r=t;
        }
        return r;
}
double M(IM &s1,IM &s2)
{
        double v1,v2;
        v1=V(s1,s2);
        v2=V(s2,s1);
        if(v1>v2)return v2;
        else return v1;
}
void IM::Clear(){
        if(A!=NULL)
        {
                for(long t=0;t<N;t++)
                {
                        delete A[t];
                }
                free(A);
                                A=NULL;
        }
        N=0;
};

// модуль CENILIB.CPP

#include "stdafx.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "LexAn.h"
#include "ILib.h"
#include "IFunc.h"
char tmpstr[255];
void Atom::sPrint(char *s){
  sprintf(s,"<%6.4g/%g>",M,X);
};
int IM::SetFromStr(char *str){
  LexAn L(str);
  static int t;
  Atom a,*b;
  Clear();
  if(L.GetLex(tmpstr)==OBRACE_){
        do{
          //t=L.GetLex(tmpstr);
          if(L.GetLex(tmpstr)!=LESS_)return(1);
          if(L.GetLex(tmpstr)!=NUMBER_)return(5);
          a.M=atof(tmpstr);
          if(L.GetLex(tmpstr)!=SLASH_)return(2);
          if(L.GetLex(tmpstr)!=NUMBER_)return(3);
          a.X=atof(tmpstr);
          if((b=FindAtom(a.X))!=NULL){
            b->M=(b->M+a.M)/2;
          }else
            NewAtom(&a);
          if(L.GetLex(tmpstr)!=MORE_)return(6);
        }while((t=L.GetLex(tmpstr))==COMMA_);
  }else
          return(1);
  if(t!=CBRACE_)return(4);
  Sort();
  return(0);
};
void IM::sPrint(char *s){
        sprintf(s,"{");
        for(long t=0;t<N;t++){
                A[t]->sPrint(tmpstr);
                strcat(s,tmpstr);
                if(t!=N-1){
                  strcat(s,", ");
                  char n[3]={0xD,0XA,0};
                  if(!((t+1)%5)){
                    strcat(s,n);
                  };
                };
        }
        strcat(s,"}");
}
void IM::Sort(){
  int i,j;
  Atom *tmp;
  for(j=N-1;j>0;j--){
    for(i=0;i<j;i++){
      if(A[i]->X>A[i+1]->X){
        tmp=A[i];
        A[i]=A[i+1];
        A[i+1]=tmp;
      };
    };
  };
};
double IM::GetMin(){
  double Min=1E+20;
  int i;
  for(i=0;i<N;i++){
    if(A[i]->X<Min)Min=A[i]->X;
  };
  return(Min);
};
double IM::GetMax(){
  double Max=-1E+20;
  int i;
  for(i=0;i<N;i++){
    if(A[i]->X>Max)Max=A[i]->X;
  };
  return(Max);
};
int IM::LoadFromFile(const char *filname){
    FILE *f=fopen(filname,"rt");
    int t;
    char *buffer=(char *)malloc(65535);
    if(f!=NULL){
          buffer[fread(buffer,1,65535,f)]=0;
          t=SetFromStr(buffer);
    }else return(1);
    free(buffer);
    fclose(f);
    return(t);
};
int IM::SaveToFile(const char *filname){
    FILE *f=fopen(filname,"wt");
    char *buffer=(char *)malloc(65535);
    if(f!=NULL){
          sPrint(buffer);
          fwrite(buffer,1,strlen(buffer),f);
    }else return(1);
    free(buffer);
    fclose(f);
    return(0);
};
IM IM::operator + (const IM &s){
  IM ret;
  double tM,tX,Min;
  int i,j,k,l;
  Sort();
  for(i=0;i<N;i++){
    for(j=0;j<s.N;j++){
          tX=A[i]->X+s.A[j]->X;
          if(ret.FindAtom(tX)==NULL){
                tM=A[i]->M>s.A[j]->M?s.A[j]->M:A[i]->M;
                for(k=i;k<N;k++){
                  for(l=0;l<s.N;l++){
                        if(A[k]->X+s.A[l]->X==tX){
                          Min=A[k]->M>s.A[l]->M?s.A[l]->M:A[k]->M;
                          if(Min>tM)tM=Min;
                        };
                  };
                };
                ret.NewAtom(tX,tM);
          };
    };
  };
  ret.Sort();
  return(ret);
};
IM IM::operator * (const IM &s){
  IM ret;
  double tM,tX,Min;
  int i,j,k,l;
  Sort();
  for(i=0;i<N;i++){
    for(j=0;j<s.N;j++){
          tX=A[i]->X*s.A[j]->X;
          if(ret.FindAtom(tX)==NULL){
                tM=A[i]->M>s.A[j]->M?s.A[j]->M:A[i]->M;
                for(k=i;k<N;k++){
                  for(l=0;l<s.N;l++){
                        if(A[k]->X*s.A[l]->X==tX){
                          Min=A[k]->M>s.A[l]->M?s.A[l]->M:A[k]->M;
                          if(Min>tM)tM=Min;
                        };
                  };
                };
                ret.NewAtom(tX,tM);
          };
    };
  };
  return(ret);
};
IM Sub(IM &s1,IM &s2){
  IM ret;
  double tM,tX,Min;
  double bMin,bMax;
  int i,j,k,l;
  s1.Sort();
  s2.Sort();
  bMin=s1.A[0]->X-s2.A[0]->X;
  bMax=s1.A[s1.N-1]->X-s2.A[s2.N-1]->X;
  for(i=0;i<s1.N;i++){
    for(j=0;j<s2.N;j++){
      tX=s1.A[i]->X-s2.A[j]->X;
      if(tX>=bMin&&tX<=bMax){
          if(ret.FindAtom(tX)==NULL){
                tM=s1.A[i]->M>=s2.A[j]->M?1:s1.A[i]->M;
                for(k=i;k<s1.N;k++){
                  for(l=0;l<s2.N;l++){
                        if(s1.A[k]->X-s2.A[l]->X==tX){
                          Min=s1.A[k]->M>=s2.A[l]->M?1:s1.A[k]->M;
                          if(Min<tM)tM=Min;
                        };
                  };
                };
                ret.NewAtom(tX,tM);
          };
      };
    };
  };
  ret.Sort();
  return(ret);
};
IM Div(IM &s1,IM &s2){
  IM ret;
  double tM,tX,Min;
  double bMin,bMax;
  int i,j,k,l;
  s1.Sort();
  s2.Sort();
  bMin=s1.A[0]->X/s2.A[0]->X;
  bMax=s1.A[s1.N-1]->X/s2.A[s2.N-1]->X;
  for(i=0;i<s1.N;i++){
    for(j=0;j<s2.N;j++){
      tX=s1.A[i]->X/s2.A[j]->X;
      if(tX>=bMin&&tX<=bMax){
          if(ret.FindAtom(tX)==NULL){
                tM=s1.A[i]->M>=s2.A[j]->M?1:s1.A[i]->M;
                for(k=i;k<s1.N;k++){
                  for(l=0;l<s2.N;l++){
                        if(s1.A[k]->X/s2.A[l]->X==tX){
                          Min=s1.A[k]->M>=s2.A[l]->M?1:s1.A[k]->M;
                          if(Min<tM)tM=Min;
                        };
                  };
                };
                ret.NewAtom(tX,tM);
          };
      };
    };
  };
  ret.Sort();
  return(ret);
};
IM Inter(IM &s1,IM &s2){
  IM ret;
  double tM,tX;
  int i,j;
  Atom *tAt;
  s1.Sort();
  s2.Sort();
  for(i=0;i<s1.N;i++){
    tX=s1.A[i]->X;
    if((tX < s2.A[0]->X) || (tX > s2.A[s2.N-1]->X) ){
      tM=0;
    }else if((tAt=s2.FindAtom(tX))!=NULL){
            tM=tAt->M;
          }else{
            for(j=1;j<s2.N;j++){
              if(s2.A[j]->X>tX)break;
            };
            tM=s2.A[j-1]->M+(tX-s2.A[j-1]->X)*
               (s2.A[j]->M-s2.A[j-1]->M)/(s2.A[j]->X-s2.A[j-1]->X);
          };
    tM=(tM+s1.A[i]->M)/2;
    ret.NewAtom(tX,tM);
  };
  for(i=0;i<s2.N;i++){
    tX=s2.A[i]->X;
    if((tX < s1.A[0]->X) || (tX > s1.A[s1.N-1]->X) ){
      tM=s1.A[i]->M/2;
      ret.NewAtom(tX,tM);
    };
  };
  return(ret);
};

// пример множества, загружаемого из файла

{<   0.8/1>, <   0.5/2>, <   0.7/3>, <   0.6/5>, <   0.2/6>}

******************************************************************************

> Словарь

     АДАПТАЦИЯ  -  Любое  изменение в структуре или функции организма, которое
позволяет ему выживать во внешней среде.
     АЛЛЕЛИ - Возможные значения генов.
     ГА  -  Генетический алгоритм. Интеллектуальное исследование произвольного
поиска. [Reeves, 1993]. Представлен Holland 1975.
     ГА   МОДЕЛЬ   ОСТРОВА   (IMGA)  -  Популяция  ГА  разделена  в  несколько
подсовокупностей,  каждая из которых беспорядочно инициализирована и выполняет
независимый   последовательный   ГА   на   собственной  подпопуляции.  Иногда,
пригодные  ветви  решений мигрируют между подсовокупностями. [Например. Levine
1994].
     ГЕНЫ - Переменные в хромосоме.
     ГЕНЕТИЧЕСКИЙ  ДРЕЙФ  -  Члены  популяции  сходятся  к  некоторой  отметке
пространства решения вне оптимума из-за накопления стохастических ошибок.
     ГЕНОТИП - Фактическая структура. Кодированная хромосома.

     ГП  -  Генетическое  программирование.  Прикладные программы использующие
принципы эволюционной адаптации к конструкции процедурного кода. [Koza 1992]
     ДИПЛОИД  -  В  каждом участке хромосомы имеется пара генов. Это позволяет
сохраняться долгосрочной памяти.
     КГА  -  Компактный  ГА  (CGA).  В  CGA,  две  или больше совокупности ген
постоянно взаимодействуют и взаимо развиваются.
     КРОССИНГОВЕР  -  Обмен отрезками хромосом родителей. В диапазоне от 75 до
95% появляются самые лучшие особи.

     ЛОКУС - Позиция гена в хромосоме.
     МУТАЦИЯ - Произвольная модификация хромосомы.
     СИНАПС - Вход нейрона.
     СХЕМА   (шемма)  -  Подмножество  подобных  хромосом,  содержащих  модель
значений гена.
     СХОДИМОСТЬ - Прогрессия к увеличивающейся однородности. Ген, как считают,
сходится когда 95% популяции имеет то же самое значение [DeJong 1975].
     УНС - Унифицированная нейронная сеть.
     ФИТНЕС-ФУНКЦИЯ  -  Значение  являющееся  целевым функциональным значением
решения.  Оно  также  называется функцией оценки или функцией цели в проблемах
оптимизации.
     ФЕНОТИП - Физическое выражение структуры. Декодированный набор ген.
     ХРОМОСОМА - Составляющий вектор, строка, или решение.
******************************************************************************

> Где искать информацию

- генетические и эволюционные алгоритмы
  http://www.chat.ru/~saisa/index.html
  http://www.genetic-programming.org/

- дифференциальное скрещивание
  http://www.icsi.berkeley.edu/~storn/code.html

- нечеткая логика:
  http://www.idisys.iae.nsk.su/fuzzy_book/content.html

- нейроны:
  http://uka.ru/people/mikhail/

- ?
  http://www.orc.ru/~stasson/neurox.html
  www.algo.4u.ru

Советуемая литература:

    Д.  -Э. Бэстенс, В. .М. Ван Ден Берг, Д. Вуд. .Нейронные сети и финансовые
рынки.., Москва, научное издательство .ТВП., 1997.

    Галушкин А. И. .Нейрокомпьютеры и их применение. Книга 1. Теория нейронных
сетей..  Москва,  Издательское  предприятие  редакции  журнала .Радиотехника.,
2000.

    Тейво   Кохонен,   Гвидо   Дебок   .Анализ  финансовых  данных  с  помощью
самоорганизующихся карт., Москва, издательский дом .Альпина., 2001.

    Ф.  Уоссерман.  .Нейрокомпьютерная  техника.,  Москва, издательство .Мир.,
1992.

    Шумский  C.  A.  .Нейрокомпьютинг и его применение в экономике и бизнесе.,
Москва, издательство МИФИ, 1998.

    А.  И.  Змитрович Интеллектуальные информационные системы. - Минск.: НТООО
"Тетра Системс", 1997. - 368с.

    В.  В.  Корнеев,  А.  Ф.  Гарев,  С.  В.  Васютин, В. В. Райх Базы данных.
Интеллектуальная обработка информации. - М.: "Нолидж", 2000. - 352с.

Секция 3 из 3 - Предыдущая - Следующая

Вернуться в раздел "Общие алгоритмы и методики" - Обсудить эту статью на Форуме
Главная - Поиск по сайту - О проекте - Форум - Обратная связь

© faqs.org.ru