알고리즘 최적화. - 페이지 4

 
MetaDriver :
이제 문제가 있습니다. 요소를 복사하는 데 많은 "비용"이 드는 배열을 효율적으로 정렬하는 것이 필요합니다(즉, 요소가 방대하고 "무거운" 구조, 클래스 개체 , 긴 문자열 등). 상식에 따르면 그것들을 제자리에 두고 대신 일종의 포인터(원래 위치의 셀 인덱스)로 정렬해야 합니다. 여기에서 추가 인용: https://www.mql5.com/en/forum/6476#comment_178318현재 존경받는 터미널 개발자에게 수많은 현재 작업을 맡기고 mql5에서 구현하겠습니다.

모든 것은 이미 우리 앞에서 도난당했습니다. :)

MQL5의 스프레드시트

입력은 정렬되는 배열의 복사본이어야 하며, 주석은 주석 처리되지 않고, 불필요한 주석은 제거되어야 합니다.

 //+------------------------------------------------------------------+
//void QuickSort(double &A[],int &r[],int from,int to)
void QuickSort( double &A[], int from , int to)
  {
   int i,j; //,itemp;
   double x,temp;
   if ( from >=to) return ;
   i= from ;
   j=to;
   x=A[( from +to)>> 1 ];
   while (i<=j)
     {
       while (A[i]<x)i++; 
       while (A[j]>x)j--;
       if (i<=j)
        {
         temp=A[i]; A[i]=A[j]; A[j]=temp;
         //itemp=r[i]; r[i]=r[j]; r[j]=itemp;
         i++;
         j--;
        }
     }
   QuickSort(A, from ,j);
   //QuickSort(A,r,from,j);
   QuickSort(A,i,to);  
   //QuickSort(A,r,i,to);
  }
//+------------------------------------------------------------------+
 
Urain :

모든 것은 이미 우리 앞에서 도난당했습니다. :)

MQL5의 스프레드시트

입력은 정렬되는 배열의 복사본이어야 하며, 주석은 주석 처리되지 않고, 불필요한 주석은 제거되어야 합니다.

사악해! 노래 망쳤어...아니 오히려 노력했어. :)

// 좋은 근사치가 있음이 분명합니다. 같은 성공으로 표준 라이브러리에서 샘플을 꺼낼 수 있었습니다. :)

샘플이 있습니다. 그리고 심지어 멋진 것들까지. 하지만 여전히. 여기서 모든 것을 등록하고 별도의 사용 가능한 제품의 작동 상태로 디버그하는 것이 중요합니다. 더욱이 (내가 이것을 여기에 보내는 이유) - 가능한 한 빨리. 저것들. 가능한 모든 성능을 최대 1/100%까지 짜낼 것을 제안합니다. :)

이것이 첫 번째입니다. 둘째, 객체의 경우 인덱스 배열의 수는 일반적으로 여러 가지가 될 수 있는 정렬 기준의 수와 동일하게 요청됩니다. +(바람직하게는) 여러 기준으로 정렬된 인덱스 배열에 삽입하는 기능입니다.

 
MetaDriver :

사악해! 노래 망쳤어...아니 오히려 노력했어. :)

// 좋은 근사치가 있음이 분명합니다. 같은 성공으로 표준 라이브러리에서 샘플을 꺼낼 수 있었습니다. :)

샘플이 있습니다. 그리고 심지어 멋진 것들까지. 하지만 여전히. 여기서 모든 것을 등록하고 별도의 사용 가능한 제품의 작동 상태로 디버그하는 것이 중요합니다. 더욱이 (내가 이것을 여기에 보내는 이유) - 가능한 한 빨리. 저것들. 가능한 모든 성능을 최대 1/100%까지 짜낼 것을 제안합니다. :)

이것이 첫 번째입니다. 둘째, 객체의 경우 인덱스 배열의 수는 일반적으로 여러 가지가 될 수 있는 정렬 기준의 수와 동일하게 요청됩니다. +(바람직하게는) 여러 기준으로 정렬된 인덱스 배열에 삽입하는 기능입니다.

