English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
인구 최적화 알고리즘

인구 최적화 알고리즘

MetaTrader 5 | 23 5월 2023, 09:29
399 0
Andrey Dik
Andrey Dik

"우주에서는 아무 일도 일어나지 않습니다. 
최대 또는 최소의 규칙이 나타나지 않는 경우"
레오나르드 오일러, 18세기

콘텐츠:

  1. 역사적 관점
  2. OA 분류
  3. 컨버전스 및 컨버전스의 속도. 컨버전스 안정성. 최적화 알고리즘 확장성
  4. 테스트 함수, 복잡한 OA 평가 기준 구축
  5. 테스트 스탠드
  6. RNG를 사용한 간단한 OA
  7. 결과

 

1. 역사적 관점

최적화 알고리즘은 함수 영역에서 함수가 최소값 또는 최대값에 도달하는 극한점을 찾도록 해주는 알고리즘입니다.

고대 그리스의 전문가들은 이미 이 사실을 알고 있었습니다:

- 주어진 둘레를 가진 모든 도형 중에서 원이 가장 넓은 면적을 가집니다.

- 주어진 변의 개수와 주어진 둘레를 가진 모든 다각형 중에서 정다각형의 면적이 가장 큽니다.

- 주어진 면적을 가진 모든 3차원 도형 중에서 구의 부피가 가장 큽니다.

변형적인 솔루션이 있는 첫 번째 문제도 비슷한 시기에 제안되었습니다. 전설에 따르면 기원전 825년경에 이런 일들을 논했습니다. 페니키아 도시 두로 왕의 여동생인 디도는 지중해 남부 해안으로 이주하여 현지 부족에게 황소 가죽으로 덮을 수 있는 땅을 구해 달라고 요청했습니다. 지역 주민들이 그녀에게 은신처를 제공했습니다. 지식이 많은 소녀는 그것을 좁은 띠로 자르고 묶어 밧줄을 만들었습니다. 이 밧줄로 연안의 영토를 덮고 그곳에 카르타고 도시를 세웠습니다.

문제는 주어진 길이의 닫힌 평면 곡선 중에서 최대 표면적을 커버하는 가장 효율적인 곡선을 찾는 것입니다. 이 문제에서 최대 면적은 반원으로 둘러싸인 면적으로 표시됩니다.

  didona1

 

 

이제 지중해의 고대 문화, 종교 재판소의 억압, 중세의 돌팔이 등 방대한 역사부터 자유로운 사상과 새로운 이론이 등장했던 르네상스 시대까지의 시기를 건너 뛰어봅시다. 1696년 6월, 요한 베르눌리는 <악타 에루디토리움> 독자들을 위해 다음과 같은 글을 발표합니다: "저 요한 베르누이는 세계에서 가장 뛰어난 수학자 여러분께 말씀드립니다. 정직하고 도전적인 문제보다 지적인 사람들에게 더 매력적인 것은 없으며 가능한 해결책은 명성을 얻고 영원한 기념물로 남을 것입니다. 파스칼, 페르마등이 제시한 예를 따라 저는 수학자들에게 그들의 방법과 지성의 힘을 시험할 수 있는 우리 시대 최고의 문제를 제시함으로써 전체 과학계의 찬사를 얻고자 합니다. 누군가가 제안된 문제의 해결책을 내게 알려주면 저는 그가 찬사를 받을 만한 사람이라고 공개적으로 선언할 것입니다"라고 말했습니다.

요한 베르눌리의 최단강하곡선 문제:

"수직 평면에 두 점 A와 B가 주어졌을 때, 중력만 작용하는 점이 A에서 시작하여 최단 시간 내에 B에 도달하는 곡선은 무엇일까요?". 놀랍게도 갈릴레오는 베르누이의 논문이 발표되기 훨씬 전인 1638년에 비슷한 문제를 해결하려고 시도했습니다. 답: 한 지점에서 다른 지점으로 가는 가장 빠른 경로는 보기와는 달리 최단 경로가 아닌 곡선입니다. - 각 지점에서 곡선의 곡률을 결정하는 사이클로이드입니다.

Brachistochrone9

 브라키스토크론 곡선

뉴턴의 해법(당시에는 밝혀지지 않은 해법)을 포함한 다른 모든 해법은 각 지점에서 기울기를 구하는 것을 기반으로 합니다. 아이작 뉴턴이 제안한 해법의 이면에는 변형적 계산의 기초가 되는 방법이 있습니다. 변형 계산 방법은 일반적으로 최적성 기준이 함수의 형태로 제시되고 해가 알려지지 않은 함수인 문제를 푸는 데 적용됩니다. 이러한 문제는 일반적으로 매개변수가 분산된 프로세스의 정적 최적화 또는 동적 최적화 문제에서 발생합니다.

변형 계산의 일차 극한 조건은 레오나드 오일러와 조셉 라그랑주에 의해 얻어졌습니다(오일러-라그랑주 방정식). 이 방정식은 최적화 문제에 널리 사용되며 정적 작용의 원리와 함께 역학에서 궤적 계산에 적용됩니다. 그러나 곧 이러한 방정식의 해가 항상 실제의 극한값을 제공하는 것은 아니라는 것이 분명해졌으며 이는 해를 구할 수 있는 충분한 조건이 필요하다는 것을 의미합니다. 이 작업은 계속되었고 레드레와 자코비에 의해 2차 극한 조건이 도출된 후 자코비의 학생인 헤세에 의해 2차 극한 조건이 도출되었습니다. 변동 계산에서 해가 존재하는지 여부에 대한 의문은 19세기 후반 바이어슈트라스에 의해 처음 제기되었습니다.

18세기 후반에 이르러 문제에 대한 최적의 해결책을 찾는 과정에서 최적화의 수학적 기초와 원리가 형성되었습니다. 안타깝게도 최적화 방법은 수학적 방법을 실제로 사용하려면 막대한 컴퓨팅 리소스가 필요했기 때문에 20세기 후반까지 많은 과학 및 기술 분야에서 거의 사용되지 않았습니다. 현대 사회에서 새로운 컴퓨팅 기술의 등장으로 마침내 복잡한 최적화 방법을 구현할 수 있게 되면서 다양한 알고리즘이 등장했습니다.

1980년대에는 자연에서 차용한 모델링의 결과인 확률적 최적화 알고리즘의 집중적인 개발이 시작되었습니다.

 

2. OA 분류

Class

 분류 AO

