1.4.40

1.4.40 #

解答 #

N 个数可组成的三元组的总数为:

$C(N, 3) = N(N-1)(N-2)/3! = ~ (N^3)/6​$ (组合数公式)

$[-M, M]​$ 中随机 $N​$ 次,有 $(2M+1)^N​$ 种随机序列(每次随机都有 $2M+1​$ 种可能)

按照分步计数方法,将随机序列分为和为零的三元组和其余 $N-3​$ 个数

这些序列中,和为零的三元组有 $3M^2 + 3M + 1​$ 种可能。

其他不为零的 $N-3​$ 个位置有 $(2M+1)^{(N-3)}​$ 种可能。

总共有 $((N^3)/6) \times (3M^2 + 3M + 1) \times (2M+1)^{(N-3)}​$ 种可能性

平均值为:

$[((N^3)/6) \times (3M^2 + 3M + 1) \times (2M+1)^{(N-3)}] / (2M+1)^N​$ $=N^3/16M​$

$3M^2 + 3M + 1$ 的推导:

在 $[-M,M]$ 中取三个数,和为零的序列有几个? 假设第一个数取 $0$, 取 $(0, 0, 0)$ 的情况最后加上。

剩下两个数只能以 $0$ 为中心对称配对,总共 $2M / 2$ 种组合。

换成排列数(例如 (0 ,1 ,-1) 和 (0 ,-1 ,1) 是两种不同的排列)就是 $2M$ 种。

加上 $(0, 0, 0)$ 就是 $2M+1$ 种序列。

假设第一个数取 $1$,那么 $M$ 就不能再取(剩下的数不能获得 $M+1$),剩下 $2M$ 个数对称配对。

总共 $(2M)/2 * 2 = 2M$ 种排列。

假设第一个数取 $2$,那么 $M$ 和 $M-1$ 就不能再取,剩下 $2M-1$ 个数配对。

总共 $2M-1$ 种序列。

以此类推,第一个数取 $M $ 时,只能在 $-M$ 到 $0$ 之间配对,总共 $M+1$ 种序列。

$-M$ 到 $-1$ 之间的序列数计算完全一样,于是由等差数列求和公式:

$$ \frac{(M+1+2M)M}{2} \times 2 + 2M+1=3M^2+3M+1 $$

第一项为 $1$ 到 $M$ 之间的序列数,乘上 $2$ 再加上取 $0​$ 时的序列数即为所求的全部序列数。

代码 #

public static class ThreeSum
{
    /// <summary>
    /// 输出所有和为零的三元组。
    /// </summary>
    /// <param name="a">输入数组。</param>
    public static void PrintAll(int[] a)
    {
        var n = a.Length;
        for (var i = 0; i < n; i++)
        {
            Console.WriteLine($"for number \"{a[i]}\"");
            var count = 0;
            for (var j = i + 1; j < n; j++)
            {
                for (var k = j + 1; k < n; k++)
                {
                    if ((long)a[i] + a[j] + a[k] == 0)
                    {
                        Console.WriteLine($"{a[i]} + {a[j]} + {a[k]}");
                        count++;
                    }
                }
            }
            Console.WriteLine($"Count:{count}");
        }
    }

    /// <summary>
    /// 计算和为零的三元组的数量。
    /// </summary>
    /// <param name="a">输入数组。</param>
    /// <returns></returns>
    public static int Count(int[] a)
    {
        var n = a.Length;
        var count = 0;
        for (var i = 0; i < n; i++)
        {
            for (var j = i + 1; j < n; j++)
            {
                for (var k = j + 1; k < n; k++)
                {
                    if ((long)a[i] + a[j] + a[k] == 0)
                    {
                        count++;
                    }
                }
            }
        }
        return count;
    }
}