Ищем прок от применения матриц - страница 4

 

Написал код на классическом MQL5 - дабы предметно обсуждать, как его ускорить.

Решением являются 3 массива - принтуются в лог, но формат представления данных я немного изменил для второй и третьей задачи. Причина изменения- так показалось легче работать с индексами виртуальных таблиц.

Цветом выделены шапки, которых по понятным причинам нет в массивах.


Таблица №5 с результатами вычислений процента принадлежности классов к целевой.


Таблица №6 с  результатами вычислений процента откликов классов ко всей таблице (выборке).


Сколько брать данных из каждого столбца для дальнейшей работы - можно узнать из массива  arr_N_Quant или arr_Quant_Trans.

В код хотел добавить генератор больших таблиц для оценки времени работы, но пока не сделал. Может кто добавит?

Жду идеи по оптимизации и ускорению кода, в том числе интересны реализации с применением матриц и OpenCL/OpenGL.

//+------------------------------------------------------------------+
//|                                                       Primer.mq5 |
//|                        Copyright 2016, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "-Aleks-"
#property link      "https://www.mql5.com/ru/users/-aleks-"
#property version   "1.00"
#property script_show_inputs
#property strict

sinput bool Random_Data=false;//Генерировать таблицу с данными?
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
float arr_Data[];//Массив для первичных данных - выборка
float arr_Quant[];//Массив с таблицами неравенств (сплитов)
char arr_Target[];//Массив с целевыми
float arr_MiniData[]=//Массив для проверки логики с малым числом примеров - первичные данные
{
   7,0,1,
   8,1,2,
   9,0,3,
   10,1,1,
   11,0,2,
   12,1,3,
   13,0,1,
   14,1,2,
   15,0,3,
   16,1,1
};
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
float arr_MiniQuant[]=//Массив для проверки логики с малым числом примеров - таблицы неравенств (сплитов)
{
   0,10.5,
   0,14.5,
   0,15.5,
   1,0.5,
   2,0.5,
   2,1.5
};
char arr_MiniTarget[]=//Массив с целевыми
{
   0,
   1,
   0,
   1,
   0,
   1,
   0,
   1,
   0,
   1
};

int Strok_Total_Data=10;//Количество строк в таблице Data
int Stolb_Total_Data=3;//Количество столбцов в таблице Data