트레이딩 시스템을 최적화할 때 가장 흥미로운 것은 메타 휴리스틱 최적화 알고리즘입니다. 이 알고리즘은 최적화되는 함수의 공식에 대한 지식이 필요하지 않습니다. 글로벌 최적에 대한 수렴은 입증되지 않았지만 대부분의 경우 상당히 좋은 솔루션을 제공하며 이는 여러 문제에 대해 충분하다는 것이 실험적으로 입증되었습니다.

자연에서 차용한 모델로 많은 OA가 등장했습니다. 이러한 모델을 새 떼의 행동(입자 군집 알고리즘) 또는 개미 군집 행동의 원리(개미 알고리즘)와 같이 행동, 군집 또는 개체군이라고도 합니다.

모집단 알고리즘은 최적화 문제를 해결하기 위한 여러 옵션을 동시에 처리하며 고전적 알고리즘은 문제를 해결할 때 탐색 영역이 하나의 후보만 진화하는 모션 궤적 기반인데 이에 대한 대안을 제시합니다.

 

3. 컨버전스 및 컨버전스 속도. 컨버전스 안정성. 최적화 알고리즘 확장성

효율성, 속도, 수렴, 문제 조건 및 알고리즘 매개변수가 주는 영향은 각 알고리즘 구현과 최적화 문제의 각 클래스에 대해 주의 깊은 분석이 필요합니다.

3.1) 컨버전스 및 컨버전스 속도

 


반복 알고리즘의 속성으로, 목적 함수의 최적값에 도달하거나 유한한 단계로 목적 함수에 충분히 근접할 수 있습니다. 위 스크린샷의 오른쪽에는 계산된 테스트 함수에 의해 산출된 결과를 반복적으로 구성한 그래프가 있습니다. 이 두 이미지를 바탕으로 수렴은 함수 표면의 복잡성에 영향을 받는다는 결론을 내릴 수 있습니다. 복잡할수록 글로벌 극값을 찾기가 더 어려워집니다.

알고리즘의 수렴 속도는 최적화 알고리즘의 품질을 나타내는 가장 중요한 지표 중 하나이며 최적화 방법의 주요 특징 중 하나입니다. 한 알고리즘이 다른 알고리즘보다 빠르다는 것은 대부분의 경우 수렴 속도를 의미합니다. 결과가 극한값에 가까울수록 그리고 알고리즘을 더 빨리 반복할수록(즉 알고리즘을 더 일찍 반복할수록) 이 매개변수가 높아집니다. 메서드의 수렴 속도는 일반적으로 이차 방정식을 초과하지 않는다는 점에 유의하세요. 드물지만 이 방법은 입방체 수렴률을 가질 수 있습니다.

3.2) 컨버전스 안정성

결과를 얻기 위해 필요한 반복 횟수는 알고리즘 자체의 검색 능력 뿐만 아니라 학습 중인 함수에 따라 달라집니다. 함수가 표면의 높은 복잡성(날카로운 굴곡, 불연속성, 불연속성)을 특징으로 하는 경우 알고리즘이 불안정하여 허용 가능한 정확도를 전혀 제공하지 못할 수도 있습니다. 또한 수렴의 안정성은 여러 번의 연속적인 테스트를 수행했을 때 최적화 결과의 반복성으로 이해될 수 있습니다. 만약 결과 값의 편차가 크면 알고리즘의 안정성이 낮다는 뜻입니다.

3.3) 최적화 알고리즘 확장성


컨버전스7 convergence6

최적화 알고리즘의 확장성은 문제의 크기가 증가함에 따라 수렴을 유지할 수 있는 능력입니다. 즉 최적화 함수의 변수의 수가 증가함에 따라 수렴은 실제 목적에 허용되는 수준으로 유지되어야 합니다. 검색 최적화 모집단 알고리즘은 특히 고차원적이고 잘 정형화되지 않은 문제를 해결하는 경우 기존의 알고리즘과 비교할 때 분명한 이점이 있습니다. 이러한 조건에서 모집단 알고리즘은 최적화하려는 함수의 글로벌 극한값을 찾을 확률이 높습니다.

매끄럽고 단일 모달로 최적화된 함수의 경우 모집단 알고리즘은 일반적으로 기존의 그라데이션 방법보다 효율성이 떨어집니다. 또한 모집단 알고리즘의 단점은 대부분의 알고리즘에서 상당히 다양한 자유도(튜닝 매개변수의 수)에 대한 효율성의 의존도가 높다는 점입니다.

 

4. 테스트 함수, 복잡한 OA 평가 기준 구축

최적화 알고리즘을 테스트하고 비교하는 데 일반적으로 인정되는 방법론은 없습니다. 그러나 연구자들이 여러 해에 걸쳐 제안한 많은 테스트 함수들이 있습니다. 여기서는 제가 첫 번째 글을 올리기 전에 만든 함수를 사용하겠습니다. 이 함수들은 터미널의 폴더 \MQL5\Experts\Exples\Math 3D\Functions.mqh 및 \MQL5\Experts\Exples\Math 3D Morpher\Functions.mqh에서 찾을 수 있습니다. 이들 함수들은 OA 테스트의 모든 복잡성의 기준을 충족합니다. 또한 포레스트 및 메가시티 함수가 개발되면서 OA 검색 기능에 대한 보다 포괄적인 학습이 가능해졌습니다.

스킨 테스트 함수:


이 함수는 도메인 전체에 걸쳐 매끄럽고 로컬 최대/최소값이 크게 다르지 않아(수렴 함정) 전역 극값에 도달하지 못하는 알고리즘이 멈추게 되는 원인이 됩니다.

피부

스킨

포레스트 테스트 함수:


이 함수는 해당 지점에서 미분이 없는 여러개의 최대값을 나타냅니다. 따라서 학습 중인 함수의 평활성에 따라 견고성이 결정되는 최적화 알고리즘의 경우 어려울 수 있습니다.

숲

포레스트

메가시티 테스트 함수:

'영역'을 형성하는 불연속 함수(변수를 변경해도 함수 값에 큰 변화가 없는 경우). 따라서 그라데이션이 필요한 알고리즘에는 어려움이 있습니다.

차이나타운

메가시티



5. 테스트 스탠드

