統計検定２級の問題をプログラムで検証してみた

# 背景

統計検定２級の問題で、２枚のコインを天秤で量る場合の誤差を考える問題がありました。
（統計検定２級受験予定で、この問題を解いてない方は、答えを見ないでくださいね。）

天秤の測定誤差が、平均０、分散 $ σ^2 $、となる場合、
１枚ずつ測定すると、測定誤差は、そのまま、平均０、分散$ σ^2 $となるが、
２枚の和と差を測定し、その連立方程式を解いて、それぞれ１枚の重さを計算すると、
平均０、分散 $ \frac{σ^2}{2} $ になるという問題です。

これを、プログラムで検証してみました。

# 準備
まず、以下の準備。
* numpy のインポート
* 正規分布をランダム出力する
* 平均、標準偏差、分散を出力する関数
* コインの重さや標準偏差、サンプルサイズ

```python
import numpy as np
rng = np.random.default_rng()

# 平均、標準偏差、分散を出力する関数
def print_mean_std(title,np_list):
  mean = np.nanmean(np_list)
  std  = np.nanstd(np_list)
  var  = np.var(np_list)
  print(f'{title} mean={mean:.2f} std={std:.2f} var={var:.2f}')
  
weitgt_A = 10 # コインAの重さ
weight_B = 11 # コインBの重さ
sigma    = 1  # 天秤で量ったときの標準偏差
n        = 10000
```

# 普通に量った場合

```python
np_weight_A_list = rng.normal(weitgt_A,sigma,n)
print_mean_std('普通に量った場合 コインA',np_weight_A_list)
np_weight_B_list = rng.normal(weight_B,sigma,n)
print_mean_std('普通に量った場合 コインB',np_weight_B_list)
```

```
普通に量った場合 コインA mean=10.01 std=0.99 var=0.99
普通に量った場合 コインB mean=10.99 std=1.00 var=1.00
```
測定誤差は、天秤の測定誤差（分散$ σ^2 $）そのままの１となります。

# ２枚の和と差を測定した場合
２枚のコインの重さの和(X)と差(Y)は、
```math
\begin{align}
X = a + b\\
Y = a - b
\end{align}
```
となり、
```python
X  = rng.normal(weitgt_A+weight_B,sigma,n)
print_mean_std('コインAとコインBの和',X)
Y = rng.normal(weitgt_A-weight_B,sigma,n)
print_mean_std('コインAとコインBの差',Y)
```
```
コインAとコインBの和 mean=20.99 std=1.00 var=0.99
コインAとコインBの差 mean=-1.00 std=1.00 var=1.00
```
測定誤差は、和でも差でも、天秤の測定誤差（分散$ σ^2 $）そのままの１となります。

## 和と差から、それぞれ１枚の重さを計算
２枚のコインの重さの和(X)と差(Y)から、それぞれのコインの重さは、
```math
\begin{align}
a = \frac{X + Y}{2} \\
b = \frac{X - Y}{2}
\end{align}
```
で求まるので、測定誤差は、を求めると、
```python
np_weight_A_list_2 = ( X + Y ) / 2
print_mean_std('和と差から求めた場合 コインA',np_weight_A_list_2)
np_weight_B_list_2 = ( X - Y ) / 2
print_mean_std('和と差から求めた場合 コインB',np_weight_B_list_2)
```

```
和と差から求めた場合 コインA mean=10.00 std=0.71 var=0.50
和と差から求めた場合 コインB mean=10.99 std=0.71 var=0.50
```
理論値の通り、測定誤差（分散）が、１枚ずつ測定するときの、1/2になりました。

# ３枚を３回量る場合を考えると、、、
２枚の和と１枚の差（引き算）を３回量ることにしました。以下のように、X,Y,Zを定義して、
```math
\begin{align}
X = - a + b + c\\
Y =   a - b + c\\
Z =   a + b - c
\end{align}
```
で３回量ると、

連立方程式の解答は省略して、答えをズバリ
```math
\begin{align}
a = \frac{Y + Z}{2}\\
b = \frac{Z + X}{2}\\
c = \frac{X + Y}{2}
\end{align}
```
２枚を２回量ったときと同じになり、このやり方では、うまくいかないことがわかりました。（２枚を２回量ったときと測定誤差が変わらない。）

# ４枚を４回量る場合を考えると、、、
２枚の和と差の、和と差を量ることにしました。具体的には、（aとbの和と差）と（cとdの和と差）の和と差を考え、以下のように、X,Y,Z,Wを定義します。
```math
\begin{align}
X = ( a + b ) + ( c + d )\\
Y = ( a + b ) - ( c + d )\\
Z = ( a - b ) + ( c - d )\\
W = ( a - b ) - ( c - d )
\end{align}
```