int Strok_Total_Quant=6;//Количество строк в таблице Quant
int Stolb_Total_Quant=2;//Количество столбцов в таблице Quant
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
{
   if(Random_Data==true)//Сгенерируем данные для оценки времени работы алгоритма
   {

   }
   else
   {
      ArrayCopy(arr_Data,arr_MiniData,0,0,WHOLE_ARRAY);
      ArrayCopy(arr_Quant,arr_MiniQuant,0,0,WHOLE_ARRAY);
      ArrayCopy(arr_Target,arr_MiniTarget,0,0,WHOLE_ARRAY);
   }


   int arr_N_Quant[];//Массив в котором будем хранить число сплитов квантовой таблицы для каждого предиктора
   ArrayResize(arr_N_Quant,Stolb_Total_Data);
   ArrayInitialize(arr_N_Quant,-1);

   int New_Size=0;//Для установки размера массива
   int N_Calc=1;//Счетчик
   int N_Pred=0;//Счетчик столбцов (//Номер предиктора)

   for(int i=1; i<Strok_Total_Quant; i++)//Найдем число сплитов для каждого предиктора
   {
      if(arr_Quant[2*i]>arr_Quant[2*(i-1)])
      {
         arr_N_Quant[N_Pred++]=N_Calc;
         if(New_Size<N_Calc)
         {
            New_Size=N_Calc;
            N_Calc=1;
         }
      }
      else N_Calc++;
      if(i+1==Strok_Total_Quant)arr_N_Quant[N_Pred++]=N_Calc;//Если последний индекс столбца, то сохраним его
   }

   int arr_Max_Calc[];//Массив в котором будем искать максимум после сортировки
   ArrayCopy(arr_Max_Calc,arr_N_Quant);//Скопируем массив
   ArraySort(arr_Max_Calc);//Отсортируем массив
   int Max_Ind=ArrayMaximum(arr_Max_Calc,0,WHOLE_ARRAY);//Присвоем индекс максимального значения массива
   int Max_N=arr_Max_Calc[Max_Ind];//Присвоем максимальное значение массива
   float arr_Quant_Trans[];//Массив, куда запишем преобразованную таблицу с неравенствами
   int Size_arr_Quant_Trans=(Max_N+1)*Stolb_Total_Data;//Размер массива транспонированной квантовой таблицы

   ArrayResize(arr_Quant_Trans,Size_arr_Quant_Trans);
   ArrayInitialize(arr_Quant_Trans,0);

   int Calc_Split=0;//Счетчик для подсчета числа сплитов
   int Index=0;
   New_Size=0;
   N_Calc=1;//Счетчик числа сплитов предиктора
   N_Pred=0;
   double Split=0.0;//Значение сплита
   for(int i=1; i<Strok_Total_Quant; i++)//Транспонируем квантовую таблицу и записываем число сплитов
   {
      arr_Quant_Trans[Stolb_Total_Data*(N_Calc)+N_Pred]=arr_Quant[2*i-1];//Значение сплита

      if(arr_Quant[2*i]>arr_Quant[2*(i-1)])//Новый сплит
      {
         Index=(int)arr_Quant[2*(i-1)];
         arr_Quant_Trans[Index]=(float)N_Calc;//Число сплитов в предикторе / столбца
         arr_N_Quant[N_Pred++]=N_Calc;
         if(New_Size<N_Calc)
         {
            New_Size=N_Calc;
            N_Calc=1;
         }
      }
      else N_Calc++;
      if(i+1==Strok_Total_Quant)//Если последний индекс столбца, то сохраним число сплитов в предикторе / столбца
      {
         arr_N_Quant[N_Pred++]=N_Calc;//Если последний индекс столбца, то сохраним его
         Index=(int)arr_Quant[2*i];
         arr_Quant_Trans[Index]=(float)N_Calc;//Число сплитов в предикторе / столбце
         arr_Quant_Trans[Stolb_Total_Data*(N_Calc)+N_Pred-1]=arr_Quant[2*i+1];//Значение сплита -1 так как это последнее значение, а счетчик N_Pred ранее по коду учел новое
      }
   }

   ushort arr_Data_Q[];//Таблица №3 с результатами классификации(квантования) выборки.
   ArrayResize(arr_Data_Q,Strok_Total_Data*Stolb_Total_Data);
   ArrayInitialize(arr_Data_Q,0);

   int Index_Data=0;//Индекс массива arr_Data и arr_Data_Q
   int Index_Quant_A=0;//Индекс массива arr_Quant_Trans для границы А
   int Index_Quant_B=0;//Индекс массива arr_Quant_Trans для границы Б

   float arr_Proc_Target[];//Процент принадлежности классов к целевой
   float arr_Proc_Otklik[];//Процент откликов классов ко всей таблице (выборке).
   ArrayResize(arr_Proc_Target,((Max_N+1)*Stolb_Total_Data)*2);//Делаем по размеру квантовой таблицы, умноженной на число разных значений (типов) целевых
   ArrayResize(arr_Proc_Otklik,((Max_N+1)*Stolb_Total_Data));//Делаем по размеру квантовой таблицы
   ArrayInitialize(arr_Proc_Target,0);
   ArrayInitialize(arr_Proc_Otklik,0);

   for(int p=0; p<Stolb_Total_Data; p++)
   {
      ushort arr_N_Class_Target[];//Считаем число целевых по их типам для каждого условного класса после квантования
      ushort arr_N_Class_Otklik[];//Считаем число откликов для каждого условного класса после квантования в выборке
      ArrayResize(arr_N_Class_Target,((int)arr_Quant_Trans[p]+1)*2);//Умножаем на количество разных целевый ("0" и "1")
      ArrayResize(arr_N_Class_Otklik,((int)arr_Quant_Trans[p]+1));//
      ArrayInitialize(arr_N_Class_Target,0);
      ArrayInitialize(arr_N_Class_Otklik,0);

      for(int i=0; i<Strok_Total_Data; i++)
      {
         Index_Data=Stolb_Total_Data*i+p;
         for(ushort q=0; q<arr_Quant_Trans[p]+1; q++)
         {
            if(arr_Quant_Trans[p]<1)
            {
               arr_Data_Q[Index_Data]=0;//Обнуляем значение предиктора, так как нет информации о сплитах
               break;
            }
            else
            {
               if(q==0)//Первый сплит
               {
                  Index_Quant_A=Stolb_Total_Data*(q+1)+p;//Рассчитаем индекс первой границы
                  if(arr_Data[Index_Data]<=arr_Quant_Trans[Index_Quant_A])
                  {
                     arr_Data_Q[Index_Data]=q+1;
                     break;
                  }
               }
               else
               {
                  Index_Quant_A=Stolb_Total_Data*(q+1-1)+p;//Рассчитаем индекс первой границы
                  Index_Quant_B=Stolb_Total_Data*(q+1)+p;//Рассчитаем индекс последней границы
                  if(q==arr_Quant_Trans[p])//Последний сплит "q" будет равно arr_Quant_Trans[p] при выходе за пределы массива)
                  {
                     if(arr_Data[Index_Data]>arr_Quant_Trans[Index_Quant_A])
                     {
                        arr_Data_Q[Index_Data]=q+1;
                        break;

                     }
                  }
                  else//Средние сплиты
                  {
                     if(arr_Data[Index_Data]>arr_Quant_Trans[Index_Quant_A] && arr_Data[Index_Data]<=arr_Quant_Trans[Index_Quant_B])
                     {
                        arr_Data_Q[Index_Data]=q+1;
                        break;
                     }

                  }
               }
            }
         }
         int Calc_Index=0;
         switch(arr_Target[i])
         {
         case 0:
            Calc_Index=((int)arr_Data_Q[Index_Data]-1)*2;
            break;
         case 1:
            Calc_Index=((int)arr_Data_Q[Index_Data]-1)*2+1;
            break;
         }

         arr_N_Class_Target[Calc_Index]++;//Прибавим 1 для подсчета целевой
         Calc_Index=((int)arr_Data_Q[Index_Data]-1);//Индекс для массива arr_N_Class_Otklik
         arr_N_Class_Otklik[Calc_Index]++;//Прибавим 1 для подсчета откликов
      }

      for(ushort q=0; q<arr_Quant_Trans[p]+1; q++)//Посчитаем проценты
      {
         int Summ=arr_N_Class_Target[2*q]+arr_N_Class_Target[2*q+1];
         if (Summ>0)
         {
            arr_Proc_Target[Stolb_Total_Data*2*q+p*2]=float((double)arr_N_Class_Target[2*q]/(double)Summ*100.0);//Целевая "0"
            arr_Proc_Target[Stolb_Total_Data*2*q+p*2+1]=float((double)arr_N_Class_Target[2*q+1]/(double)Summ*100.0);//Целевая "1"
         }
         arr_Proc_Otklik[Stolb_Total_Data*q+p]=float((double)arr_N_Class_Otklik[q]/(double)Strok_Total_Data*100.0);

      }
//break;
   }
   Print("Задача №1 Таблица №3 с результатами классификации выборки.");
   ArrayPrint(arr_Data_Q);
   Print("Таблица №5 с результатами вычислений процента принадлежности классов к целевой.");
   ArrayPrint(arr_Proc_Target);
   Print("Таблица №6 с  результатами вычислений процента откликов классов ко всей таблице (выборке).");
   ArrayPrint(arr_Proc_Otklik);
}
//+------------------------------------------------------------------+
Код на R или Python так же приветствуется!
 