MQL5의 여전히 동일한 답변 스프레드시트

모든 것이 거기에 있습니다. 테이블을 옆으로 돌립니다. 글쎄, 특정 작업의 경우 열이 아닌 행을 조작하기 위해 다시 만들 수 있습니다. 열은 거기에 만들어져 다른 유형을 선언할 수 있습니다. 테이블 유형이 1이면 모든 것을 재생할 수 있습니다.

 

기본 유형에 대한 인덱스 정렬과 함께 포함을 만들었습니다.

 bool ArrayIndexSort( const void &source[], int &index[], bool byDescending= true );

기본 정렬은 "내림차순"이며 오름차순으로 정렬하려면 정렬 방향 플래그를 false로 설정합니다.

테스트 결과: // 배열 인덱싱 double[], int[], string[]; 순차: 원시 배열, 내림차순, 오름차순

 2012.04 . 08 05 : 04 : 46      IndexSort_Test (USDJPY,M30)     SArray[index[i]] = {MetaQuotes, Абырвалг, Газета, Колбаса, Компилятор, Молоко, Препроцессор, Яйцо}
2012.04 . 08 05 : 04 : 46      IndexSort_Test (USDJPY,M30)     SArray[index[i]] = {Яйцо, Препроцессор, Молоко, Компилятор, Колбаса, Газета, Абырвалг, MetaQuotes}
2012.04 . 08 05 : 04 : 46      IndexSort_Test (USDJPY,M30)     SArray[i]        = {Абырвалг, Газета, MetaQuotes, Колбаса, Молоко, Яйцо, Компилятор, Препроцессор}
2012.04 . 08 05 : 04 : 46      IndexSort_Test (USDJPY,M30)     iarray[index[i]] = { 89 , 2393 , 3742 , 4772 , 5098 , 5364 , 5560 , 6226 , 6758 , 7029 , 7245 , 7540 , 8097 , 8195 , 8908 , 8945 }
2012.04 . 08 05 : 04 : 46      IndexSort_Test (USDJPY,M30)     iarray[index[i]] = { 8945 , 8908 , 8195 , 8097 , 7540 , 7245 , 7029 , 6758 , 6226 , 5560 , 5364 , 5098 , 4772 , 3742 , 2393 , 89 }
2012.04 . 08 05 : 04 : 46      IndexSort_Test (USDJPY,M30)     iarray[i]        = { 8945 , 7245 , 8195 , 5364 , 8097 , 5560 , 5098 , 3742 , 89 , 6758 , 6226 , 7029 , 4772 , 7540 , 8908 , 2393 }
2012.04 . 08 05 : 04 : 46      IndexSort_Test (USDJPY,M30)     darray[index[i]] = { 0.29 , 13.40 , 16.11 , 28.86 , 31.05 , 35.63 , 38.71 , 39.65 , 47.79 , 50.33 , 50.40 , 59.39 , 63.31 , 65.35 , 70.65 , 78.98 }
2012.04 . 08 05 : 04 : 46      IndexSort_Test (USDJPY,M30)     darray[index[i]] = { 78.98 , 70.65 , 65.35 , 63.31 , 59.39 , 50.40 , 50.33 , 47.79 , 39.65 , 38.71 , 35.63 , 31.05 , 28.86 , 16.11 , 13.40 , 0.29 }
2012.04 . 08 05 : 04 : 46      IndexSort_Test (USDJPY,M30)     darray[i]        = { 50.33 , 47.79 , 39.65 , 70.65 , 31.05 , 16.11 , 38.71 , 78.98 , 50.40 , 35.63 , 0.29 , 63.31 , 13.40 , 59.39 , 28.86 , 65.35 }

예고편에서 라이브러리 및 테스트.

포함을 "MQL5\Include\Indexes\" 폴더에 넣습니다.

파일:
 

다음은 OCL 작업을 위한 수업 준비입니다. 물론 어떤 것은 완성되지 않고 서툴지만 누군가에게 유용할 수 있습니다.

 //——————————————————————————————————————————————————————————————————————————————
