Войти в систему

Home
    - Создать дневник
    - Написать в дневник
       - Подробный режим

LJ.Rossia.org
    - Новости сайта
    - Общие настройки
    - Sitemap
    - Оплата
    - ljr-fif

Редактировать...
    - Настройки
    - Список друзей
    - Дневник
    - Картинки
    - Пароль
    - Вид дневника

Сообщества

Настроить S2

Помощь
    - Забыли пароль?
    - FAQ
    - Тех. поддержка



Пишет Дмитрий Коняев ([info]dimchansky)
@ 2006-01-10 17:28:00


Previous Entry  Add to memories!  Tell a Friend!  Next Entry
Rational Approximation
Кросспост:
Может быть кому-то пригодится: алгоритм приближенного представления вещественного числа x в виде рациональной дроби N/D. Можно задать точность представления дроби: |N/D - x|<=tol.
Алгоритм безжалостно выдран из матлабовской функции rat. На авторство не претендую. :)


// RationalApproximation.h
//---------------------------------------------------------------------------
#ifndef __RATIONALAPPROXIMATION_H
#define __RATIONALAPPROXIMATION_H

//---------------------------------------------------------------------------
class TRationalApproximation
{
  public:
    TRationalApproximation();

    // Modifiers
    void Calculate(double value);    
    void Calculate(double value, double tolerance);

    // Selectors
    inline double GetNumerator() const { return m_numerator; }
    inline double GetDenominator() const { return m_denominator; }

  protected:
    const double m_defaultTolerance;
    double m_numerator;
    double m_denominator;
};

//---------------------------------------------------------------------------
#endif




// RationalApproximation.cpp
//---------------------------------------------------------------------------
#include "RationalApproximation.h"
#include <math.h>
#include <algorithm>
#include <limits> 

//---------------------------------------------------------------------------

TRationalApproximation::TRationalApproximation()
  : m_defaultTolerance(1e-6)
  , m_numerator(0.0)
  , m_denominator(0.0)
{
}
//---------------------------------------------------------------------------

void TRationalApproximation::Calculate(double value)
{
  Calculate(value, m_defaultTolerance * fabs(value));
}
//---------------------------------------------------------------------------

void TRationalApproximation::Calculate(double value, double tolerance)
{
  double x = value,
         c11 = 1.0, c12 = 0.0,
         c21 = 0.0, c22 = 1.0,
         epsilonTolerance = std::numeric_limits<double>::epsilon()*fabs(value),
         maxTolerance = std::max(tolerance, epsilonTolerance);

  while(1)
  {
    double d = (x > 0) ? floor(x + 0.5) : -floor(-x + 0.5); // round(x)
    x -= d;

    double tmp_c12 = c12;
    double tmp_c22 = c22;

    c12 = c11;
    c22 = c21;
    c11 = c11 * d + tmp_c12;
    c21 = c21 * d + tmp_c22;

    if(x == 0 || fabs(c11/c21 - value) <=  maxTolerance)
    {
      break;
    }

    x = 1.0 / x;
  }

  m_numerator = (c21 > 0) ? c11 : -c11;
  m_denominator = fabs(c21);
}
//---------------------------------------------------------------------------



Тестовая програмка:


#include <iostream>
#include <vector>

#include "RationalApproximation.h"

int main(int argc, char* argv[])
{
  typedef std::vector<double> values_type;
  values_type vals;
  vals.push_back(M_PI);
  vals.push_back(0.1);
  vals.push_back(0.02);
  vals.push_back(0.003);
  vals.push_back(0.0004);
  vals.push_back(0.00005);
  vals.push_back(0.000006);
  vals.push_back(0.0000007);
  vals.push_back(0.00000008);
  vals.push_back(0.000000009);
  vals.push_back(247037.0 / 4.0);
  vals.push_back(1.999999999);
  vals.push_back(0.12345678901);
  
  TRationalApproximation rat;

  std::cout.unsetf(std::ios::skipws);
  std::cout.precision(std::numeric_limits<values_type::value_type>::digits10 + 1);

  const double tol = 1e-6;
  std::cout << "Tolerance = " << tol << std::endl;

  for(values_type::const_iterator it = vals.begin(); it != vals.end(); ++it)
  {
    rat.Calculate(*it, tol);

    std::cout << *it << " = ";
    std::cout << rat.GetNumerator() << " / " << rat.GetDenominator() << std::endl;
  }

  return 0;;
}
 



Результат тестовой програмки:
Tolerance = 1e-06
3.141592653589793 = 355 / 113
0.1 = 1 / 10
0.02 = 1 / 50
0.003 = 3 / 1000
0.0004 = 1 / 2500
5e-05 = 1 / 20000
6e-06 = 1 / 166667
7e-07 = 0 / 1
8e-08 = 0 / 1
9e-09 = 0 / 1
61759.25 = 247037 / 4
1.999999999 = 2 / 1
0.12345678901 = 10 / 81


(Добавить комментарий)


[info]kedder@lj
2006-01-11 06:59 (ссылка)
Ну теперь жди письма от матлабовских юристов :)

(Ответить) (Ветвь дискуссии)


[info]dimchansky@lj
2006-01-11 08:07 (ссылка)
А я, а что я? Image
У них этот код в m-файле (на матлабовском языке) в открытом виде лежит, они ни от кого не скрывают этот код, а я на них сослался. Image

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]kedder@lj
2006-01-11 10:21 (ссылка)
Если я правильно понимаю закон об авторских правах, то там всё, что явно не разрешено автором или законом, то запрещено, включая создание производных работ и их распространение.

А если в том .m файле ещё сверху комментарий есть про Copyright ... All rights reserved, то это ваще смертный грех :)

С другой стороны, алгоритм наверняка общеизвестный и не матлабовцы его придумали..

N.B. Я не юрист - могу и ошибаться.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]dimchansky@lj
2006-01-11 11:24 (ссылка)
Copyright стоит, all right reserved нету. А алгоритм для разложения в цепную дробь явно задолго до них был придуман (ещё даже когда компов не было), так что я буду защищаться. Image

(Ответить) (Уровень выше)