【问题标题】:Expected value of expression表达式的期望值
【发布时间】:2017-01-19 18:51:50
【问题描述】:

如何在P/Q 表单中找到表达式的预期值

鉴于:
N 个整数
2 个运算符,“按位或”和“+”

我们可以使用两个运算符中的任何一个,每个连续整数之间的概率相等。

目前,我想到的解决方案是使用运算符生成所有可能的表达式,然后使用每个表达式的值来计算它的期望值。

但随着 N 的增长,这种方法会失败。就时间复杂度而言,还有其他有效的替代方案吗?

注意:对于这个问题:'Bitwise OR' 的优先级高于 '+' 运算符。

最多可以有 10^5 个整数。

例子:

Input
1 2 3

Output
19/4

不同的方式是:

1+2+3 = 6

1+2|3 = 4

1|2+3 = 6

1|2|3 = 3

所有这些方式都有概率=1/4

所以预期值为19/4

【问题讨论】:

  • 你确定这是一道编程题吗?还是通过某种逻辑找到期望?
  • 你能举个例子吗?整数可以是负数吗?
  • 使用 DP 可以大大减少工作量。
  • @A.S.H 是的,它的编程问题。
  • 这是来自编程比赛吗?数字计数及其值的限制是什么?

标签: algorithm math


【解决方案1】:

重要的观察是每个+ 都将其左右部分分成可以独立处理的部分。

设数字数组为a[1…N]。定义f(i) 为从a[i…N] 得到的期望值。我们要查找的是f(1)

请注意,第一个+ 登录[i…N] 将出现在ith 元素之后,概率为1/2i+1th 元素,概率为1/4,依此类推。只需找到元素的按位或直到+ 并添加剩余部分的期望值。

这样我们就有了循环

f(i) = sum_{j = i to N-1} (or(a[i…j]) + f(j+1))/(2^(j-i+1)) + or(a[i…N])/(2^(N-i))

这应该很容易有效地实现而不会出错。

对于示例数组[1,2,3]

  • f(3) = or(a[3…3]) = 3
  • f(2) = (or(a[2…2])+f(3))/2 + or(a[2…3])/2 = 5/2 + 3/2 = 4李>
  • f(1) = (or(a[1…1])+f(2))/2 + (or(a[1…2])+f(3))/4 + or(a[ 1…3])/4 = 5/2 + 6/4 + 3/4 = 19/4

正如预期的那样,发现答案是 19/4。

【讨论】:

  • 我无法使用您的方法得到正确答案。你能用上面的例子详细说明一下吗?
  • 已添加。你和这有什么不同?
  • 因为我需要单独的P&Q,所以我忽略了每项除以2,只计算了分子部分。因为分母永远是 2^(N-1) 。仅计算 P 是否需要进行任何修改?我用 18 代替分子中的 19。
  • 在这种情况下,您可以尝试修改循环以找到 f(i)*2^(N-i)。尝试与我的分步解决方案进行比较,看看我们中的哪一个犯了错误。
  • 上面的时间复杂度也是O(N^2)。有什么方法可以通过一些预计算减少到O(NlogN)
【解决方案2】:

首先,由于存在2ⁿ⁻¹ 表达式(数字之间的每个n-1 位置上都有两个可能的运算符)并且它们的概率相同,因此期望值是所有表达式的总和除以@987654323 @。所以问题归结为计算表达式的总和。

O(n²) 算法

x_1, x_2, ..., x_n 成为输入数字。

S_k 是通过在列表x_1, x_2, ..., x_k 中的每对连续数字之间插入|+ 形成的所有表达式的总和。

N_k 为所有此类表达式的数量。 N_k = 2 ^ (k - 1).

让我们看看如何使用S_1, S_2, ..., S_(k-1) 来计算S_k

这个想法是将所有可能的表达式除以它们中最后一个"+" 的位置。

  1. "... + x_k" 形式的表达式的总和是
    • S_(k-1) + x_k * N_(k-1)
  2. "... + x_(k-1) | x_k" 形式的表达式的总和是
    • S_(k-2) + (x_(k-1) | x_k) * N_(k-2)
  3. "... + x_(k-2) | x_(k-1) | x_k" 形式的表达式的总和是
    • S_(k-2) + (x_(k-2) | x_(k-1) | x_k) * N_(k-3)
  4. ...以此类推,直到单个表达式 x_1 | x_2 | ... | x_k