최적화 알고리즘을 포괄적으로 비교하기 위해 일반적인 평가 기준을 만들려고 시도했습니다. 이 아이디어의 복잡성은 알고리즘을 비교하는 방법이 명확하지 않다는 사실에 있습니다. 왜냐하면 각각의 알고리즘이 해당 문제 클래스에 대해 고유한 방식으로 우수하기 때문입니다. 예를 들어 어떤 알고리즘은 빠르게 수렴하지만 확장이 잘 되지 않는 반면 다른 알고리즘은 확장은 잘 되지만 불안정합니다. 

  •   컨버전스: 컨버전스를 연구하기 위해 우리는 위에 제시된 세 가지 함수를 사용합니다. 최대값과 최소값은 0.0(최악의 결과)에서 1.0(최상의 결과)까지의 범위로 변환되고 이를 통해 우리는 다양한 유형의 문제에 대한 수렴을 보장하는 알고리즘의 능력을 평가할 수 있습니다.
  •   컨버전스 비율: 알고리즘의 최상의 결과는 테스트된 함수의 1000번째 및 10,000번째 실행에서 측정됩니다. 따라서 우리는 OA가 얼마나 빠르게 수렴하는지 확인할 수 있습니다. 수렴이 빠를수록 수렴 그래프가 최대치를 향해 더 많은 곡선을 그리게 됩니다.
  •   안정성: 각 함수에 대해 5번의 최적화를 실행하고 0.0에서 1.0 범위의 평균값을 계산합니다. 일부 알고리즘의 결과는 실행할 때마다 크게 달라질 수 있으므로 이 작업이 필요합니다. 다섯 가지 테스트 각각에서 수렴이 높을수록 안정성이 높다는 뜻입니다.
  •   확장성: 일부 OA는 변수가 2개 이하로 적은 함수에 대해서만 실질적인 결과를 보여줄 수 있습니다. 일부는 둘 이상의 변수로 작업할 수조차 없습니다. 또한 수천 개의 변수가 있는 함수로 작업할 수 있는 알고리즘도 있습니다. 이러한 최적화 알고리즘은 신경망의 OA로 사용할 수 있습니다.  

테스트 함수 사용의 편의를 위해 나중에 해당 테스트 함수의 자식 클래스의 객체를 선택할 수 있는 부모 클래스와 열거자를 작성해 보겠습니다:

//——————————————————————————————————————————————————————————————————————————————
class C_Function
{
  public: //====================================================================
  double CalcFunc (double &args [], //function arguments
                   int     amount)  //amount of runs functions
  {
    double x, y;
    double sum = 0.0;
    for (int i = 0; i < amount; i++)
    {
      x = args [i * 2];
      y = args [i * 2 + 1];

      sum += Core (x, y);
    }

    sum /= amount;

    return sum;
  }
  double GetMinArg () { return minArg;}
  double GetMaxArg () { return maxArg;}
  double GetMinFun () { return minFun;}
  double GetMaxFun () { return maxFun;}
  string GetNamFun () { return fuName;}

  protected: //==================================================================
  void SetMinArg (double min) { minArg = min;}
  void SetMaxArg (double max) { maxArg = max;}
  void SetMinFun (double min) { minFun = min;}
  void SetMaxFun (double max) { maxFun = max;}
  void SetNamFun (string nam) { fuName = nam;}

  private: //====================================================================
  virtual double Core (double x, double y) { return 0.0;}
  
  double minArg;
  double maxArg;
  double minFun;
  double maxFun;
  string fuName;
};
//——————————————————————————————————————————————————————————————————————————————

//——————————————————————————————————————————————————————————————————————————————
enum EFunc
{
  Skin,
  Forest,
  Megacity,
  
};
C_Function *SelectFunction (EFunc f)
{
  C_Function *func;
  switch (f)
  {
    case  Skin:
      func = new C_Skin (); return (GetPointer (func));
    case  Forest:
      func = new C_Forest (); return (GetPointer (func));
    case  Megacity:
      func = new C_Megacity (); return (GetPointer (func));
    
    default:
      func = new C_Skin (); return (GetPointer (func));
  }
}
//——————————————————————————————————————————————————————————————————————————————

         

그러면 자식 클래스는 다음과 같이 표시됩니다:

//——————————————————————————————————————————————————————————————————————————————
class C_Skin : public C_Function
{
  public: //===================================================================
  C_Skin ()
  {
    SetNamFun ("Skin");
    SetMinArg (-5.0);
    SetMaxArg (5.0);
    SetMinFun (-4.3182);  //[x=3.07021;y=3.315935] 1 point
    SetMaxFun (14.0606);  //[x=-3.315699;y=-3.072485] 1 point
  }

  private: //===================================================================
  double Core (double x, double y)
  {
    double a1=2*x*x;
    double a2=2*y*y;
    double b1=MathCos(a1)-1.1;
    b1=b1*b1;
    double c1=MathSin(0.5*x)-1.2;
    c1=c1*c1;
    double d1=MathCos(a2)-1.1;
    d1=d1*d1;
    double e1=MathSin(0.5*y)-1.2;
    e1=e1*e1;

   double res=b1+c1-d1+e1;
   return(res);
  }
};
//——————————————————————————————————————————————————————————————————————————————

//——————————————————————————————————————————————————————————————————————————————
class C_Forest : public C_Function
{
  public: //===================================================================
  C_Forest ()
  {
    SetNamFun ("Forest");
    SetMinArg (-50.0);
    SetMaxArg (-18.0);
    SetMinFun (0.0);             //many points
    SetMaxFun (15.95123239744);  //[x=-25.132741228718345;y=-32.55751918948773] 1 point
  }

  private: //===================================================================
  double Core (double x, double y)
  {
    double a = MathSin (MathSqrt (MathAbs (x - 1.13) + MathAbs (y - 2.0)));
    double b = MathCos (MathSqrt (MathAbs (MathSin (x))) + MathSqrt (MathAbs (MathSin (y - 2.0))));
    double f = a + b;

    double res = MathPow (f, 4);
    if (res < 0.0) res = 0.0;
    return (res);
  }
};
//——————————————————————————————————————————————————————————————————————————————

//——————————————————————————————————————————————————————————————————————————————
class C_Megacity : public C_Function
{
  public: //===================================================================
  C_Megacity ()
  {
    SetNamFun ("Megacity");
    SetMinArg (-15.0);
    SetMaxArg (15.0);
    SetMinFun (0.0);   //many points
    SetMaxFun (15.0);  //[x=`3.16;y=1.990] 1 point
  }

  private: //===================================================================
  double Core (double x, double y)
  {
    double a = MathSin (MathSqrt (MathAbs (x - 1.13) + MathAbs (y - 2.0)));
    double b = MathCos (MathSqrt (MathAbs (MathSin (x))) + MathSqrt (MathAbs (MathSin (y - 2.0))));
    double f = a + b;

    double res = floor (MathPow (f, 4));
    return (res);
  }
};
//——————————————————————————————————————————————————————————————————————————————


