вместо Кодобейз: поиск ближайшей дроби

27 октября 2025, 08:14
Maxim Kuznetsov
0
51

из принципа "увидел прикольный алгоритм, не проходи мимо" :-) 

на stackoverflow встретился компактный алг. поиск ближайшей правильной дроби - портировал в MQL. 

/** поиск ближайшей подходящей дроби
   порт с https://stackoverflow.com/questions/5124743/algorithm-for-simplifying-decimal-to-fractions/37573546#37573546
**/   

double Fraction(const double x0,unsigned long &num,unsigned long &den,double &sign,double err=1.0e-10)
{
   sign=x0>=0?1.0:-1.0;
   double g=MathAbs(x0);
   unsigned long a=0;
   unsigned long b=1;
   unsigned long c=1;
   unsigned long d=0;
   unsigned long s;
   unsigned int iter=0;
   double diff;
   do {
      s = (unsigned long)MathFloor(g);
      num = a+s*c;
      den = b+s*d;
      a = c;
      b = d;
      c = num;
      d = den;
      g = 1.0/(g-s);
      diff=MathAbs(sign * num / den - x0);
      if (err>diff) {
         return diff;
      }
   } while(iter++<1.0e6);
   return double("nan");
}
void Test(double v,string s="") {
   unsigned long num,den;
   double sign;
   double diff=Fraction(v,num,den,sign);
   if (MathIsValidNumber(diff)) {
      PrintFormat("%s = %s%lu/%lu +%12f",s==""?DoubleToString(v,12):s,sign<0?"-":"",num,den,diff);
   }
}
void OnStart()
{
   Test(1.01);   
   Test(1.02);   
   Test(2.03);   
   Test(1.33333333333);   
   Test(0.66666666666);   
   Test(M_PI,"pi");
   Test(MathPow(M_PI,2),"pi^2");
   Test(1.0/M_PI,"1/pi");
   Test(1.0/MathPow(M_PI,2),"1/pi^2");
   Test(M_E,"e");
   Test(1.0/M_E,"1/e");
   Test((MathSqrt(5)+1)/2); 	// golden ratio
   Test((MathSqrt(5)-1)/2);
   Test((MathSqrt(2)+1));	// silver ratio
   Test((MathSqrt(2)-1));
}