屈折の法則と光路の最短経路について

# はじめに
光は、空間を通るとき必ず最短の距離だけ移動し目的地まで到達するという性質がある。ある点からある点に光が通るとき、その軌跡を考える。同一の性質がある空間（今回の場合は、絶対屈折率が同じとする。）を光が通るとき、光路は最短距離である直線となる。しかし、絶対屈折率の異なる2つの空間（境界は直線とする）を斜め方向に光が伝播する場合、境界で光路が折れ曲がる屈折という現象が生じる。

例えば、屈折率が$n_{12}$倍の空間に入射する場合、光速は$c_n=\frac{c}{n_{12}}$と表すことができる。（ただし$c$は入射する前の空間における光速である)
これは、光からすれば、実際の長さの$n_{12}$倍の長さの空間を伝播するのと同じである。

この考え方によれば、光から見れば最短の直線距離を移動しているように感じるが、観測者から見れば空間の境界部で屈折しているように見える。

そこで、今回は2点間の光路を表現できるプログラムを作成した。具体的には、2点間の道のりの長さを最小化する経路を探索するプログラムである。屈折率の違いは目的関数に重みを掛けることで再現した。

![output3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1686019/b19929e6-a1f5-4581-a353-1c97d3367146.gif)




# 光路の性質
光は、空間を通るとき常に’光から見て’最短経路を通る。したがって、均一の空間では光路は最短経路である直線になる。しかし、2つの屈折率が異なる空間を通る場合は最も移動時間が短くなる‘最短経路‘を通るが観測者から見れば屈折したように見える。

例として、前半の空間の絶対屈折率が1で後半の空間の絶対屈折率が2の場合を考える。移動時間を最小化するためには、絶対屈折率が2という光速が半分になってしまう空間内での移動はできるだけ小さくすることが望ましい。なので、単純な直線経路ではなく、光速が小さくなる空間での移動距離を小さくするような屈折した経路を通る。

# プログラム
以下の記事のプログラムを改造する。

https://qiita.com/arairuca/items/fdd5449e49d8805a5bd9

具体的には、$xy$平面において$(0,0)$から$(1,1)$までの光路について考える。$x=0.5$付近で屈折率は変化し、2つの空間の相対屈折率$n_{12}$とする。

つまり、同じ長さ$L$を進むにしても、$x<0.5$では、$L$の長さに光は感じているが、$x\ge 0.5$では$2L$の長さに感じている。それを目的関数の重みとして表現し、目的関数を最小化する経路を考える。

```python c_opt.py
import numpy as np
import matplotlib.pyplot as plt
import japanize_matplotlib
import math
import matplotlib.animation as animation


#空間1,2に対する相対屈折率
n_12=1.0

## アニメーション用の設定
fig = plt.figure()
plt.xlim(0,1)
plt.ylim(0,1)


ims = []

m=100

h=10e-5
alpha=0.015
n=10
x=np.linspace(0,1, n)
y1=np.random.rand(n-2)

y0=np.array([0])
y2=np.array([1])
y=np.concatenate([y0,y1], axis=0)
y=np.concatenate([y,y2], axis=0)
#print(y)
# plt.plot(x, y, marker='o', linestyle='-', color='b', label='データ')

# plt.show()


def error(y):

  sum=0


  for i in range(1,n):
    dx=x[i]-x[i-1]
    dy=y[i]-y[i-1]
    d=math.sqrt(dx**2+dy**2)
    if i>= int(n/2):
      sum=sum+n_12*d
    else:
      sum=sum+d
  return sum

k_ary=[]
results=[]

for k in range(m):  
  d_errs=[]
  err=error(y)
  for i in range(1,n-1):
    y[i]=y[i]+h
    err2=error(y)
    d_err=(err2-err)/h
    d_errs.append(d_err)
    y[i]=y[i]-h

  #print(d_errs)
  d_errs=np.concatenate([y0,d_errs,y0], axis=0)
  y=y-np.array(d_errs)*alpha

  # ## アニメーション用のプロット
  im = plt.plot(x, y, marker='o', linestyle='-', color='b')            
  ims.append(im)

  results.append(error(y))
  k_ary.append(k)


# 目的関数の推移のグラフ化
# plt.plot(k_ary,results)
# plt.xlabel('試行回数')
# plt.ylabel('目的関数（総距離）')
# plt.savefig("目的関数.png")
# plt.show()

# 10枚のプロットを 100ms ごとに表示
ani = animation.ArtistAnimation(fig, ims, interval=100)
#plt.show()
ani.save("output3.gif", writer="imagemagick")
```

# 結果
## 均一の空間の場合
$n_{12}=1$の場合の結果を以下に示す。



![output2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1686019/9831a6bb-fdd1-46e6-91a2-e2c7c590517c.gif)


当たり前ではあるが最適化された経路は直線$y=x$になる。
ところが、屈折率が異なる場合は以下のように挙動が異なる。

## 屈折率が異なる空間の場合
$n_{12}=2.0$とする。つまり、光にとって最初は速く移動できるが後半は遅く移動しなければならない。つまり、如何に前半の空間で移動距離を稼げるかが勝負なのである。

そのことを考慮して以下のようにプログラムを実行した結果を吟味する。

![output3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1686019/b19929e6-a1f5-4581-a353-1c97d3367146.gif)

確かに、光路は境界面である$x=0.5$付近で屈折しているのが分かる。屈折の考え方は色々あるが、筆者は’光から見た最短経路’の解釈が好きである。

# まとめ
屈折の法則は、物理選択の高校生にとっては難しい概念である。何故なら、教科書や参考書の説明は複雑な幾何学を用いているからである。また、最短経路から屈折の現象を数学的に説明する場合は、高度な解析学が必要になる（参考文献参照）。なので、今回は感覚的に屈折の法則をイメージできるようなプログラムを作成した。


# 参考文献

https://manabitimes.jp/math/955





# まとめ