테스트 스탠드에서 얻은 OA 테스트 결과의 유효성을 확인하기 위해 단계별 크기로 X 및 Y 함수를 열거하는 스크립트를 사용할 수 있습니다. 단계 크기가 너무 작으면 계산이 너무 오래 걸리므로 단계를 선택할 때 주의해야 합니다. 예를 들어 스킨 함수의 인수 범위는 [-5;5]입니다. X축의 스텝이 0.00001이면 (5-(-5))/0.00001=1'000'000(백만) 단계가 됩니다, Y축을 따라 각각 같은 수이므로 각 지점의 값을 계산하는 테스트 함수의 총 실행 횟수는 1'000'000 х 1'000'000= 1'000'000'000'000(10^12, 조)가 됩니다.

단 10,000 단계내로 최대값을 찾아야하기 때문에 OA 작업이 얼마나 어려운지 이해할 필요가 있습니다 (대략이 값은 MetaTrader 5 옵티마이저에서 사용됨). 이 계산은 두 개의 변수가 있는 함수에 대한 계산이며 테스트에 사용되는 최대 변수 수는 1000개라는 점에 유의하세요.

이 글과 다음에 계속되는 글의 알고리즘 테스트에서는 0.0! 단계 또는 해당 OA의 특정한 구현에 대해 가능한 최소 단계를 사용합니다.

//——————————————————————————————————————————————————————————————————————————————
input EFunc  Function          = Skin;
input double Step              = 0.01;

//——————————————————————————————————————————————————————————————————————————————
void OnStart ()
{
  C_Function *TestFunc = SelectFunction (Function);

  double argMin = TestFunc.GetMinArg ();
  double argMax = TestFunc.GetMaxArg ();

  double maxFuncValue = 0;
  double xMaxFunc     = 0.0;
  double yMaxFunc     = 0.0;
  
  double minFuncValue = 0;
  double xMinFunc     = 0.0;
  double yMinFunc     = 0.0;
  
  double fValue       = 0.0;

  double arg [2];

  arg [0] = argMin;
  arg [1] = argMin;

  long cnt = 0;

  while (arg [1] <= argMax && !IsStopped ())
  {
    arg [0] = argMin;

    while (arg [0] <= argMax && !IsStopped ())
    {
      cnt++;

      fValue = TestFunc.CalcFunc (arg, 1);

      if (fValue > maxFuncValue)
      {
        maxFuncValue = fValue;
        xMaxFunc = arg [0];
        yMaxFunc = arg [1];
      }
      if (fValue < minFuncValue)
      {
        minFuncValue = fValue;
        xMinFunc = arg [0];
        yMinFunc = arg [1];
      }

      arg [0] += Step;
      
      if (cnt == 1)
      {
       maxFuncValue = fValue;
       minFuncValue = fValue;
      }
    }

    arg [1] += Step;
  }
  
  Print ("======", TestFunc.GetNamFun (), ", launch counter: ", cnt);
  Print ("MaxFuncValue: ", DoubleToString (maxFuncValue, 16), " X: ", DoubleToString (xMaxFunc, 16), " Y: ", DoubleToString (yMaxFunc, 16));
  Print ("MinFuncValue: ", DoubleToString (minFuncValue, 16), " X: ", DoubleToString (xMinFunc, 16), " Y: ", DoubleToString (yMinFunc, 16));
         
  delete TestFunc;
}

//——————————————————————————————————————————————————————————————————————————————


테스트 스탠드를 작성해 보겠습니다:

#include <Canvas\Canvas.mqh>
#include <\Math\Functions.mqh>
#include "AO_RND.mqh"

//——————————————————————————————————————————————————————————————————————————————
input int    Population_P       = 50;
input double ArgumentStep_P     = 0.0;
input int    Test1FuncRuns_P    = 1;
input int    Test2FuncRuns_P    = 20;
input int    Test3FuncRuns_P    = 500;
input int    Measur1FuncValue_P = 1000;
input int    Measur2FuncValue_P = 10000;
input int    NumberRepetTest_P  = 5;
input int    RenderSleepMsc_P   = 0;

//——————————————————————————————————————————————————————————————————————————————
int WidthMonitor = 750;  //monitor screen width
int HeighMonitor = 375;  //monitor screen height

int WidthScrFunc = 375 - 2;  //test function screen width
int HeighScrFunc = 375 - 2;  //test function screen height

CCanvas Canvas;  //drawing table
C_AO_RND AO;     //AO object

C_Skin       SkiF;
C_Forest     ForF;
C_Megacity  ChiF;

struct S_CLR
{
    color clr [];
};

S_CLR FunctScrin []; //two-dimensional matrix of colors
double ScoreAll = 0.0;

//——————————————————————————————————————————————————————————————————————————————
void OnStart ()
{
  //creating a table -----------------------------------------------------------
  string canvasName = "AO_Test_Func_Canvas";
  if (!Canvas.CreateBitmapLabel (canvasName, 5, 30, WidthMonitor, HeighMonitor, COLOR_FORMAT_ARGB_RAW))
  {
    Print ("Error creating Canvas: ", GetLastError ());
    return;
  }
  ObjectSetInteger (0, canvasName, OBJPROP_HIDDEN, false);
  ObjectSetInteger (0, canvasName, OBJPROP_SELECTABLE, true);

  ArrayResize (FunctScrin, HeighScrFunc);
  for (int i = 0; i < HeighScrFunc; i++)
  {
    ArrayResize (FunctScrin [i].clr, HeighScrFunc);
  }
  
  //============================================================================
  //Test Skin###################################################################
  Print ("=============================");
  CanvasErase ();
  FuncTests (SkiF, Test1FuncRuns_P, SkiF.GetMinFun (), SkiF.GetMaxFun (), -3.315699, -3.072485, clrLime);
  FuncTests (SkiF, Test2FuncRuns_P, SkiF.GetMinFun (), SkiF.GetMaxFun (), -3.315699, -3.072485, clrAqua);
  FuncTests (SkiF, Test3FuncRuns_P, SkiF.GetMinFun (), SkiF.GetMaxFun (), -3.315699, -3.072485, clrOrangeRed);
  
  //Test Forest#################################################################
  Print ("=============================");
  CanvasErase ();
  FuncTests (ForF, Test1FuncRuns_P, ForF.GetMinFun (), ForF.GetMaxFun (), -25.132741228718345, -32.55751918948773, clrLime);
  FuncTests (ForF, Test2FuncRuns_P, ForF.GetMinFun (), ForF.GetMaxFun (), -25.132741228718345, -32.55751918948773, clrAqua);
  FuncTests (ForF, Test3FuncRuns_P, ForF.GetMinFun (), ForF.GetMaxFun (), -25.132741228718345, -32.55751918948773, clrOrangeRed);
  
  //Test Megacity#############################################################
  Print ("=============================");
  CanvasErase ();
  FuncTests (ChiF, Test1FuncRuns_P, ChiF.GetMinFun (), ChiF.GetMaxFun (), 3.16, 1.990, clrLime);
  FuncTests (ChiF, Test2FuncRuns_P, ChiF.GetMinFun (), ChiF.GetMaxFun (), 3.16, 1.990, clrAqua);
  FuncTests (ChiF, Test3FuncRuns_P, ChiF.GetMinFun (), ChiF.GetMaxFun (), 3.16, 1.990, clrOrangeRed);
  
  Print ("All score for C_AO_RND: ", ScoreAll / 18.0);
}
//——————————————————————————————————————————————————————————————————————————————

