2017-02-09 3 views
0

Если у нас есть конкретный диапазон значений max..min, довольно легко нормализовать его до значений 0..1 float, но если у нас нет конкретных ограничений? Можно ли построить универсальную функцию для вывода от 0 до 1? На мой взгляд, я думаю, что это невозможно, но я не специалист по математике.Нормализовать любое значение в диапазоне (-inf ... + inf) до (0 ... 1). Является ли это возможным?

Я ищу для реализации на JavaScript или PHP, но любой код на C/C++/Python/Delphi ОК, чтобы привести примеры (если есть некоторые)

ответ

3

Есть много способов сделать это. Я оставлю карту -inf и +inf, что можно сделать с помощью условных операторов.

  1. exp(x)/(1 + exp(x)) или эквивалент 1/(1 + exp(-x)), где exp является экспоненциальной функцией. Это логистическая функция.
  2. atan(x)/pi + 1/2
  3. (tanh(x) + 1)/2
  4. (1 + x/sqrt(1 + x*x))/2
  5. (1 + x/(1 + abs(x))/2
  6. (erf(x) + 1)/2

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

В моем Python 3.5.2 самым быстрым был (1 + x/(1 + abs(x)) * 0.5.

enter image description here

1

С почти все программирования чисел с плавающей запятой, значения распределены логарифмически. Поэтому сначала возьмите значение log() значения, чтобы начать картографирование, обращая внимание на проблемы, связанные с граничным случаем.

double map(double x, double x0, double x1, double y0, double y1) { 
    return (x - x0)/(x1 - x0) * (y1 - y0) + y0; 
} 

double noramlize01(double x) { 
    assert(x == x); // fail is x is NaN 
    // These values only need to be calculated once. 
    double logxmin = log(DBL_TRUE_MIN); // e.g. -323.306... 
    double logxmax = log(DBL_MAX); // e.g. 308.254... 
    double y; 
    if (x < -DBL_MAX) y = 0.0; 
    else if (x < 0.0) { 
    y = map(log(-x), logxmax, logxmin, nextafter(0.0,1.0), nextafter(0.5,0.0)); 
    } else if (x == 0.0) { 
    y = 0.5; 
    } else if (x <= DBL_MAX) { 
    y = map(log(x), logxmin, logxmax, nextafter(0.5,1.0), nextafter(1.0,0.5)); 
    } else { 
    y = 1.0; 
    } 
    return y; 
} 

double round_n(double x, unsigned n) { 
    return x * n; 
} 

void testr(double x) { 
    printf("% 20e %#.17g\n", x, noramlize01(x)); 
    //printf("% 20e %.17f\n", -x, noramlize01(-x)); 
} 

int main(void) { 
    double t[] = {0.0, DBL_TRUE_MIN, DBL_MIN, 1/M_PI, 1/M_E, 
     1.0, M_E, M_PI, DBL_MAX, INFINITY}; 
    for (unsigned i = sizeof t/sizeof t[0]; i > 0; i--) { 
    testr(-t[i-1]); 
    } 
    for (unsigned i = 0; i < sizeof t/sizeof t[0]; i++) { 
    testr(t[i]); 
    } 
} 

Пример вывода

   -inf 0.0000000000000000 
     -1.797693e+308 4.9406564584124654e-324 
     -3.141593e+00 0.24364835649917244 
     -2.718282e+00 0.24369811843639441 
     -1.000000e+00 0.24404194470924687 
     -3.678794e-01 0.24438577098209935 
     -3.183099e-01 0.24443553291932130 
     -2.225074e-308 0.48760724499523350 
     -4.940656e-324 0.49999999999999994 
     -0.000000e+00 0.50000000000000000 
     0.000000e+00 0.50000000000000000 
     4.940656e-324 0.50000000000000011 
     2.225074e-308 0.51239275500476655 
     3.183099e-01 0.75556446708067870 
     3.678794e-01 0.75561422901790065 
     1.000000e+00 0.75595805529075311 
     2.718282e+00 0.75630188156360556 
     3.141593e+00 0.75635164350082751 
     1.797693e+308 0.99999999999999989 
       inf 1.0000000000000000 

1000 samples

+0

Ничего себе! это действительно выглядит здорово, но я немного застрял в этой части «union {d; i64} = x», так как я понимаю, что C++ разбивает число на объединение, но в JS я его не реализовал. в любом случае выглядит, как этот код отлично работает в JS https://jsfiddle.net/p1ratrulezzz/qz5nzry8/ – P1ratRuleZZZ

1

Если вы не возражаете битовую dibbling и уверены в том, что код использует IEEE двоичную 64-бит с плавающей точкой, некоторые быстрого кода только несколько FP математических операций

// If double is 64-bit and same endian as integer 
double noramlize01(double x) { 
    assert(x == x); // fail if x is NaN 
    union { 
    double d; 
    int64_t i64; 
    uint64_t u64; 
    } u = {x}; 
    double d; 
    if (u.i64 < 0) { 
    u.u64 -= 0x8000000000000000; 
    d = (double) -u.i64; 
    } else { 
    d = (double) u.i64; 
    } 
    return d/(+2.0 * 0x7ff0000000000000) + 0.5; 
} 

// Аналогичный тестер this answer

  -inf 0.0000000000000000 
    -1.797693e+308 0.0000000000000000 
    -3.141593e+00 0.24973844740430023 
    -2.718282e+00 0.24979014633262589 
    -1.000000e+00 0.25012212994626282 
    -2.225074e-308 0.49975574010747437 
    -4.940656e-324 0.50000000000000000 
    -0.000000e+00 0.50000000000000000 
    0.000000e+00 0.50000000000000000 
    4.940656e-324 0.50000000000000000 
    2.225074e-308 0.50024425989252563 
    1.000000e+00 0.74987787005373718 
    2.718282e+00 0.75020985366737414 
    3.141593e+00 0.75026155259569971 
    1.797693e+308 1.0000000000000000 
      inf 1.0000000000000000 
Смежные вопросы