Aleksey Vyazmikin #:

Можете показать, как реализовать это преимущество на конкретном примере?

Ниже я выложу код, как сделал - наверняка не оптимально и на MQL5 - поэтому открыт к критике и замечаниям.

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

Мне это быстродействие понадобилось для вывода в файл статистики - отчета о результатах имитации торговли за прошедшую неделю на 37 счетах по 29 инструментам с десятком характеристик этой торговли. Это не матрица, есть разные типы данных, скорее следовало бы отнести  это к таблицам.

 

ещё раз - у вас задача не матричных операций. Там приткнуть их некуда

опции OMP или CUDA запрятанные внутрь их операторов, вам не помогут.

Перемножение двух матриц - очевидно будет быстрее средствами ново-встроенных типов vector/matrix. А у вас оно где-нить может быть применимо ? 

 
Aleksey Vyazmikin #:

Спасибо! А конкретной ссылке нет куда смотреть?  Увы, я не знаю где искать :(

В поиске можно ввести CMatrixDouble и получить кучу результатов. Определение находится тут: %MQL5\Include\Math\Alglib\matrix.mqh.

 
Vladimir #:

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

Мне это быстродействие понадобилось для вывода в файл статистики - отчета о результатах имитации торговли за прошедшую неделю на 37 счетах по 29 инструментам с десятком характеристик этой торговли. Это не матрица, есть разные типы данных, скорее следовало бы отнести  это к таблицам.

К сожалению, без реальных примеров сложно понять, если ничего подобного ранее не делал.

А разработчики - нет, они ничего не доказывают, не их это дело.

 
Maxim Kuznetsov #:

ещё раз - у вас задача не матричных операций. Там приткнуть их некуда

опции OMP или CUDA запрятанные внутрь их операторов, вам не помогут.

Перемножение двух матриц - очевидно будет быстрее средствами ново-встроенных типов vector/matrix. А у вас оно где-нить может быть применимо ? 

Если бы я видел возможность применить, то не открыл бы ветку. Бывает, что люди находят нестандартное применение обыденным инструментам, может это тот случай - поэтому и решил обсудить.

 
Denis Kirichenko #:

В поиске можно ввести CMatrixDouble и получить кучу результатов. Определение находится тут: %MQL5\Include\Math\Alglib\matrix.mqh.

Не совсем понял, что я могу увидеть в этом классе полезного, быть может покажете конкретный пример применительно к выше описанной задаче?

 
Aleksey Vyazmikin #:

Если бы я видел возможность применить, то не открыл бы ветку. Бывает, что люди находят нестандартное применение обыденным инструментам, может это тот случай - поэтому и решил обсудить.

вам нужно применить модные в этом сезоне средства или решать задачу ?

вы уж определитесь..:-)