void CanvasErase ()
{
  Canvas.Erase (XRGB (0, 0, 0));
  Canvas.FillRectangle (1,                1, HeighMonitor - 2, HeighMonitor - 2, COLOR2RGB (clrWhite));
  Canvas.FillRectangle (HeighMonitor + 1, 1, WidthMonitor - 2, HeighMonitor - 2, COLOR2RGB (clrWhite));
}

//——————————————————————————————————————————————————————————————————————————————
void FuncTests (C_Function &f,
                int        funcCount,
                double     minFuncVal,
                double     maxFuncVal,
                double     xBest,
                double     yBest,
                color      clrConv)
{
  DrawFunctionGraph (f.GetMinArg (), f.GetMaxArg (), minFuncVal, maxFuncVal, f);
  SendGraphToCanvas (1, 1);
  int x = (int)Scale (xBest, f.GetMinArg (), f.GetMaxArg (), 0, WidthScrFunc - 1, false);
  int y = (int)Scale (yBest, f.GetMinArg (), f.GetMaxArg (), 0, HeighScrFunc - 1, false);
  Canvas.Circle (x + 1, y + 1, 10, COLOR2RGB (clrBlack));
  Canvas.Circle (x + 1, y + 1, 11, COLOR2RGB (clrBlack));
  Canvas.Update ();
  Sleep (1000);

  int xConv = 0.0;
  int yConv = 0.0;

  int EpochCmidl = 0;
  int EpochCount = 0;

  double aveMid = 0.0;
  double aveEnd = 0.0;

  //----------------------------------------------------------------------------
  for (int test = 0; test < NumberRepetTest_P; test++)
  {
    InitAO (funcCount * 2, f.GetMaxArg (), f.GetMinArg (), ArgumentStep_P);

    EpochCmidl = Measur1FuncValue_P / (ArraySize (AO.S_Colony));
    EpochCount = Measur2FuncValue_P / (ArraySize (AO.S_Colony));

    // Optimization-------------------------------------------------------------
    AO.F_EpochReset ();


    for (int epochCNT = 1; epochCNT <= EpochCount && !IsStopped (); epochCNT++)
    {
      AO.F_Preparation ();

      for (int set = 0; set < ArraySize (AO.S_Colony); set++)
      {
        AO.A_FFcol [set] = f.CalcFunc (AO.S_Colony [set].args, funcCount);
      }

      AO.F_Sorting ();

      if (epochCNT == EpochCmidl) aveMid += AO.A_FFpop [0];

      SendGraphToCanvas  (1, 1);

      //draw a population on canvas
      for (int i = 0; i < ArraySize (AO.S_Population); i++)
      {
        if (i > 0) PointDr (AO.S_Population [i].args, f.GetMinArg (), f.GetMaxArg (), clrWhite, 1, 1, funcCount);
      }
      PointDr (AO.S_Population [0].args, f.GetMinArg (), f.GetMaxArg (), clrBlack, 1, 1, funcCount);

      Canvas.Circle (x + 1, y + 1, 10, COLOR2RGB (clrBlack));
      Canvas.Circle (x + 1, y + 1, 11, COLOR2RGB (clrBlack));

      xConv = (int)Scale (epochCNT,       1,          EpochCount, 2, WidthScrFunc - 2, false);
      yConv = (int)Scale (AO.A_FFpop [0], minFuncVal, maxFuncVal, 1, HeighScrFunc - 2, true);

      Canvas.FillCircle (xConv + HeighMonitor + 1, yConv + 1, 1, COLOR2RGB (clrConv));

      Canvas.Update ();
      Sleep (RenderSleepMsc_P);
    }

    aveEnd += AO.A_FFpop [0];

    Sleep (1000);
  }

  aveMid /= (double)NumberRepetTest_P;
  aveEnd /= (double)NumberRepetTest_P;

  double score1 = Scale (aveMid, minFuncVal, maxFuncVal, 0.0, 1.0, false);
  double score2 = Scale (aveEnd, minFuncVal, maxFuncVal, 0.0, 1.0, false);
  
  ScoreAll += score1 + score2;

  Print (funcCount, " ", f.GetNamFun (), "'s; Func runs ", Measur1FuncValue_P, " result: ", aveMid, "; Func runs ", Measur2FuncValue_P, " result: ", aveEnd);
  Print ("Score1: ", DoubleToString (score1, 5), "; Score2: ", DoubleToString (score2, 5));
}
//——————————————————————————————————————————————————————————————————————————————

//——————————————————————————————————————————————————————————————————————————————
void InitAO (const int    params,  //amount of the optimized arguments
             const double max,     //maximum of the optimized argument
             const double min,     //minimum of the optimized argument
             const double step)    //step of the optimized argument
{
  AO.F_Init (params, Population_P);
  for (int idx = 0; idx < params; idx++)
  {
    AO.A_RangeMax  [idx] = max;
    AO.A_RangeMin  [idx] = min;
    AO.A_RangeStep [idx] = step;
  }
}
//——————————————————————————————————————————————————————————————————————————————