//|                                                            class   OCL.mqh |
//|                                                Copyright 2011, JQS aka Joo |
//|                                        https://www.mql5.com/ru/users/joo |
//——————————————————————————————————————————————————————————————————————————————
#property copyright "Copyright 2011, JQS aka Joo"
#property link       "https://www.mql5.com/ru/users/joo"
//——————————————————————————————————————————————————————————————————————————————
class OCL
{
public :
   bool               DeviceInfo( int DeviceID);
   bool               InitOCL   ( int DeviceID, uint &Offset[], uint &Work[], int BuffCount, int ArgCount, string Source);
   bool               BuffInit  ( int buffID, int size, int regime);
   bool               Execute   ( int work_dim);
   void               DeinitOCL ();
   //---
   int                BuffID[]; // идентификаторы буферов
   //----------------------------------------------------------------------------
private :
   int                cl_ctx;   // идентификатор контекста
   int                cl_prg;   // идентификатор программы
   int                cl_krn;   // идентификатор ядра
   //---
   int                ArguID[]; // идентификаторы аргументов
   uint               offset[]; 
   uint               work  [];
};
//——————————————————————————————————————————————————————————————————————————————
bool OCL::DeviceInfo( int DeviceID)
{
   long DeviceCount=CLGetInfoInteger( 0 ,CL_DEVICE_COUNT);
   if (DeviceCount< 1 )
     return ( false );
   else
  {
     string s= "Всего устройств OCL: " +( string )CLGetInfoInteger( 0 ,CL_DEVICE_COUNT)+ " - " ;
     long DeviceType=CLGetInfoInteger(DeviceID,CL_DEVICE_TYPE);

     switch (DeviceType)
    {
     case CL_DEVICE_ACCELERATOR:
      s=s+ "Используется спец.OpenCL ускоритель (например, IBM CELL Blade)" ;
       break ;
     case CL_DEVICE_CPU:
      s=s+ "Используется CPU" ;
       break ;
     case CL_DEVICE_GPU:
      s=s+ "Используется GPU" ;
       break ;
     case CL_DEVICE_DEFAULT:
      s=s+ "Устройство по умолчанию" ;
       break ;
     case CL_DEVICE_CUSTOM:
      s=s+ "Специализированный ускоритель, не поддерживает OpenCL" ;
       break ;
     default :
      s=s+ "Непонятное устройство, скорее всего это какое то устройство по умолчанию" ;
       break ;
    }
     Print (s);
     return ( true );
  }
}
//——————————————————————————————————————————————————————————————————————————————
bool OCL::InitOCL( int DeviceID, uint &Offset[], uint &Work[], int BuffCount, int ArgCount, string Source)
{
   ArrayResize (offset, ArraySize (Offset)); ArrayCopy (offset,Offset, 0 , 0 , WHOLE_ARRAY );
   ArrayResize (work,   ArraySize (Work));   ArrayCopy (work,  Work,   0 , 0 , WHOLE_ARRAY );

   if ((cl_ctx=CLContextCreate(DeviceID))==- 1 )
  {
     Print ( "OpenCL не найден: " , GetLastError ());
     return ( false );
  }
//----------------------------------------------------------------------------
// Создаем OpenCL программу из исходного кода
   if ((cl_prg=CLProgramCreate(cl_ctx,Source))==- 1 )
  {
    CLContextFree(cl_ctx);
     Print ( "Ошибка созания OpenCL-программы" );
     switch ( GetLastError ())
    {
     case ERR_OPENCL_INVALID_HANDLE:
       Print ( "Некоректный хендл на программу OpenCL" );
       break ;
     case ERR_INVALID_PARAMETER :
       Print ( "Некоректный строковой параметр" );
       break ;
     case ERR_OPENCL_TOO_LONG_KERNEL_NAME:
       Print ( "Имя кернела содержит более 127 символов" );
       break ;
     case ERR_OPENCL_KERNEL_CREATE:
       Print ( "Внутренняя ошибка при создании объекта OpenCL." );
       break ;
     default : Print ( "Неопознанная ошибка." );
    }
     return ( false );
  }
   //----------------------------------------------------------------------------
   //Создаем точку входа в программу OpenCL  
   if ((cl_krn=CLKernelCreate(cl_prg, "Work" ))==- 1 )
  {
    CLProgramFree(cl_prg);
    CLContextFree(cl_ctx);
     Print ( "Ошибка создания OpenCL-ядра" );
     return ( false );
  }
   //----------------------------------------------------------------------------
   ArrayResize (BuffID,BuffCount);
   ArrayResize (ArguID,ArgCount);
   return ( true );
}
//——————————————————————————————————————————————————————————————————————————————
void OCL::DeinitOCL()
{
   for ( int i= 0 ;i< ArraySize (BuffID);i++)
    CLBufferFree(BuffID[i]);

  CLKernelFree (cl_krn);
  CLProgramFree(cl_prg);
  CLContextFree(cl_ctx);
}
//——————————————————————————————————————————————————————————————————————————————
bool OCL::Execute( int work_dim)
{
   if (!CLExecute(cl_krn,work_dim,offset,work))
  {
     Print ( "Ошибка при расчете в OpenCL" );
     return ( false );
  }
   return ( true );
}
//——————————————————————————————————————————————————————————————————————————————
bool OCL::BuffInit( int buffID, int size, int regime)
{
   if (buffID>= ArraySize (BuffID))
     return ( false );

  BuffID[buffID]=CLBufferCreate(cl_ctx,size,regime);
   if (BuffID[buffID]==- 1 )
  {
     Print ( "OpenCL buffer create failed" );
     switch ( GetLastError ())
    {
     case ERR_OPENCL_INVALID_HANDLE:
       Print ( "Нкоректный хендл на контекст OpenCL" );
       break ;
     case ERR_NOT_ENOUGH_MEMORY :
       Print ( "Недостаточно памяти" );
       break ;
     case ERR_OPENCL_BUFFER_CREATE:
       Print ( "Внутренняя ошибка создания буфера" );
       break ;
     default : Print ( "Неопознанная ошибка." );
    }
     return ( false );
  }
   //Выставляем буфер OpenCL в качестве параметра функции OpenCL
   if (!CLSetKernelArgMem(cl_krn,buffID,BuffID[buffID]))
     return ( false );
   return ( true );
}
//——————————————————————————————————————————————————————————————————————————————


