Python 单调递增函数的反函数,log10()的溢出错误

Python 单调递增函数的反函数,log10()的溢出错误,python,binary-search,newtons-method,Python,Binary Search,Newtons Method,对于赋值,我们被要求创建一个返回反函数的函数。基本问题是从平方函数创建平方根函数。我提出了一个使用二进制搜索的解决方案和另一个使用牛顿方法的解决方案。对于立方根和平方根,我的解决方案似乎很好,但对于log10就不行了。以下是我的解决方案: #Binary Search def inverse1(f, delta=1e-8): """Given a function y = f(x) that is a monotonically increasing function on no

对于赋值,我们被要求创建一个返回反函数的函数。基本问题是从平方函数创建平方根函数。我提出了一个使用二进制搜索的解决方案和另一个使用牛顿方法的解决方案。对于立方根和平方根,我的解决方案似乎很好,但对于log10就不行了。以下是我的解决方案:

#Binary Search
def inverse1(f, delta=1e-8):
    """Given a function y = f(x) that is a monotonically increasing function on
    non-negative numbers, return the function x = f_1(y) that is an approximate
    inverse, picking the closest value to the inverse, within delta."""
    def f_1(y):
        low, high = 0, float(y)
        last, mid = 0, high/2
        while abs(mid-last) > delta:
            if f(mid) < y:
                low = mid
            else:
                high = mid
            last, mid = mid, (low + high)/2
        return mid
    return f_1

#Newton's Method
def inverse(f, delta=1e-5):
    """Given a function y = f(x) that is a monotonically increasing function on
    non-negative numbers, return the function x = f_1(y) that is an approximate
    inverse, picking the closest value to the inverse, within delta."""
    def derivative(func): return lambda y: (func(y+delta) - func(y)) / delta
    def root(y): return lambda x: f(x) - y
    def newton(y, iters=15):
        guess = float(y)/2
        rootfunc = root(y)
        derifunc = derivative(rootfunc)
        for _ in range(iters):
            guess = guess - (rootfunc(guess)/derifunc(guess))
        return guess
    return newton
发布的其他解决方案在运行全套测试输入时似乎没有问题(我尽量不看发布的解决方案)。对这个错误有什么见解吗


似乎大家的共识是数字的大小,然而,我教授的代码似乎对所有情况都适用:

#Prof's code:
def inverse2(f, delta=1/1024.):
    def f_1(y):
        lo, hi = find_bounds(f, y)
        return binary_search(f, y, lo, hi, delta)
    return f_1

def find_bounds(f, y):
    x = 1
    while f(x) < y:
        x = x * 2
    lo = 0 if (x ==1) else x/2
    return lo, x

def binary_search(f, y, lo, hi, delta):
    while lo <= hi:
        x = (lo + hi) / 2
        if f(x) < y:
            lo = x + delta
        elif f(x) > y:
            hi = x - delta
        else:
            return x;
    return hi if (f(hi) - y < y - f(lo)) else lo

log10 = inverse2(power10)
sqrt = inverse2(square)
cbrt = inverse2(cube)

print test() 