//——————————————————————————————————————————————————————————————————————————————
void PointDr (double &args [], double Min, double Max, color clr, int shiftX, int shiftY, int count)
{
  double x = 0.0;
  double y = 0.0;
  
  double xAve = 0.0;
  double yAve = 0.0;
  
  int width  = 0;
  int height = 0;
  
  color clrF = clrNONE;
  
  for (int i = 0; i < count; i++)
  {
    xAve += args [i * 2];
    yAve += args [i * 2 + 1];
       
    x = args [i * 2];
    y = args [i * 2 + 1];
    
    width  = (int)Scale (x, Min, Max, 0, WidthScrFunc - 1, false);
    height = (int)Scale (y, Min, Max, 0, HeighScrFunc - 1, false);
    
    clrF = DoubleToColor (i, 0, count - 1, 0, 360);
    Canvas.FillCircle (width + shiftX, height + shiftY, 1, COLOR2RGB (clrF));
  }
  
  xAve /= (double)count;
  yAve /= (double)count;

  width  = (int)Scale (xAve, Min, Max, 0, WidthScrFunc - 1, false);
  height = (int)Scale (yAve, Min, Max, 0, HeighScrFunc - 1, false);

  Canvas.FillCircle (width + shiftX, height + shiftY, 3, COLOR2RGB (clrBlack));
  Canvas.FillCircle (width + shiftX, height + shiftY, 2, COLOR2RGB (clr));
}
//——————————————————————————————————————————————————————————————————————————————