ZY Bitok이 초기화를 변경하여 이제 다차원 계산을 실행할 수 있습니다.

 
좋아요, 좋아요. C와 같은 코드를 다시 만드시겠습니까?
 

좋은 주제!

지금 막 가격 극단(최소) 검색 알고리즘의 최적화 문제가 발생했습니다. 조건은 다음과 같습니다. 막대가 있습니다. 왼쪽과 오른쪽에 n개의 막대가 있으며 그 중 최대값보다 낮습니다(높음).

n 은 임의로 선택한 값입니다. 기간 n은 항상 홀수입니다. 오른쪽과 왼쪽에 있는 두 세그먼트의 합은 항상 짝수이고 여기에 실제 가격 극단의 중앙 막대가 추가됩니다.

알고리즘의 첫 번째 버전에 대해서는 별로 생각하지 않고 가장 눈에 띄는 코드를 작성했습니다. 지금 저는 WealthLab 플랫폼의 프레임워크 내에서 C#으로 작성하고 있지만 문제가 되는 알고리즘의 본질을 쉽게 이해할 수 있을 거라 생각합니다. 가장 문제가 되는 부분은 다음과 같습니다.

 //Перебираем все бары
for ( int bar = 0 ; bar < MyQuotes.Count; bar++)
{
    //Подготовка данных
   ...
   int pperiod = ( N - 1)/2;
   //Перебираем бары относительно потенциального экстремума
    for ( int i = 1 ; i <= pperiod; i++)
   {
      if (MyQuotes.High[bar - i] > MyQuotes.High[bar] ||
         MyQuotes.High[bar + i] > MyQuotes.High[bar])
         up = false ;
      if (MyQuotes.Low[bar - i] < MyQuotes.Low[bar] ||
         MyQuotes.Low[bar + i] < MyQuotes.Low[bar])
         down = false ;
   }
}