这是该算法的 Python 实现。

numbers = [1, 2, 3] # The input numbers.
totals = [0]        # The partial sums. For every k > 0 totals[k] is S_k.

for i in range(len(numbers)):  # Processing the numbers one by one.
    new_total = 0
    last_summand = 0           # last_summand is numbers[j] | ... | numbers[i]
    for j in range(i, 0, -1):  # j is the position of the last plus in the expression.
        # On every iteration new_total is increased by the sum of the 
        # expressions of the form "... + numbers[j] | ... | numbers[i]".
        last_summand |= numbers[j]
        new_total += totals[j] + last_summand * (2 ** (j - 1))
    last_summand |= numbers[0]
    new_total += last_summand  # Handling the expression with no pluses at all.
    totals.append(new_total)

# Now the last element in totals is the sum of all expressions.
print(str(totals[-1]) + '/' + str(2**(len(numbers) - 1))) 

进一步优化:O(n*log(M))

这个问题有两个属性可以用来创建一个更快的算法。

  1. 如果S_n是由数字x_1, x_2, ..., x_n组成的表达式之和,那么2*S_n是由数字2*x_1, 2*x_2, ..., 2*x_n组成的表达式之和。
  2. 如果x_1, x_2, ..., x_ny_1, y_2, ..., y_n 是这样的x_k & y_m == 0 对于任何km,并且SX_n 是由x_1, x_2, ..., x_n 形成的表达式的总和,而SY_n 是总和由y_1, y_2, ..., y_n组成的表达式,那么SX_n + SY_n是由x_1+y_1, x_2+y_2, ..., x_n+y_n组成的表达式的总和。

这意味着,问题可以简化为求 1 位数字的表达式之和。从031的每个位位置都可以单独处理,找到解决方案后我们可以简单地添加它们。

x_1, x_2, ..., x_n 是一位数字(每个x_i01)。

S_kx_1, x_2, ..., x_k 组成的表达式之和。

N0_k 为最后一个和等于 0 的此类表达式的数量。

N1_k 为最后一个和等于 1 的此类表达式的数量。

这是只知道x_kS_(k-1)N0_(k-1)N1_(k-1)的循环关系:

  1. k = 1, x_1 = 0:
    • S_1 = 0
    • N0_1 = 1
    • N1_1 = 0
  2. k = 1, x_1 = 1:
    • S_1 = 1
    • N0_1 = 0
    • N1_1 = 1
  3. k > 1, x_k = 0:
    • S_k = S_(k-1) * 2
    • N0_k = N0_(k-1) * 2 + N0_(k-1)
    • N1_k = N1_(k-1)
  4. k > 1, x_k = 1:
    • S_k = S_(k-1) * 2 + N0_(k-1) * 2 + N0_(k-1)
    • N0_k = 0
    • N1_k = N0_(k-1) * 2 + N0_(k-1) * 2

由于S_n可以在O(n)中找到,并且需要对每个位位置找到,因此整个算法的时间复杂度为O(n*log(M)),其中M是数字的上限。

一个实现:

numbers = [1, 2, 3]
max_bits_in_number = 31

def get_bit(x, k):
    return (x >> k) & 1

total_sum = 0
for bit_index in range(max_bits_in_number):
    bit = get_bit(numbers[0], bit_index)
    expression_sum = bit
    expression_count = (1 - bit, bit)
    for i in range(1, len(numbers)):
        bit = get_bit(numbers[i], bit_index)
        if bit == 0:
            expression_sum = expression_sum * 2
            expression_count = (expression_count[0] * 2 + expression_count[1], expression_count[1])
        else:
            expression_sum = expression_sum * 2 + expression_count[0] * 2 + expression_count[1]
            expression_count = (0, expression_count[0] * 2 + expression_count[1]*2)
    total_sum += expression_sum * 2**bit_index

print(str(total_sum) + '/' + str(2**(len(numbers) - 1)))

【讨论】:

  • 最多可以有10^5 整数。所以这个O(N^2) 解决方案不会节省时间。有没有优化的方法?
  • 一个整数可以有多大?
  • 每个整数都在[0, 10^9]范围内
  • @Khaneja 我添加了另一种算法,它与 N 成线性关系(并且与数字的界限成对数)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多