转换数据,使中位数附近的范围更准确

Transforming data so that the range around the median is more accurate

假设我有一些在 0 附近呈正态分布的浮点数。我需要将其序列化为一个 uint8,但我想将 uint8 的“更多”分配给分布的中心,并失去围绕 uint8 的分辨率边缘。

例如:127 对应 0.0255 对应 1.0。但是 191 而不是 0.5 — 相反,它会是 0.3 之类的东西,因为我们正在拉伸它以便大多数数字对应到接近 0 的值。

实际上,我实际上要生成一个随机 uint32 并将其转换为 float。但是在测试线性映射时,极端值(-1.0 和 1.0 附近)出现得太频繁了,我想把它集中在 0.0.

周围

我知道我可以使用 Box–Muller transform,但实际上不适合这里,因为:

  1. 我们可以在 -1.0 和 1.0 之间设置上限,不需要无限输出。

  2. 我们只有一个数字可以采样,而不是两个。

谢谢

分位数函数(也称为逆 CDF)将 [0, 1] 中的均匀随机数映射到服从分布(例如正态分布)的数字。

但是,在 normal distribution 的情况下,有一些事情需要知道(从现在开始调用分位数函数 Q(u)):

  • 分位数函数的范围是从 0 到 1,而不是从 -1 到 1 或从 0 到 255。
  • 正态分布可以取任何实数。事实上,对于这个分布,Q(0) 和 Q(1) 将等于无穷大。
  • 正态分布的分位数涉及反误差函数。分位数可能容易也可能不容易实现,具体取决于您的编程环境是否已经具有可用的反误差函数。
  • 出于上述原因,您必须缩放分位数函数以适应您想要的范围并避免无穷大,例如,从 [0.001, 0.999] 到 [0, 255](其中 128 将对应于 Q( 0.5),在正态分布情况下为 0)。下面是一个伪代码示例。
 for k in 0..255
    c=0.001+(0.999-0.001)*(k*1.0/256)
    print([k, Q(c)]) // print the uint8 value followed by the quantile
 end

我同意 Peter O 的回答,正确的重新映射是由误差函数完成的,并且需要进行一些缩放,因为在有限区间内运行。基本上归结为使用 s-shaped 函数将区间 [-1,1] 重新映射到自身。如果你想接近高斯,你需要 erf 和相应的逆。但如果你只想接近,你可以使用任何你喜欢的 s-shaped 功能。 前向和后向映射的一些 Python 示例可能是:

def map_fwd( x, s ):
    return  x / np.sqrt( 1 + s**2 * ( 1 - x**2 ) )
 
def map_bwd( y, s ):
    x = np.sqrt( 1 + s**2 ) * y / np.sqrt( 1 + (s * y)**2 )
    return x

def map_fwd( x, s ):
    return  np.arctanh( np.tanh( s ) * x ) / s

def map_bwd( y, s ):
    x = np.tanh( s * y ) / np.tanh( s )
    return x

或一些极端的

def map_fwd( x, m ):
    return  ellipkinc( np.pi * x / 2.0, m ) / ellipkinc( np.pi / 2.0, m )

def map_bwd( y, m ):
    u = ellipkinc( np.pi / 2.0, m ) * y
    phi =  ellipj( u, m )[-1]
    x = phi * 2 / np.pi
    return x

其中 sm 是描述与线性映射的偏差的参数。
我猜有无限的可能性,选择取决于精度与计算量。