323 Bitwise OR Operations on Random Integers

Problem 323

这道题乍一看是位运算,其实是一道和概率分布相关的题目。

一系列 32 bits 的无符号数。

初始为 0,然后和这一系列数异或操作,操作了 次,直到 ,即 所有 bits 都是1。

的期望值。

期望怎么计算呢?

计算对于每一个 对应的概率 ,然后加权求和。 我们现在逐一分析一下 1. 为 1,那么说明 每一个 bits 都是 1,概率是 2. 为 2,针对每个 bit 进行计算,第一个 bit,结果是 1 的概率是 1 减去不是 1 的概率,不是 1 的概率就是 的第一个 bit 是 0, 的第一个 bit 也是 0,概率是 ,那么 3. 为3,

以此类推。题目要求小数点后十位,如果 很小,乘以 对结果都没有影响了,也就是 小于 10 的十次方。

我计算的时候搞了一个数据结构保存概率的分子和分母。我解答出来之后,看了下别人的分析,感觉直接使用 double 类型保存概率也能给出正确答案,我没有试,不确定。

下面是我的代码:

public class Probability
{
    public BigInteger Numerator { get; set; }
    public BigInteger Denominator { get; set; }

    public Probability(BigInteger numerator, BigInteger denominator)
    {
        Numerator = numerator;
        Denominator = denominator;
    }
}

public static string GetAnswer()
{
    List<Probability> probabilities = new List<Probability>();
    probabilities.Add(new Probability(0, 1));
    BigInteger twoPow32 = BigInteger.Pow(2, 32);
    for (int i = 1; ; i++)
    {
        BigInteger baseDenominator = BigInteger.Pow(2, i);
        BigInteger baseNumerator = baseDenominator - 1;
        BigInteger lastBaseDenominator = BigInteger.Pow(2, i - 1);
        BigInteger lastBaseNumerator = lastBaseDenominator - 1;
        BigInteger denominator = BigInteger.Pow(baseDenominator, 32);
        BigInteger numerator = BigInteger.Pow(baseNumerator, 32);
        BigInteger lastNumerator = BigInteger.Pow(lastBaseNumerator, 32);
        probabilities.Add(new Probability(
            numerator - lastNumerator * twoPow32,
            denominator));

        if (probabilities[i].Denominator / probabilities[i].Numerator / i > BigInteger.Pow(10, 12))
        {
            break;
        }
    }

    BigInteger ansDenominator = probabilities.Last().Denominator;
    BigInteger ansNumerator = 0;
    for (int i = 1; i < probabilities.Count; i++)
    {
        Probability current = probabilities[i];
        BigInteger times = ansDenominator / current.Denominator;
        ansNumerator += times * current.Numerator * i;
    }

    BigInteger times11 = ansNumerator * 100000000000 / ansDenominator;
    double answer = (double)times11 / 100000000000;
    return answer.ToString("N10");
}