전체 문제는 두 번째 주기에 있습니다. 잠재적 극값에서 왼쪽 및 오른쪽 분기를 동시에 처리하므로 (N - 1)/2개의 막대만 반복하지만 이것으로는 충분하지 않습니다. 측정값은 산술 진행에서 극한값을 찾는 데 소요된 시간이 기간 N에 따라 달라지는 것을 보여줍니다. 이는 매우 매우 나쁩니다.

 N         Time
3        00 : 00 : 00.5460312
4        00 : 00 : 00.4720270
5        00 : 00 : 00.4820276
6        00 : 00 : 00.4250243
7        00 : 00 : 00.4410252
8        00 : 00 : 00.4350249
9        00 : 00 : 00.4300246
10        00 : 00 : 00.4380251
11        00 : 00 : 00.4520258
12        00 : 00 : 00.4420253
13        00 : 00 : 00.4500257
14        00 : 00 : 00.4640266
15        00 : 00 : 00.5100291
16        00 : 00 : 00.4950284
17        00 : 00 : 00.5200297
18        00 : 00 : 00.5110292
19        00 : 00 : 00.5090291
20        00 : 00 : 00.5690326
21        00 : 00 : 00.5320304
22        00 : 00 : 00.5560318
23        00 : 00 : 00.5750329
24        00 : 00 : 00.5850335
25        00 : 00 : 00.6140351
26        00 : 00 : 00.6120350
27        00 : 00 : 00.6160352
28        00 : 00 : 00.6510373
29        00 : 00 : 00.6510372
30        00 : 00 : 00.6770387
31        00 : 00 : 00.6900395
32        00 : 00 : 00.7040403
33        00 : 00 : 00.7000400
34        00 : 00 : 00.7190411
35        00 : 00 : 00.7320419
36        00 : 00 : 00.7510430
37        00 : 00 : 00.7510429
38        00 : 00 : 00.8290474
39        00 : 00 : 00.7760444
40        00 : 00 : 00.8080463
41        00 : 00 : 00.7990457
42        00 : 00 : 00.8240471
43        00 : 00 : 00.8460484
44        00 : 00 : 00.8690497
45        00 : 00 : 00.8680496
46        00 : 00 : 00.9120522
47        00 : 00 : 00.8870507
48        00 : 00 : 00.9520545
49        00 : 00 : 00.9230528
50        00 : 00 : 00.9430539
51        00 : 00 : 00.9460541
52        00 : 00 : 00.9590549
53        00 : 00 : 00.9750558
54        00 : 00 : 00.9920567
55        00 : 00 : 01.0010573
56        00 : 00 : 01.0440597
57        00 : 00 : 01.0400595
58        00 : 00 : 01.0610607
59        00 : 00 : 01.0610606
60        00 : 00 : 01.0860622
61        00 : 00 : 01.0730613
62        00 : 00 : 01.1170639
63        00 : 00 : 01.1400652
64        00 : 00 : 01.1370651
65        00 : 00 : 01.1190640
66        00 : 00 : 01.1960684
67        00 : 00 : 01.1740671
68        00 : 00 : 01.2110693
69        00 : 00 : 01.2490715
70        00 : 00 : 01.3010744
71        00 : 00 : 01.2750730
72        00 : 00 : 01.3090748
73        00 : 00 : 01.3000744
74        00 : 00 : 01.3060747
75        00 : 00 : 01.3610779
76        00 : 00 : 01.3740785
77        00 : 00 : 01.4190812
78        00 : 00 : 01.3500772
79        00 : 00 : 01.3350764
80        00 : 00 : 01.3530774
81        00 : 00 : 01.4690840
82        00 : 00 : 01.4270816
83        00 : 00 : 01.3870794
84        00 : 00 : 01.4250815
85        00 : 00 : 01.4250815
86        00 : 00 : 01.4500829
87        00 : 00 : 01.4600835
88        00 : 00 : 01.4630837
89        00 : 00 : 01.4500830
90        00 : 00 : 01.5060861
91        00 : 00 : 01.4930854
92        00 : 00 : 01.5340878
93        00 : 00 : 01.5620893
94        00 : 00 : 01.5470885
95        00 : 00 : 01.5450884
96        00 : 00 : 01.5730899
97        00 : 00 : 01.5640895
98        00 : 00 : 01.5840906
99        00 : 00 : 01.5970914