連立方程式の解答は省略して、答えをズバリ
```math
\begin{align}
a = \frac{X + Y + Z + W}{4}\\
b = \frac{X + Y - Z - W}{4}\\
c = \frac{X + Y + Z - W}{4}\\
d = \frac{X + Y - Z + W}{4}
\end{align}
```

となり、１枚ずつ測定するときの、1/4になりました。これもプログラムで試してみました。
再度、４枚分のコインの重さや標準偏差、サンプルサイズを定義します。
```python
## ４枚を４回量る
weitgt_A = 10 # コインAの重さ
weight_B = 11 # コインBの重さ
weitgt_C = 12 # コインCの重さ
weight_D = 13 # コインDの重さ
sigma    = 1  # 天秤で量ったときの標準偏差
n        = 10000
```
## 普通に量った場合

```python
# 普通に量った場合
np_weight_A_list = rng.normal(weitgt_A,sigma,n)
print_mean_std('普通に量った場合 コインA',np_weight_A_list)
np_weight_B_list = rng.normal(weight_B,sigma,n)
print_mean_std('普通に量った場合 コインB',np_weight_B_list)
np_weight_C_list = rng.normal(weitgt_C,sigma,n)
print_mean_std('普通に量った場合 コインC',np_weight_C_list)
np_weight_D_list = rng.normal(weight_D,sigma,n)
print_mean_std('普通に量った場合 コインD',np_weight_D_list)
```
```
普通に量った場合 コインA mean=10.00 std=1.00 var=1.01
普通に量った場合 コインB mean=11.00 std=0.99 var=0.97
普通に量った場合 コインC mean=12.01 std=1.00 var=1.00
普通に量った場合 コインD mean=13.00 std=1.00 var=0.99
```
測定誤差は、天秤の測定誤差（分散$ σ^2 $）そのままの１となります。

## ２枚の和と差の、和と差を量った場合

```python
np_weight_X  = rng.normal(weitgt_A+weight_B+weitgt_C+weight_D,sigma,n)
print_mean_std('X',np_weight_X)
np_weight_Y  = rng.normal(weitgt_A+weight_B-weitgt_C-weight_D,sigma,n)
print_mean_std('Y',np_weight_Y)
np_weight_Z  = rng.normal(weitgt_A-weight_B+weitgt_C-weight_D,sigma,n)
print_mean_std('Z',np_weight_Z)
np_weight_W  = rng.normal(weitgt_A-weight_B-weitgt_C+weight_D,sigma,n)
print_mean_std('W',np_weight_W)
```

```
X mean=46.01 std=0.99 var=0.98
Y mean=-4.00 std=1.00 var=1.00
Z mean=-1.99 std=1.00 var=1.00
W mean=-0.02 std=1.00 var=1.00
```
測定誤差は、２枚の和と差の、和と差でも、天秤の測定誤差（分散$ σ^2 $）そのままの１となります。

## ２枚の和と差の、和と差から、それぞれ１枚の重さを計算
コイン１枚の測定誤差を求めると、
```python
np_weight_A_list_4 = ( np_weight_X + np_weight_Y + np_weight_Z + np_weight_W ) / 4
print_mean_std('２枚の和と差の、和と差から求めた場合 コインA',np_weight_A_list_4)
np_weight_B_list_4 = ( np_weight_X + np_weight_Y - np_weight_Z - np_weight_W ) / 4
print_mean_std('２枚の和と差の、和と差から求めた場合 コインB',np_weight_B_list_4)
np_weight_C_list_4 = ( np_weight_X - np_weight_Y + np_weight_Z - np_weight_W ) / 4
print_mean_std('２枚の和と差の、和と差から求めた場合 コインC',np_weight_C_list_4)
np_weight_D_list_4 = ( np_weight_X - np_weight_Y - np_weight_Z + np_weight_W ) / 4
print_mean_std('２枚の和と差の、和と差から求めた場合 コインD',np_weight_D_list_4)
```

```
２枚の和と差の、和と差から求めた場合 コインA mean=10.00 std=0.50 var=0.25
２枚の和と差の、和と差から求めた場合 コインB mean=11.01 std=0.50 var=0.25
２枚の和と差の、和と差から求めた場合 コインC mean=12.01 std=0.50 var=0.25
２枚の和と差の、和と差から求めた場合 コインD mean=13.00 std=0.50 var=0.25
```
理論値の通り、測定誤差（分散）が、１枚ずつ測定するときの、1/4になりました。

# 感想
連立方程式を解くのが面倒なので、これ以上は計算しませんが、おそらく、2のN乗のコインの重さを量るときに、測定誤差を低減できるものと思います。（実際はやらないと思いますが、スゴイ技ですね。）