---

и кстати про определители, если надо ознакомиться где и как применимы vector и matrix то стоит почитать материалы https://www.alglib.net/ и https://www.gnu.org/software/gsl/doc/html/index.html

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

 
Maxim Kuznetsov #:

вам нужно применить модные в этом сезоне средства или решать задачу ?

вы уж определитесь..:-)

Мне нужно ускорить вычисления - в этом цель.

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

Далее по коду - в нем есть расчет процентов в массивах, и эта часть кода кажется претендентом на матричные/векторные вычисления.

Нужно сложить два вектора в матрице, потом каждый вектор поделить на полученную сумму предшествующего действия и результат умножить на 100 (можно на число или надо вектор заполнять соразмерный им - не знаю пока). 

      for(ushort q=0; q<arr_Quant_Trans[p]+1; q++)//Посчитаем проценты
      {
         int Summ=arr_N_Class_Target[2*q]+arr_N_Class_Target[2*q+1];
         if (Summ>0)
         {
            arr_Proc_Target[Stolb_Total_Data*2*q+p*2]=float((double)arr_N_Class_Target[2*q]/(double)Summ*100.0);//Целевая "0"
            arr_Proc_Target[Stolb_Total_Data*2*q+p*2+1]=float((double)arr_N_Class_Target[2*q+1]/(double)Summ*100.0);//Целевая "1"
         }
         arr_Proc_Otklik[Stolb_Total_Data*q+p]=float((double)arr_N_Class_Otklik[q]/(double)Strok_Total_Data*100.0);
      }
 
Aleksey Vyazmikin #:

Мне нужно ускорить вычисления - в этом цель.

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

Далее по коду - в нем есть расчет процентов в массивах, и эта часть кода кажется претендентом на матричные/векторные вычисления.

Нужно сложить два вектора в матрице, потом каждый вектор поделить на полученную сумму предшествующего действия и результат умножить на 100 (можно на число или надо вектор заполнять соразмерный им - не знаю пока). 

Не надо просить разработчиков, если нужна матрица отличий - вычитаем матрицы друг их друга, поскольку https://www.mql5.com/ru/docs/matrix/matrix_operations (*):

"Математические операции — сложение, вычитание, умножение и деление — можно производить над матрицами и векторами почленно. "

Если нужны более хитрые сравнения, то https://www.mql5.com/ru/docs/matrix/matrix_manipulations :

Compare

Сравнивает элементы двух матриц/векторов с указанной точностью

CompareByDigits

Сравнивает элементы двух матриц/векторов на совпадение с точностью до значащих цифр

По поводу умножения на 100 см. пример на той же странице (*)  с вычитанием скаляра из вектора:

double avr=r.Mean();       // среднее значение массива
vector d=r-avr;            // вычислим массив отклонений от среднего значения

Документация по MQL5: Методы матриц и векторов / Манипуляции / Compare
Документация по MQL5: Методы матриц и векторов / Манипуляции / Compare
  • www.mql5.com
Compare - Манипуляции - Методы матриц и векторов - Справочник MQL5 - Справочник по языку алгоритмического/автоматического трейдинга для MetaTrader 5