Python 将numpy数组与标量传递给函数会产生不一致的结果

Python 将numpy数组与标量传递给函数会产生不一致的结果,python,numpy,Python,Numpy,我有一个简单的程序来计算一个函数 import numpy as np def fn(n, x0): return (np.sin(np.arcsin(x0**0.5)*2**n))**2 n = np.arange(100) x0 = 0.3 print(fn(n, x0)) print(fn(50, x0)) 结果如下: [0.3 0.84 0.5376 0.99434496 0.02249224 0.08794536 0.32084391

我有一个简单的程序来计算一个函数

import numpy as np

def fn(n, x0):
    return (np.sin(np.arcsin(x0**0.5)*2**n))**2

n = np.arange(100)

x0 = 0.3
print(fn(n, x0))
print(fn(50, x0))
结果如下:

[0.3        0.84       0.5376     0.99434496 0.02249224 0.08794536
 0.32084391 0.87161238 0.44761695 0.98902407 0.04342185 0.16614558
 0.55416492 0.98826465 0.04639054 0.17695382 0.58256466 0.97273231
 0.10609667 0.37936067 0.94178461 0.21930545 0.68484228 0.86333333
 0.47195556 0.99685404 0.01254426 0.04954761 0.18837059 0.61154843
 0.95022779 0.18917976 0.         0.         0.         0.
 0.         0.         0.         0.         0.         0.
 0.         0.         0.         0.         0.         0.
 0.         0.         0.         0.         0.         0.
 0.         0.         0.         0.         0.         0.
 0.         0.         0.         0.         0.         0.
 0.         0.         0.         0.         0.         0.
 0.         0.         0.         0.         0.         0.
 0.         0.         0.         0.         0.         0.
 0.         0.         0.         0.         0.         0.
 0.         0.         0.         0.         0.         0.
 0.         0.         0.         0.        ]
0.9931071163166798
这两个结果是不一致的,因为
fn(n,x0)
结果中的索引50(其中n=50)元素为零,而对
50
的函数进行评估会导致非零值。为什么会这样


为什么结果数组中的大多数元素都是
f(n,x0)
0?根据数学,情况不应该是这样。

对于计算结果中预期有十进制值的数学表达式,必须将函数中的数值具体定义为浮点数:

def fn(n, x0):      
  return (np.sin(np.arcsin(x0**0.5)*2.**n))**2.
然后,对于
(fn(n,x0))
我得到结果:

[0.3        0.84       0.5376     0.99434496 0.02249224 0.08794536
 0.32084391 0.87161238 0.44761695 0.98902407 0.04342185 0.16614558
 0.55416492 0.98826465 0.04639054 0.17695382 0.58256466 0.97273231
 0.10609667 0.37936067 0.94178461 0.21930545 0.68484228 0.86333333
 0.47195556 0.99685404 0.01254426 0.04954761 0.18837059 0.61154843
 0.95022779 0.18917976 0.61356311 0.94841368 0.19570069 0.62960771
 0.93280737 0.25071114 0.75142025 0.74715144 0.75566467 0.7385423
 0.77239028 0.70321414 0.83481605 0.55159286 0.98935271 0.04213571
 0.16144118 0.5415117  0.99310712 0.02738149 0.10652697 0.38071589
 0.9430852  0.21470202 0.67442025 0.87831031 0.42752525 0.97898964
 0.08227569 0.3020256  0.84322454 0.52878765 0.99668508 0.01321571
 0.05216421 0.19777242 0.63463397 0.92749478 0.26899286 0.78654281
 0.67157288 0.88225099 0.41553673 0.97146383 0.11088744 0.39436567
 0.95536555 0.17056885 0.56590047 0.98262851 0.06827888 0.25446749
 0.75885515 0.73197605 0.78474845 0.67567327 0.8765556  0.43282351
 0.98194928 0.07089957 0.26349128 0.7762545  0.69473381 0.84831498
 0.51470671 0.99913485 0.0034576  0.01378259]
(fn(n,x0))[50]
的计算结果为
0.993107116317


虽然我使用
n=np.arange(100)
获得这些结果,但最好将所有内容都保持为浮动,并使用
n=np.arange(100)

这需要一些调试,这在使用
numpy
时非常典型。两个常见的原因是

  • 遭受溢出或整数除法的整数数据类型
  • 给出不精确行为的浮点数据类型
  • 导致
    nan
    inf
    的计算会传播到后续步骤
  • 找出哪一个原因(或者是完全不同的原因)的方法是一步一步地进行

    >>> x0 = 0.3
    >>> x0**0.5
    0.5477225575051661
    
    没问题。这并不奇怪,因为此步骤与
    n
    无关。下一步

     >>> 2**n
    array([                   1,                    2,                    4,
                              8,                   16,                   32,
                             64,                  128,                  256,
                            512,                 1024,                 2048,
                           4096,                 8192,                16384,
                          32768,                65536,               131072,
                         262144,               524288,              1048576,
                        2097152,              4194304,              8388608,
                       16777216,             33554432,             67108864,
                      134217728,            268435456,            536870912,
                     1073741824,           2147483648,           4294967296,
                     8589934592,          17179869184,          34359738368,
                    68719476736,         137438953472,         274877906944,
                   549755813888,        1099511627776,        2199023255552,
                  4398046511104,        8796093022208,       17592186044416,
                 35184372088832,       70368744177664,      140737488355328,
                281474976710656,      562949953421312,     1125899906842624,
               2251799813685248,     4503599627370496,     9007199254740992,
              18014398509481984,    36028797018963968,    72057594037927936,
             144115188075855872,   288230376151711744,   576460752303423488,
            1152921504606846976,  2305843009213693952,  4611686018427387904,
           -9223372036854775808,                    0,                    0,
                              0,                    0,                    0,
                              0,                    0,                    0,
                              0,                    0,                    0,
                              0,                    0,                    0,
                              0,                    0,                    0,
                              0,                    0,                    0,
                              0,                    0,                    0,
                              0,                    0,                    0,
                              0,                    0,                    0,
                              0,                    0,                    0,
                              0,                    0,                    0,
                              0])
    
    清楚地显示了有问题的行为。有一堆零,前面有一个大的负数。更仔细地观察结果

    >>> (2**n).dtype
    dtype('int64')
    
    您可以看到,第n次幂的2仍然是一个整数。似乎发生了一个简单的位移位,当结果不再包含在整数数据类型中时,会导致全零


    修复方法很简单,请使用
    n.astype(float)
    而不是
    n

    请注意,简单地用float替换int不是合适的解决方案。事实上,对于较大的
    n
    ,此方法返回的值完全是假的

    让我使用
    bigfloat
    一个提供任意精度浮点的库来演示这一点

    使用float64,返回n=99,x0=0.3的值

    >>> fn(99., 0.3)
    0.013782590413701074
    
    为~0.0138

    现在,我们将使用1000位的高精度计算正确的值

    >>> def fnbig(n, x0):
    ...     n, x0 = map(bigfloat.BigFloat, (n, x0))
    ...     return bigfloat.pow(bigfloat.sin(bigfloat.asin(bigfloat.sqrt(x0))*bigfloat.pow(2, n)), 2)
    ... 
    >>> 
    >>> bigfloat.setcontext(bigfloat.Context(1000))
    
    请注意,0.3必须作为字符串输入,因为从文本0.3生成的float64已经带有一个太大的错误,无法得到正确的答案

    >>> fnbig('99', '0.3')
    BigFloat.exact('0.363780859940401348053691101648398065131477584225708461696799538248050278540782181716110363889498612214432889606382752875154011855764448898240841915231368492158238806206980341185053867226372528105024157964509865633147960964164133657255856469376571664623973084231004713906743471127849494395877727320492003', precision=1000)
    
    返回约0.364

    为了确保这是正确的,让我们将精度加倍到2000位

    >>> bigfloat.setcontext(bigfloat.Context(2000))
    >>> 
    >>> fnbig('99', '0.3')
    BigFloat.exact('0.3637808599404013480536911016483980651314775842257084616967995382480502785407821817161103638894986122144328896063827528751540118557644488982408419152313684921582388062069803411850538672263725281050241579645098656331479609641641336572558564693765716646239730842310047139067975945820088206827783571320258628882284629795545097600685961974610320482001970915733612836861863674071009032317962504679512051859460424746278292327826581975723619660002116915303723311156451829258099225827808017028059470793304713100650332080089174169114171398280313842625628566029927379227478504732491009738418661061753082431884081337', precision=2000)
    
    返回的精度为1000和2000的值基本相同,因此我们可以确信它们是正确的


    相比之下,使用“普通”浮点运算返回的值几乎是一个随机数。

    因为
    np.arange(100)
    50
    不同?我真正想知道的是,为什么生成的numpy数组与数学结果不符。大多数元素都是零,不应该是零。我用n=50来检查,只是为了确保它们不应该是零,但是元素中没有一个应该是零。我用n=90来检查,结果仍然不是零。这个函数是一个映射x_n=4x(1-x)的解析解,这是一个混沌映射,所以
    f(n,x0)
    的元素都不应该是零…当我们得到不同的结果时,它更令人困惑,然而,后面的元素都是零,这不应该是这种情况哦,我想我已经发现了原因。与浮点/整数相关。仍然不确定我们之间最初的结果为何不同。修改我的答案…哦,问题解决了,谢谢!我以前遇到过一些问题,因为我还没有用浮点数来指定数字,我真的应该记住这一点。这篇文章(Sarra&Meador,2011)看起来是一个很好的关于这个问题的参考文献:本文中的等式2与我们正在处理的等式相同: