Python 使用scipy计算积分,其中被积函数是参数来自(任意长)列表的乘积

Python 使用scipy计算积分,其中被积函数是参数来自(任意长)列表的乘积,python,scipy,numerical-integration,Python,Scipy,Numerical Integration,我想用我在维基百科上找到的Flajolet公式解决一般情况下优惠券收集者的问题(每个优惠券的可能性不同)(请参阅)。根据这个公式,我必须计算一个积分,其中被积函数是乘积。我使用scipy.integrad.quad和lambda表示法进行积分。问题是被积函数中的因子数量不是固定的(参数来自列表)。当我尝试乘以被积函数因子时,我得到了一个错误,因为我似乎无法乘以形式表达式。但是如果我没有,我不知道如何得到积分变量x 我找到了整合产品的方法,例如,如果只有两个因素。它似乎不涉及双重整合或类似的东西。

我想用我在维基百科上找到的Flajolet公式解决一般情况下优惠券收集者的问题(每个优惠券的可能性不同)(请参阅)。根据这个公式,我必须计算一个积分,其中被积函数是乘积。我使用scipy.integrad.quad和lambda表示法进行积分。问题是被积函数中的因子数量不是固定的(参数来自列表)。当我尝试乘以被积函数因子时,我得到了一个错误,因为我似乎无法乘以形式表达式。但是如果我没有,我不知道如何得到积分变量x

我找到了整合产品的方法,例如,如果只有两个因素。它似乎不涉及双重整合或类似的东西。有人能帮忙吗(我对这东西很陌生)


如果使用
args=
将参数传递给
quad
,则可以使用任意数量的参数定义积分函数:

def integrand(x, *p_list):
    p_list = np.asarray(p_list)
    return 1 - np.product(1 - np.exp(-x * p_list))   #don't need to for-loop a product in numpy

result, abserr = quad(integrand, 0, np.inf, args=[1,1,1,1])
print(result, abserr)
>> 2.083333333333334 2.491001112400493e-10

有关更多信息,请参见如果使用
args=
将参数传递给
quad
,则可以使用任意数量的参数定义积分函数:

def integrand(x, *p_list):
    p_list = np.asarray(p_list)
    return 1 - np.product(1 - np.exp(-x * p_list))   #don't need to for-loop a product in numpy

result, abserr = quad(integrand, 0, np.inf, args=[1,1,1,1])
print(result, abserr)
>> 2.083333333333334 2.491001112400493e-10

有关更多信息,请参见

感谢您回答这个问题:作为@Mstaino回答的后续内容,您甚至不需要
参数
,因为您可以通过闭包将它们传递到函数中:

def coupon_collector_expected_samples(probs):
    """
    Find the expected number of samples before all "coupons" (with a
    non-uniform probability mass) are "collected".

    Args:
        probs (ndarray): probability mass for each unique item

    References:
        https://en.wikipedia.org/wiki/Coupon_collector%27s_problem
        https://www.combinatorics.org/ojs/index.php/eljc/article/view/v20i2p33/pdf
        https://stackoverflow.com/questions/54539128/scipy-integrand-is-product

    Example:
        >>> import numpy as np
        >>> import ubelt as ub
        >>> # Check EV of samples for a non-uniform distribution
        >>> probs = [0.38, 0.05, 0.36, 0.16, 0.05]
        >>> ev = coupon_collector_expected_samples(probs)
        >>> print('ev = {}'.format(ub.repr2(ev, precision=4)))
        ev = 30.6537

        >>> # Use general solution on a uniform distribution
        >>> probs = np.ones(4) / 4
        >>> ev = coupon_collector_expected_samples(probs)
        >>> print('ev = {}'.format(ub.repr2(ev, precision=4)))
        ev = 8.3333

        >>> # Check that this is the same as the solution for the uniform case
        >>> import sympy
        >>> n = len(probs)
        >>> uniform_ev = float(sympy.harmonic(n) * n)
        >>> assert np.isclose(ev, uniform_ev)
    """
    import numpy as np
    from scipy import integrate
    probs = np.asarray(probs)
    # Philippe Flajolet's generalized expected value integral
    def _integrand(t):
        return 1 - np.product(1 - np.exp(-probs * t))
    ev, abserr = integrate.quad(func=_integrand, a=0, b=np.inf)
    return ev
您还可以看到我的实现速度更快:

import timerit
ti = timerit.Timerit(100, bestof=10, verbose=2)

probs = np.random.rand(100)

