【问题标题】:How to implement a cumulative product table?如何实现累积产品表?
【发布时间】:2016-12-27 05:45:26
【问题描述】:

鉴于以下问题:

有一个由k个整数组成的序列,命名为s,其中可以有2个操作,

1) 总和[i,j] - s[i]+s[i+1]+...+s[j]的值是多少?

2) 更新[i,val] - 将 s[i] 的值更改为 val

我相信这里的大多数人都听说过使用累积频率表/fenwick 树来优化复杂性。

现在,如果我不想查询总和,而是想执行以下操作:

产品[i,j] - s[i] * s[i+1] * ... * s[j]的值是多少?

新问题一开始似乎微不足道,至少对于第一个操作 Product[i,j]

假设我正在使用一个名为 f 的累积产品表:

  1. 乍一看,当我们调用 Update[i,val] 时,我们应该将 f[z] 处的累积积除以 z 从 i - > j 乘以 s[i] 的旧值,然后乘以新值。
  2. 但如果 s[i] 的旧值为 0,我们将面临 2 个问题:

    • 除以 0。但这很容易通过检查 s[i] 的旧值是否为 0 来解决。

    • 任何实数与 0 的乘积为 0。此结果将导致从 f[i]f[j] 的所有其他值0. 所以我们无法成功执行Update[i,val]。这个问题并不是那么微不足道,因为它会影响除 f[i] 之外的其他值。

有没有人知道如何实现支持上述两种操作的累积产品表?

【问题讨论】:

    标签: algorithm cumulative-frequency


    【解决方案1】:

    维护 2 个表:

    • 累积产品表,其中所有零条目都存储为一(以避免影响其他条目)。
    • 存储零条目数的累积总和。如果 f[i] 为 0,则每个条目 s[i] 为 1,如果非零,则为 0。

    要计算累积乘积,首先计算给定范围内零项的累积和。如果非零(即范围内有 1 个或多个零),则累积乘积为零。如果为零,则按照您的描述计算累积乘积。

    将因子存储为某个基数的对数并将累积乘积计算为对数值的总和可能更准确。您只需计算 2 个累积总和。在这种情况下,您需要在产品表中将零条目存储为 0 的日志值(即 1 的值)。

    这是一个示例,使用简单的累积和(不是 Fenwick 树,但您可以轻松地使用它们):

     row   f   cum_f   isZero  cum_isZero  log(f)  cum_log(f)
    
    -1     1     1       0         0        0         0
    
     0     3     3       0         0        0.477     0.477
     1     0     3       1         1        -inf      0.477
     2     4    12       0         1        0.602     1.079
     3     2    24       0         1        0.301     1.38
     4     3    72       0         1        0.477     1.857
    

    row 是索引,f 是因子,cum_f 是 f 将零视为 1 的累积积,isZero 是指示 f 是否为零的标志,cum_isZero 是 isZero 标志的累积和,log (f)是以10为底的f的对数,cum_log(f)是log_f的累积和,把-inf当作零。

    要计算从第 i 行到第 j 行(含)的范围的总和或乘积,请从 row[j] 中减去 row[i-1],将第 -1 行用作“虚拟”行。

    要计算f在第0-2行的累积积,首先求isZero的累积和:cum_isZero[2] - cum_isZero[-1] = 1 - 0 = 1。那是非零,所以累积积是 0

    要计算第 2-4 行中 f 的累积乘积,请执行上述操作:cum_isZero[4] - cum_isZero[1] = 0 - 0 = 0。为零,因此我们可以计算乘积。

    使用 cum_f:cum_f[4] / cum_f[1] = 72 / 3 = 24 = 4 x 2 x 3

    使用 cum_log_f:cum_log(f)[4] - cum_log(f)[1] = 1.857 - 0.477 = 1.38

    101.38 = 大约 24

    【讨论】:

    • 对数值的总和?你指的是使用像芬威克树这样的东西吗?
    • 使用log(s) 是个好主意(如果双精度没问题的话)!通过将log(0)=-inf-inf+x=inf 用于任何实数x,我们只需要一棵芬威克树。
    • @LanceHAOH 是的。利用 log(axbxcxdx e...) = log(a)+log(b)+log(c)+log(d)+log(e)... 这个事实因素,然后用它作为指数来找到你的累积产品。您可以使用 Fenwick 树来计算对数的范围总和
    • @ead 我不明白如何使用 log(0) 使我们只能使用一棵 fenwick 树。无论如何有理由使用对数吗?不会提高效率吧?
    • 使用对数的原因是为了避免溢出最大浮点值(如果这是一个问题),而且实现起来更简单,因为您只需要实现存储累积和,而不是总和和产品。这只是一个建议。
    猜你喜欢
    • 1970-01-01
    • 2017-03-07
    • 1970-01-01
    • 2019-03-30
    • 1970-01-01
    • 1970-01-01
    • 2020-11-21
    • 1970-01-01
    • 2021-07-21
    相关资源
    最近更新 更多