2017-02-09 39 views
0

如果我們具有最大最小值的具體範圍,將它標準化爲0..1浮點值是相當容易的,但是如果我們沒有具體限制?是否有可能建立通用功能,輸出0和1之間?在我看來,我認爲這是不可能的,但我不是數學專家。將範圍(-inf ... + inf)中的任何值標準化爲(0 ... 1)。可能嗎?

我在尋找上的JavaScript或PHP實現,但在C/C++/Python的/德爾福的任何代碼即可提供實例(如果有一些)

回答

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++如何將數字拆分成union,但在JS中我沒有實現它。無論如何看起來像這個代碼在JS中很好用https://jsfiddle.net/p1ratrulezzz/qz5nzry8/ – P1ratRuleZZZ

1

如果你不介意位點播和有信心,代碼使用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