【问题标题】:Normalize any value in range (-inf...+inf) to (0...1). Is it possible?将 (-inf...+inf) 范围内的任何值标准化为 (0...1)。可能吗?
【发布时间】:2017-06-27 16:16:18
【问题描述】:

如果我们有 max..min 值的具体范围,很容易将其标准化为 0..1 浮点值,但如果我们没有具体限制?是否可以构建通用函数以使输出介于 0 和 1 之间?在我看来,我认为这是不可能的,但我不是数学专家。

我正在寻找 JavaScript 或 PHP 上的实现,但是 C/C++/Python/Delphi 上的任何代码都可以提供示例(如果有的话)

【问题讨论】:

    标签: math range normalize


    【解决方案1】:

    有很多方法可以做到这一点。我将省略映射-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

    【讨论】:

      【解决方案2】:

      如果您不介意比特挖空并且确信代码使用 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
      

      【讨论】:

        【解决方案3】:

        几乎所有的浮点数编程,值都是以对数分布的。因此首先取值的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
        

        【讨论】:

        • 哇!这看起来真的很棒,但我很少但坚持这个“联合 {d; i64} = x”部分,因为我知道这是 C++ 如何将数字拆分为联合,但在 JS 中我没有实现它。无论如何看起来这段代码在 JS jsfiddle.net/p1ratrulezzz/qz5nzry8 中效果很好
        猜你喜欢
        • 2019-08-12
        • 2012-09-02
        • 2020-02-11
        • 2020-12-23
        • 1970-01-01
        • 2016-08-19
        • 2018-02-01
        • 1970-01-01
        • 2016-10-31
        相关资源
        最近更新 更多