from scipy import integrate
def orig_method(p_list):
    def integrand(x, *p_list):
        p_list = np.asarray(p_list)
        return 1 - np.product(1 - np.exp(-x * p_list))
    result, abserr = integrate.quad(integrand, 0, np.inf, args=p_list)
    return result

ti = timerit.Timerit(100, bestof=10, verbose=2)
for timer in ti.reset('orig_implementation'):
    with timer:
        orig_method(probs)

for timer in ti.reset('my_implementation'):
    with timer:
        coupon_collector_expected_samples(probs)

# Results:
# Timed orig_implementation for: 100 loops, best of 10
#     time per loop: best=7.267 ms, mean=7.954 ± 0.5 ms
# Timed my_implementation for: 100 loops, best of 10
#     time per loop: best=5.618 ms, mean=5.648 ± 0.0 ms

感谢您回答这个问题:作为@Mstaino回答的后续内容,您甚至不需要
args
,因为您可以通过闭包将它们传递到函数中:

def coupon_collector_expected_samples(probs):
    """
    Find the expected number of samples before all "coupons" (with a
    non-uniform probability mass) are "collected".

    Args:
        probs (ndarray): probability mass for each unique item

    References:
        https://en.wikipedia.org/wiki/Coupon_collector%27s_problem
        https://www.combinatorics.org/ojs/index.php/eljc/article/view/v20i2p33/pdf
        https://stackoverflow.com/questions/54539128/scipy-integrand-is-product

    Example:
        >>> import numpy as np
        >>> import ubelt as ub
        >>> # Check EV of samples for a non-uniform distribution
        >>> probs = [0.38, 0.05, 0.36, 0.16, 0.05]
        >>> ev = coupon_collector_expected_samples(probs)
        >>> print('ev = {}'.format(ub.repr2(ev, precision=4)))
        ev = 30.6537

        >>> # Use general solution on a uniform distribution
        >>> probs = np.ones(4) / 4
        >>> ev = coupon_collector_expected_samples(probs)
        >>> print('ev = {}'.format(ub.repr2(ev, precision=4)))
        ev = 8.3333

        >>> # Check that this is the same as the solution for the uniform case
        >>> import sympy
        >>> n = len(probs)
        >>> uniform_ev = float(sympy.harmonic(n) * n)
        >>> assert np.isclose(ev, uniform_ev)
    """
    import numpy as np
    from scipy import integrate
    probs = np.asarray(probs)
    # Philippe Flajolet's generalized expected value integral
    def _integrand(t):
        return 1 - np.product(1 - np.exp(-probs * t))
    ev, abserr = integrate.quad(func=_integrand, a=0, b=np.inf)
    return ev
您还可以看到我的实现速度更快:

import timerit
ti = timerit.Timerit(100, bestof=10, verbose=2)

probs = np.random.rand(100)

from scipy import integrate
def orig_method(p_list):
    def integrand(x, *p_list):
        p_list = np.asarray(p_list)
        return 1 - np.product(1 - np.exp(-x * p_list))
    result, abserr = integrate.quad(integrand, 0, np.inf, args=p_list)
    return result

ti = timerit.Timerit(100, bestof=10, verbose=2)
for timer in ti.reset('orig_implementation'):
    with timer:
        orig_method(probs)

for timer in ti.reset('my_implementation'):
    with timer:
        coupon_collector_expected_samples(probs)

# Results:
# Timed orig_implementation for: 100 loops, best of 10
#     time per loop: best=7.267 ms, mean=7.954 ± 0.5 ms
# Timed my_implementation for: 100 loops, best of 10
#     time per loop: best=5.618 ms, mean=5.648 ± 0.0 ms

你到底在问什么?如果你的公式是正确的,或者是否存在一些现成的包来处理任意长度的情况?对不起,我忽略了你的评论(因为我是这个网站的新手)。与此同时,塔诺女士已经回答了我的问题。这个公式来自维基百科(很可能)是正确的。我不知道的是如何编码你到底在问什么?如果你的公式是正确的,或者是否存在一些现成的包来处理任意长度的情况?对不起,我忽略了你的评论(因为我是这个网站的新手)。与此同时,塔诺女士已经回答了我的问题。这个公式来自维基百科(很可能)是正确的。我不知道的是如何编码。非常感谢。我仍然需要在脑海中构思出细节,并对其进行一些处理,但这似乎是可行的!非常感谢。我仍然需要在脑海中构思出细节,并对其进行一些处理,但这似乎是可行的!