如果您使用的是Python2.x,
int
long
是不同的类型,而
overflowerrror
只能用于
int
(q.v.)。请尝试显式使用
long
(在整数值上使用
long()
内置函数,或在数值文本中附加
L


编辑:显然,正如Paul Seeb和KennyTM在他们的高级答案中指出的那样,这并不能弥补算法缺陷。

我追踪了您的错误,但基本上归结为10**10000000导致python溢出。使用数学库进行快速检查

math.pow(10,10000000)

Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    math.pow(10,10000000)
OverflowError: math range error
math.pow(1010000000)
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
数学功率(1010000000)
溢出错误:数学范围错误
我为你做了一点调查,发现了这个

您需要重新评估为什么需要计算如此大的数字(并相应地更改代码::建议),或者开始寻找一些更大的数字处理解决方案

您可以编辑您的反函数,以检查某些输入是否会导致它失败(try语句),如果函数不是单调递增的,这也可以解决一些零除法问题,并避免这些区域或区域


您可以镜像“有趣”区域中关于y=x的许多点,并通过这些点使用插值方案来创建“逆”函数(hermite、taylor级数等)

这实际上是你理解数学而不是程序的问题。算法很好,但提供的初始条件不是

您可以这样定义
逆(f,delta)

def inverse(f, delta=1e-5):
    ...
    def newton(y, iters=15):
        guess = float(y)/2
        ...
    return newton
所以你猜1000=10x的结果是500.0,但10500肯定太大了。初始猜测应选择为对f有效的a,而不是对f的倒数

我建议您使用猜测值1进行初始化,即将该行替换为

guess = 1
它应该很好用


顺便说一句,您的二进制搜索的初始条件也是错误的,因为您假定解决方案介于0和y之间:


这对于您的测试用例是正确的,但是构造反例很容易,例如log10 0.1(=-1),√0.36(=0.6)等。(你教授的
find_bounds
方法确实解决了这个问题√是的,
10**1000.0
太大了。@KennyTM其他学生的解决方案似乎通过了测试。我觉得我的解决方案遗漏了一些东西。也许你不应该从y=1000开始。在所有情况下都尝试y=1。下面是一个提示,说明为什么您的代码运行得更好。看看他的“寻找边界”方法的目的,看看为什么会出现这种逐次逼近算法的溢出,一个足够好的初始猜测往往是成功的一半。二进制搜索就像伊索的乌龟:如果lo和hi都包含解决方案,则速度慢且可靠。牛顿的方法更像兔子。它可能会很快跳转到解决方案(良好的初始猜测),但它可能会跳转到无穷大(糟糕的初始猜测)。我不知道大小限制,但它似乎不会影响课堂论坛上发布的其他解决方案,这些解决方案通过了所有测试。还有,有没有什么洞察到为什么我的牛顿方法根本不能处理log10(答案是wayyyy off)?感谢
math
模块本质上提供了对C的fast
的访问,因此将针对内置
pow()
**
运算符不允许的值引发
溢出错误
异常。这就是说,你的观点是正确的,因为计算如此巨大的数字会非常慢,即使它不会引发异常。看看他的“findbounds”方法的用途,看看为什么会出现溢出。你可能连一次计算都没有完成他的教授的“寻找边界”方法试图解决你的第二个问题。我想我自动地假设,这是任何一个单增函数的一般反函数,将范围从中间拆分将是一个很好的起点。。。应该多考虑一下。因此,从总体上看,或者仅仅从log10的角度来看,从1开始会是一个更好的起点?另外,关于为什么我的牛顿方法在所有log10测试中都不适用(上面的例子是我的bin搜索方法)?或者这与牛顿的猜测问题有关系吗?@Verbal\u Kint我想最好继续询问这些数值寻根方法的细节。
     2: sqrt =     1.4134903 (    1.4142136 actual); 0.0007 diff; ok
     2: log =     0.3000984 (    0.3010300 actual); 0.0009 diff; ok
     2: cbrt =     1.2590427 (    1.2599210 actual); 0.0009 diff; ok
     4: sqrt =     2.0009756 (    2.0000000 actual); 0.0010 diff; ok
     4: log =     0.6011734 (    0.6020600 actual); 0.0009 diff; ok
     4: cbrt =     1.5865107 (    1.5874011 actual); 0.0009 diff; ok
     6: sqrt =     2.4486818 (    2.4494897 actual); 0.0008 diff; ok
     6: log =     0.7790794 (    0.7781513 actual); 0.0009 diff; ok
     6: cbrt =     1.8162270 (    1.8171206 actual); 0.0009 diff; ok
     8: sqrt =     2.8289337 (    2.8284271 actual); 0.0005 diff; ok
     8: log =     0.9022484 (    0.9030900 actual); 0.0008 diff; ok
     8: cbrt =     2.0009756 (    2.0000000 actual); 0.0010 diff; ok
    10: sqrt =     3.1632442 (    3.1622777 actual); 0.0010 diff; ok
    10: log =     1.0009756 (    1.0000000 actual); 0.0010 diff; ok
    10: cbrt =     2.1534719 (    2.1544347 actual); 0.0010 diff; ok
    99: sqrt =     9.9506714 (    9.9498744 actual); 0.0008 diff; ok
    99: log =     1.9951124 (    1.9956352 actual); 0.0005 diff; ok
    99: cbrt =     4.6253061 (    4.6260650 actual); 0.0008 diff; ok
   100: sqrt =    10.0004883 (   10.0000000 actual); 0.0005 diff; ok
   100: log =     2.0009756 (    2.0000000 actual); 0.0010 diff; ok
   100: cbrt =     4.6409388 (    4.6415888 actual); 0.0007 diff; ok
   101: sqrt =    10.0493288 (   10.0498756 actual); 0.0005 diff; ok
   101: log =     2.0048876 (    2.0043214 actual); 0.0006 diff; ok
   101: cbrt =     4.6575475 (    4.6570095 actual); 0.0005 diff; ok
  1000: sqrt =    31.6220242 (   31.6227766 actual); 0.0008 diff; ok
  1000: log =     3.0000000 (    3.0000000 actual); 0.0000 diff; ok
  1000: cbrt =    10.0004883 (   10.0000000 actual); 0.0005 diff; ok
 10000: sqrt =    99.9991455 (  100.0000000 actual); 0.0009 diff; ok
 10000: log =     4.0009756 (    4.0000000 actual); 0.0010 diff; ok
 10000: cbrt =    21.5436456 (   21.5443469 actual); 0.0007 diff; ok
 20000: sqrt =   141.4220798 (  141.4213562 actual); 0.0007 diff; ok
 20000: log =     4.3019052 (    4.3010300 actual); 0.0009 diff; ok
 20000: cbrt =    27.1449150 (   27.1441762 actual); 0.0007 diff; ok
 40000: sqrt =   199.9991455 (  200.0000000 actual); 0.0009 diff; ok
 40000: log =     4.6028333 (    4.6020600 actual); 0.0008 diff; ok
 40000: cbrt =    34.2003296 (   34.1995189 actual); 0.0008 diff; ok
 1e+08: sqrt =  9999.9994545 (10000.0000000 actual); 0.0005 diff; ok
 1e+08: log =     8.0009761 (    8.0000000 actual); 0.0010 diff; ok
 1e+08: cbrt =   464.1597912 (  464.1588834 actual); 0.0009 diff; ok
None
math.pow(10,10000000)

Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    math.pow(10,10000000)
OverflowError: math range error
def inverse(f, delta=1e-5):
    ...
    def newton(y, iters=15):
        guess = float(y)/2
        ...
    return newton
guess = 1
low, high = 0, float(y)