기간의 열거는 시간의 산술적 진행의 합을 취하며 이것은 매우 큰 값입니다.

한 가지 가능한 솔루션은 변하기 쉬운. 결국, 극값이 발견되면 (N - 1)/2 동안 오른쪽에 단 하나의 막대가 없다는 보장이 있으므로 새 극값에 대한 검색은 다음 막대에서 시작할 수 있습니다. current_bar + (N - 1)/2. 그러나 극단과 함께 최저점도 찾아야 하며, current_bar + (N - 1)/2 막대 이전에 새로운 최저점을 찾을 수 있습니다. 따라서 극한값과 최소값에 대한 검색을 두 개의 패스로 나누어 성능 향상을 무효화해야 합니다. C#에서는 두 개의 패스를 두 개의 코어에서 동시에 처리되는 두 개의 스레드로 쉽게 나눌 수 있지만 먼저 최적의 알고리즘을 찾아 최적화하고 싶습니다. 전문가님들의 도움을 기다립니다.

 
C-4 : 방금 가격 극한(최소) 검색 알고리즘의 최적화 문제가 발생했습니다.

글쎄요, 최적화 문제가 아닙니다.

기간의 열거는 시간의 산술적 진행의 합을 취하며 이것은 매우 큰 값입니다.

극값 검색은 일종의 O(n) 차수 문제이며, 여기서 n은 데이터 수입니다. 이 점근선이 어떻게 악화될 수 있습니까? O(n^2) - 상상조차 할 수 없습니다. 또는 용어가 혼란스럽습니다.

가장 간단한 알고리즘은 ArraySort() 를 정렬하는 것입니다. 내장은 O(n * ln( n ) ) 영역에서 매우 빠릅니다. 그러나 아마도 이 작업에는 과도할 것입니다.

더 빠른 재귀적인 것을 생각해낼 수 있습니다.

최소값과 막대 수를 얼마나 오래 검색합니까? 글쎄, 나는 100개의 막대에서 당신이 최대 1.5초를 찾고 있다고 믿지 않는다.

 
Mathemat :

가장 간단한 알고리즘은 ArraySort()를 정렬하는 것이며 내장형은 충분히 빠릅니다. 그러나 아마도 이 작업에는 과도할 것입니다.

가장 좋은 정렬은 O(n*log(n))입니다. 절대적으로 중복입니다.

더 빠른 재귀적인 것을 생각해낼 수 있습니다.

천천히. 재귀는 대부분 악입니다. 재발? 어떻게 하든 속도가 거의 같을 때의 경우일 것입니다.

코드별:

 //Перебираем все бары
for ( int bar = 0 ; bar < MyQuotes.Count; bar++)
{
   //Подготовка данных
   ...
   int pperiod = (N - 1 )/ 2 ;
   //Перебираем бары относительно потенциального экстремума
   for ( int i = 1 ; i <= pperiod; i++)
   {
       if (MyQuotes.High[bar - i] > MyQuotes.High[bar] ||
         MyQuotes.High[bar + i] > MyQuotes.High[bar])
         up = false ;
       if (MyQuotes.Low[bar - i] < MyQuotes.Low[bar] ||
         MyQuotes.Low[bar + i] < MyQuotes.Low[bar])
         down = false ;
   }
}

최소 및 최대 주기는 명확하게 구분되어야 합니다. 그리고 실패하면 즉시 루프를 종료하십시오.

 
TheXpert : 이것은 당신이 어떻게 하든 속도가 거의 같을 때의 경우일 것입니다.

예, 그렇습니다. 그러나 여전히 O(n) 이하입니다.

OCL이 여기에 도움이 될 것입니다. 물론 점근선은 그대로 유지됩니다. 그러나 속도는 백 배 증가하는 것이 가능합니다.