//——————————————————————————————————————————————————————————————————————————————
void SendGraphToCanvas (int shiftX, int shiftY)
{
  for (int w = 0; w < HeighScrFunc; w++)
  {
    for (int h = 0; h < HeighScrFunc; h++)
    {
      Canvas.PixelSet (w + shiftX, h + shiftY, COLOR2RGB (FunctScrin [w].clr [h]));
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

//——————————————————————————————————————————————————————————————————————————————
void DrawFunctionGraph (double     min,
                        double     max,
                        double     fMin,
                        double     fMax,
                        C_Function &f)
{
  double ar [2];
  double fV;

  for (int w = 0; w < HeighScrFunc; w++)
  {
    ar [0] = Scale (w, 0, HeighScrFunc, min, max, false);
    for (int h = 0; h < HeighScrFunc; h++)
    {
      ar [1] = Scale (h, 0, HeighScrFunc, min, max, false);
      fV = f.CalcFunc (ar, 1);
      FunctScrin [w].clr [h] = DoubleToColor (fV, fMin, fMax, 0, 250);
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

//——————————————————————————————————————————————————————————————————————————————
//Scaling a number from a range to a specified range
double Scale (double In, double InMIN, double InMAX, double OutMIN, double OutMAX, bool Revers = false)
{
  if (OutMIN == OutMAX) return (OutMIN);
  if (InMIN == InMAX) return ((OutMIN + OutMAX) / 2.0);
  else
  {
    if (Revers)
    {
      if (In < InMIN) return (OutMAX);
      if (In > InMAX) return (OutMIN);
      return (((InMAX - In) * (OutMAX - OutMIN) / (InMAX - InMIN)) + OutMIN);
    }
    else
    {
      if (In < InMIN) return (OutMIN);
      if (In > InMAX) return (OutMAX);
      return (((In - InMIN) * (OutMAX - OutMIN) / (InMAX - InMIN)) + OutMIN);
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

//——————————————————————————————————————————————————————————————————————————————
color DoubleToColor (const double in,    //input value
                     const double inMin, //minimum of input values
                     const double inMax, //maximum of input values
                     const int    loH,   //lower bound of HSL range values
                     const int    upH)   //upper bound of HSL range values
{
  int h = (int)Scale (in, inMin, inMax, loH, upH, true);
  return HSLtoRGB (h, 1.0, 0.5);
}
//——————————————————————————————————————————————————————————————————————————————

//——————————————————————————————————————————————————————————————————————————————
color HSLtoRGB (const int    h, //0   ... 360
                const double s, //0.0 ... 1.0
                const double l) //0.0 ... 1.0
{
  int r;
  int g;
  int b;
  if (s == 0.0)
  {
    r = g = b = (unsigned char)(l * 255);
    return StringToColor ((string)r + "," + (string)g + "," + (string)b);
  }
  else
  {
    double v1, v2;
    double hue = (double)h / 360.0;
    v2 = (l < 0.5) ? (l * (1.0 + s)) : ((l + s) - (l * s));
    v1 = 2.0 * l - v2;
    r = (unsigned char)(255 * HueToRGB (v1, v2, hue + (1.0 / 3.0)));
    g = (unsigned char)(255 * HueToRGB (v1, v2, hue));
    b = (unsigned char)(255 * HueToRGB (v1, v2, hue - (1.0 / 3.0)));
    return StringToColor ((string)r + "," + (string)g + "," + (string)b);
  }
}
//——————————————————————————————————————————————————————————————————————————————
//——————————————————————————————————————————————————————————————————————————————
double HueToRGB (double v1, double v2, double vH)
{
  if (vH < 0) vH += 1;
  if (vH > 1) vH -= 1;
  if ((6 * vH) < 1) return (v1 + (v2 - v1) * 6 * vH);
  if ((2 * vH) < 1) return v2;
  if ((3 * vH) < 2) return (v1 + (v2 - v1) * ((2.0f / 3) - vH) * 6);
  return v1;
}
//——————————————————————————————————————————————————————————————————————————————

범위의 double 수를 색상으로 변환하기 위해 HSL을 RGB로 변환하는 알고리즘이 사용되었습니다(MetaTrader 5의 색상 시스템).

테스트 스탠드는 그래프에 이미지를 표시합니다. 반으로 나뉩니다.

  • 테스트 함수는 왼쪽에 표시됩니다. 3차원 그래프는 평면에 투영되며 빨간색은 최대값을, 파란색은 최소값을 의미합니다. 모집단에서 점의 위치를 표시하고(색상은 변수 수가 40과 1000인 테스트 함수의 서수에 해당하며 두 변수가 있는 함수의 경우 색상이 지정되지 않음), 좌표가 평균화된 점은 흰색으로 표시하고 가장 좋은 점은 검은색으로 표시합니다. 
  • 수렴 그래프는 오른쪽에 표시되며 변수가 2개인 테스트는 녹색, 40개인 테스트는 파란색, 1000개의 변수가 있는 테스트는 빨간색으로 표시됩니다. 각 테스트는 5회 수행됩니다(각 색상의 수렴 그래프 5개). 여기서 변수의 수가 증가함에 따라 OA의 수렴이 얼마나 저하되는지 관찰할 수 있습니다.


6. RNG를 사용한 간단한 OA

가장 간단한 검색 전략을 테스트 예제로 구현해 보겠습니다. 실제로 사용하려는 것은 아니지만 어떤 식으로 든 최적화 알고리즘을 비교할 수 있는 기준이 될 것입니다. 이 전략은 모집단에서 무작위로 선택된 부모 집합에서 변수를 복사하거나 최소/최대 범위에서 변수를 생성하는 등 50/50 무작위 선택으로 새로운 함수 변수의 집합을 생성합니다. 테스트 함수의 값을 받은 후 그 결과로 생성된 새로운 변수의 집합이 모집단의 후반부에 복사되고 정렬됩니다. 따라서 새로운 세트는 지속적으로 모집단의 절반을 대체하는 반면 최고의 세트는 다른 쪽에 집중되어 있습니다.

아래는 RNG를 기반으로 한 OA 코드입니다:

//+————————————————————————————————————————————————————————————————————————————+
class C_AO_RND
{
  public: //||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

  struct ArrColony
  {
      double args [];
  };

  //----------------------------------------------------------------------------
  double    A_RangeStep []; //Step ranges of genes
  double    A_RangeMin  []; //Min ranges of genes
  double    A_RangeMax  []; //Max ranges of genes

  ArrColony S_Population []; //Population
  ArrColony S_Colony     []; //Colony

  double    A_FFpop [];      //Values of fitness of individuals in population
  double    A_FFcol [];      //Values of fitness of individuals in colony

  //----------------------------------------------------------------------------
  // Initialization of algorithm
  void F_Init (int argCount,       //Number of arguments

               int populationSize) //Population size
  {
    MathSrand ((int)GetMicrosecondCount ()); //reset of the generator

    p_argCount  = argCount;
    p_sizeOfPop = populationSize;
    p_sizeOfCol = populationSize / 2;

    p_dwelling  = false;

    f_arrayInitResize (A_RangeStep, argCount, 0.0);
    f_arrayInitResize (A_RangeMin,  argCount, 0.0);
    f_arrayInitResize (A_RangeMax,  argCount, 0.0);

    ArrayResize (S_Population, p_sizeOfPop);
    ArrayResize (s_populTemp, p_sizeOfPop);
    for (int i = 0; i < p_sizeOfPop; i++)
    {
      f_arrayInitResize (S_Population [i].args, argCount, 0.0);
      f_arrayInitResize (s_populTemp [i].args, argCount, 0.0);
    }

    ArrayResize (S_Colony, p_sizeOfCol);
    for (int i = 0; i < p_sizeOfCol; i++)
    {
      f_arrayInitResize (S_Colony [i].args, argCount, 0.0);
    }

    f_arrayInitResize (A_FFpop, p_sizeOfPop, -DBL_MAX);
    f_arrayInitResize (A_FFcol, p_sizeOfCol, -DBL_MAX);

    f_arrayInitResize (a_indexes, p_sizeOfPop, 0);
    f_arrayInitResize (a_valueOnIndexes, p_sizeOfPop, 0.0);
  }

  //----------------------------------------------------------------------------
  void F_EpochReset ()   //Reset of epoch, allows to begin evolution again without initial initialization of variables
  {
    p_dwelling = false;
    ArrayInitialize (A_FFpop, -DBL_MAX);
    ArrayInitialize (A_FFcol, -DBL_MAX);
  }
  //----------------------------------------------------------------------------
  void F_Preparation ();  //Preparation
  //----------------------------------------------------------------------------
  void F_Sorting ();      //The settling of a colony in population and the subsequent sorting of population

  private: //|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
  //----------------------------------------------------------------------------
  void F_PopulSorting ();

  //----------------------------------------------------------------------------
  ArrColony          s_populTemp      []; //Temporal population
  int                a_indexes        []; //Indexes of chromosomes
  double             a_valueOnIndexes []; //VFF of the appropriate indexes of chromosomes

  //----------------------------------------------------------------------------
  template <typename T1>
  void f_arrayInitResize (T1 &arr [], const int size, const T1 value)
  {
    ArrayResize     (arr, size);
    ArrayInitialize (arr, value);
  }

  //----------------------------------------------------------------------------
  double f_seInDiSp         (double In, double InMin, double InMax, double step);
  double f_RNDfromCI        (double min, double max);
  double f_scale            (double In, double InMIN, double InMAX, double OutMIN, double OutMAX);

  //---Constants----------------------------------------------------------------
  int  p_argCount;   //Quantity of arguments in a set of arguments
  int  p_sizeOfCol;  //Quantity of set in a colony
  int  p_sizeOfPop;  //Quantity of set in population
  bool p_dwelling;   //Flag of the first settling of a colony in population
};
//——————————————————————————————————————————————————————————————————————————————

//——————————————————————————————————————————————————————————————————————————————
void C_AO_RND::F_Preparation ()
{
  //if starts of algorithm weren't yet - generate a colony with random arguments
  if (!p_dwelling)
  {
    for (int person = 0; person < p_sizeOfCol; person++)
    {
      for (int arg = 0; arg < p_argCount; arg++)
      {
        S_Colony [person].args [arg] = f_seInDiSp (f_RNDfromCI (A_RangeMin [arg], A_RangeMax [arg]),
                                                    A_RangeMin  [arg],
                                                    A_RangeMax  [arg],
                                                    A_RangeStep [arg]);
      }
    }

    p_dwelling = true;
  }
  //generation of a colony using with copying arguments from parent sets--------
  else
  {
    int parentAdress = 0;
    double rnd       = 0.0;
    double argVal    = 0.0;

    for (int setArg = 0; setArg < p_sizeOfCol; setArg++)
    {
      //get a random address of the parent set
      parentAdress = (int)f_RNDfromCI (0, p_sizeOfPop - 1);

      for (int arg = 0; arg < p_argCount; arg++)
      {
        if (A_RangeMin [arg] == A_RangeMax [arg]) continue;

        rnd = f_RNDfromCI (0.0, 1.0);

        if (rnd < 0.5)
        {
          S_Colony [setArg].args [arg] = S_Population [parentAdress].args [arg];
        }
        else
        {
          argVal = f_RNDfromCI (A_RangeMin [arg], A_RangeMax [arg]);
          argVal = f_seInDiSp (argVal, A_RangeMin [arg], A_RangeMax [arg], A_RangeStep [arg]);

          S_Colony [setArg].args [arg] = argVal;
        }
      }
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

//——————————————————————————————————————————————————————————————————————————————
void C_AO_RND::F_Sorting ()
{
  for (int person = 0; person < p_sizeOfCol; person++)
  {
    ArrayCopy (S_Population [person + p_sizeOfCol].args, S_Colony [person].args, 0, 0, WHOLE_ARRAY);
  }
  ArrayCopy (A_FFpop, A_FFcol, p_sizeOfCol, 0, WHOLE_ARRAY);

  F_PopulSorting ();
}
//——————————————————————————————————————————————————————————————————————————————

//——————————————————————————————————————————————————————————————————————————————
// Ranging of population.
void C_AO_RND::F_PopulSorting ()
{
  //----------------------------------------------------------------------------
  int   cnt = 1, i = 0, u = 0;
  int   t0 = 0;
  double t1 = 0.0;
  //----------------------------------------------------------------------------

  // We will put indexes in the temporary array
  for (i = 0; i < p_sizeOfPop; i++)
  {
    a_indexes [i] = i;
    a_valueOnIndexes [i] = A_FFpop [i];
  }
  while (cnt > 0)
  {
    cnt = 0;
    for (i = 0; i < p_sizeOfPop - 1; i++)
    {
      if (a_valueOnIndexes [i] < a_valueOnIndexes [i + 1])
      {
        //-----------------------
        t0 = a_indexes [i + 1];
        t1 = a_valueOnIndexes [i + 1];
        a_indexes [i + 1] = a_indexes [i];
        a_valueOnIndexes [i + 1] = a_valueOnIndexes [i];
        a_indexes [i] = t0;
        a_valueOnIndexes [i] = t1;
        //-----------------------
        cnt++;
      }
    }
  }

  // On the received indexes create the sorted temporary population
  for (u = 0; u < p_sizeOfPop; u++) ArrayCopy (s_populTemp [u].args, S_Population [a_indexes [u]].args, 0, 0, WHOLE_ARRAY);

  // Copy the sorted array back
  for (u = 0; u < p_sizeOfPop; u++) ArrayCopy (S_Population [u].args, s_populTemp [u].args, 0, 0, WHOLE_ARRAY);

  ArrayCopy (A_FFpop, a_valueOnIndexes, 0, 0, WHOLE_ARRAY);
}
//——————————————————————————————————————————————————————————————————————————————

//——————————————————————————————————————————————————————————————————————————————
// Choice in discrete space
double C_AO_RND::f_seInDiSp (double in, double inMin, double inMax, double step)
{
  if (in <= inMin) return (inMin);
  if (in >= inMax) return (inMax);
  if (step == 0.0) return (in);
  else return (inMin + step * (double)MathRound ((in - inMin) / step));
}
//——————————————————————————————————————————————————————————————————————————————

//——————————————————————————————————————————————————————————————————————————————
// Random number generator in the custom interval.
double C_AO_RND::f_RNDfromCI (double min, double max)
{
  if (min == max) return (min);
  double Min, Max;
  if (min > max)
  {
    Min = max;
    Max = min;
  }
  else
  {
    Min = min;
    Max = max;
  }
  return (double(Min + ((Max - Min) * (double)MathRand () / 32767.0)));
}
//——————————————————————————————————————————————————————————————————————————————

//——————————————————————————————————————————————————————————————————————————————
double C_AO_RND::f_scale (double In, double InMIN, double InMAX, double OutMIN, double OutMAX)
{
  if (OutMIN == OutMAX) return (OutMIN);
  if (InMIN == InMAX) return (double((OutMIN + OutMAX) / 2.0));
  else
  {
    if (In < InMIN) return (OutMIN);
    if (In > InMAX) return (OutMAX);
    return (((In - InMIN) * (OutMAX - OutMIN) / (InMAX - InMIN)) + OutMIN);
  }
}
//——————————————————————————————————————————————————————————————————————————————


7. 결과

AO

실행

스킨

포레스트

메가시티(개별)

최종 결과

매개변수 2개(1 F)

40개 매개변수(20F)

1000 매개변수(500 F)

매개변수 2개(1 F)

40개 매개변수(20F)

1000 매개변수(500 F)

매개변수 2개(1 F)

40개 매개변수(20F)

1000 매개변수(500 F)

RND

1000

0.98744

0.61852

0.49408

0.89582

0.19645

0.14042

0.77333

0.19000

0.14283

0.51254

10,000

0.99977

0.69448

0.50188

0.98181

0.24433

0.14042

0.88000

0.20133

0.14283


테스트 스탠드에서 테스트한 결과를 보면 RND OA의 결과는 예상치 못했던 것이었습니다. 이 알고리즘은 매우 높은 정확도로 두 변수의 최적 함수를 찾을 수 있는 반면 포레스트 및 메가시티의 경우 결과가 눈에 띄게 나쁩니다. 하지만 어쨌든 변수가 많은 함수에 대한 검색 속성이 약할 것이라는 저의 가정은 확인되었습니다. 결과는 40개의 인수로 매우 평범합니다. 최종 누적 값은 0.51254입니다.

다음 기사에서는 잘 알려져 있고 널리 사용되는 최적화 알고리즘을 분석 및 테스트하고 OA 등급을 구성하는 결과 표를 계속 작성할 것입니다.

MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
원본 기고글: https://www.mql5.com/ru/articles/8122

파일 첨부됨 |
MQL5.zip (9.71 KB)
모집단 최적화 알고리즘: 파티클 스웜(PSO) 모집단 최적화 알고리즘: 파티클 스웜(PSO)
이 글에서는 널리 사용되는 파티클 스웜 최적화(PSO) 알고리즘에 대해 살펴보겠습니다. 이전에는 수렴, 수렴 속도, 안정성, 확장성과 같은 최적화 알고리즘의 중요한 특성에 대해 알아보고 테스트 스탠드를 개발했으며 가장 간단한 RNG 알고리즘에 대해 알아보았습니다.
차이킨 오실레이터로 트레이딩 시스템을 설계하는 방법 알아보기 차이킨 오실레이터로 트레이딩 시스템을 설계하는 방법 알아보기
가장 인기 있는 보조지표로 트레이딩 시스템을 설계하는 방법을 소개하는 시리즈의 새로운 글에 오신 것을 환영합니다. 이 새로운 기사를 통해 우리는 차이킨 오실레이터 지표로 거래 시스템을 설계하는 방법을 배웁니다.
Expert Advisor 개발 기초부터(18부): 새로운 주문 시스템(I) Expert Advisor 개발 기초부터(18부): 새로운 주문 시스템(I)
이것이 새로운 주문 시스템의 첫 번째 부분입니다. 기사를 통해 EA를 문서화하기 시작한 이후 우리는 차트 주문 시스템 모델을 그대로 유지하면서 다양한 변경 및 개선을 추가했습니다.
표준 편차로 거래 시스템을 설계하는 방법 알아보기 표준 편차로 거래 시스템을 설계하는 방법 알아보기
이 기사는 MetaTrader 5 거래 플랫폼에서 가장 인기 있는 기술 지표를 사용하여 거래 시스템을 설계하는 방법에 대해 알아보는 시리즈의 새로운 기사입니다. 이번 기사에서는 표준 편차 지표로 거래 시스템을 설계하는 방법에 대해 알아봅니다.