Python: 熱力学のパズル問題

# 熱力学のパズル問題
ヨビノリさんの動画で紹介されていた熱力学問題について実験してみた。過去問題集があったので張っておきます。

[物理チャレンジ2008(第1チャレンジ理論問題 第5問)](http://www.jpho.jp/images/pdf/riron-q2008-1.pdf)

では、まず問題を確認していきましょう。

# 問題

　断熱性のよい容器A, Bと熱伝導性の高い容器C, Dがある。容器A, Bにはそれぞれ, 80℃のお茶（ただし，比熱は水と等しいとする）1ℓと，20℃の水1ℓが入っている（ℓはリットルを表す単位記号である）。A, Bは容器C, Dを中に入れられる大きさで，中の液体がこぼれることはない。
　これらの容器は, 次のような使い方ができる。
1) 容器Bに入っている水を全て容器Cに移し，容器Cを容器Aに入れる。
2) 熱伝導によって，しばらくすると，容器Aのお茶と，容器Cの水の温度は等しくなる。
　このとき，
$$(移動する熱量) = (物質の量)×（比熱）×（温度変化）$$
とすると，
$$(容器Aの中のお茶が放出した熱量) = (容器Cの中の水が吸収した熱量）$$
という関係が成り立つと考えられる。ただし、ここでは、容器の温度変化のために吸収する熱量は考えないものとし，周囲の環境への熱の放出はないものとしている。

## 問
　容器A～Dを利用した熱伝導の過程だけを用いて，お茶と水を混ぜることなく，最終的な水全体の温度を，最終的なお茶全体の温度より高くすることはできるだろうか。できるとすればその方法の1例を，またできないとすればその理由を述べなさい。

# 考察

　つまり整理すると, 水の一部を容器 C か D に移し，お茶の入った容器 A に入れるという操作を考える。変化前にお茶の温度を $T_n$ , 移した水の量を $a$ ℓとすると, 変化後のお茶の温度 $T_{n+1}$ は以下の計算で求まる。
$$T_{n+1} = (T_n + 20a) / (1 + a)$$

これに対して, pythonで記述し以下のようなシミュレーションを行ってみよう。

```python
import time

def test(n):
    T = 80 # お茶の初期温度
    dn = 1 / n # dn

    st = time.time() # 開始時間（s）
    for _ in range(n):
        T = (T + 20 * dn) / (1 + dn) # T_n = (T_(n-1) + 20dn) / (1 + dn)

    print('実行時間(s)：', time.time()-st, 'お茶の温度：', T, '水の温度：', 100-T) 

test(1)

>>> 実行時間(s)： 0.0 お茶の温度： 50.0 水の温度： 50.0
```

このシミュレーションは, 容器 B の水の $n$ 分割し, 順の容器 C へ移し, お茶の容器 B に入れて温め, 容器 C から容器 D へ移すという作業をシミュレートしている。つまり,上記のような `n = 1` の場合は問題中の以下の状況に等しくなる。

> 1) 容器Bに入っている水を全て容器Cに移し，容器Cを容器Aに入れる。

`n = 2` の時に, ヨビノリさんの解説通りの変化になる。

```python
test(2)

>>> 実行時間(s)： 0.0 お茶の温度： 46.666666666666664 水の温度： 53.333333333333336
```

では, 1000万回くらいでやってみましょう！

```python
test(10000000)

>>> 実行時間(s)： 1.2569599151611328 お茶の温度： 42.07276755714358 水の温度： 57.92723244285642
```

ちなみに私がやったわけではないですが，漸化式として解くと，お茶の温度が$20+60/e$，水の温度が$20+60(1-1/e)$となるようです。コメント欄で解いてくれた人がいました(´っ･ω･)っ 計算結果も載せておきますね。流石に1000万回もやればほぼほぼ一致するんですねぇ。

```python
import math
20 + 60 / math.e, 20 + 60 * (1 - 1 / math.e)
>>> (42.07276647028654, 57.92723352971346)
```

つまり,　一度にお茶に接触させる量を減らせば減らすほど水は温まるという事みたいですね。であれば, 以下のようなの用意して, ガラス管の中を移動している最中に, ガラス管の中の水の温度とお茶の温度が等しくなるみたいなのがあると便利そうだなぁっと思いました。色々と無理がある気がしますが…

![こんなの？.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365961/3da3c5da-13fc-1052-3174-1fa3532ab05f.png)

これも簡単にシミュレートしてみてみました。 つまりは, ガラス管の中の水で一旦冷やされた状態から始まるとして, その後ぽたぽたとガラス管から水が落ちていくと同時にガラス管に新しい水が上から入ってくる。という事は, 何回ぽたぽたするかでどれだけ温度差が生まれるか変わると考えられて, 以降はぽたぽたする回数＝先ほどのシミュレーションの容器を移す回数になると考えられます。とりあえず1000ぽたぽたでやってみました。

```python
import time

def test(n):
    T = 80 # お茶の初期温度
    a = 0.1 # ガラス管の中の水
    T = (T + 20 * a) / (1 + a) # ガラス管の中の水で一旦冷やされた状態
    dn = (1 - a) / n # dn
    st = time.time() # 開始時間（s）
    for _ in range(n):
        T = (T + 20 * dn) / (1 + dn) # T_n = (T_(n-1) + 20dn) / (1 + dn)

    print('実行時間(s)：', time.time()-st, 'お茶の温度：', T, '水の温度：', 100-T) 

test(1000)

>>> 実行時間(s)： 0.0 お茶の温度： 42.185504819894156 水の温度： 57.814495180105844
```

　上であっていたと思っていたのですが…色々と雑だったようです。Step1として, ガラス管に水が満ちた状態から始めるのは問題ないかと思います。その後ですが, ぽたぽたする回数=上から追加される回数ではないので, まず n はStep1後にぽたぽたすることで上から水が追加される回数が正しいです。従って, Step1後に残った上の水を n 分割すればよい。次に温度変化ですが, ガラス管内の水という事が頭から抜けていました。したがって, 温度変化は 1 + dn ではなく, 常に 1 + a で起こるはずです。これらを考慮して書き換えを行ったので追加しておきます。

```python
import time

def test(n, a):
    """ ガラス管を用いたシミュレート

    :param n: 上から追加される回数
    :param a: ガラス管内の水量
    """
    T = 80 # お茶の初期温度

    # Step1:ガラス管の中に水が満ちた状態
    T = (T + 20 * a) / (1 + a)

    # Step2:Step1の状態から上から水が追加され, ぽたぽた落ちる
    dn = (1 - a) / n # dn
    st = time.time() # 開始時間（s）
    for _ in range(n):
        # ガラス管内とお茶の量の和は 1 + a だったので修正
        T = (T * (1 + a - dn) + 20 * dn) / (1 + a)

    print('実行時間(s)：', time.time()-st, 'お茶の温度：', T, '水の温度：', 100-T) 

test(1000, 0.1)

>>> 実行時間(s)： 0.0 お茶の温度： 44.05920507708823 水の温度： 55.94079492291177
```

ガラス管内の水の量がかなり効いてくるようですね。試しに0.01ℓにしてみました。

```python
test(1000, 0.01)

>>> 実行時間(s)： 0.0 お茶の温度： 42.28058125439092 水の温度： 57.71941874560908
```

こんな感じかな(´っ･ω･)っぽたぽた～