144. 一束激光的多次反射(Investigating multiple reflections of a laser beam)

激光物理学中,白腔指的是一个使激光束发生延迟的镜面系统。激光进入镜面系统后,最终会不断反射并重新射出。本题考察的白腔系统是一个椭圆,其方程为\(4x^2+y^2=100\)。在椭圆顶端割去了\(-0.01\le x\le+0.01\)的区域,使得激光能够进入和离开白腔。

p144_1

本题中,激光从白腔外的点\((0.0,10.1)\)发出,首次接触镜面的位置是\((1.4,-9.6)\)。每当激光击中椭圆表面时,它遵循反射定律入射角等于反射角,也就是说,入射光线和反射光线在入射点和法线的夹角相等。在上图中,红线表示的是激光前两次击中镜面的过程;蓝线表示的是激光第一次击中镜面时入射点的切线。

对于椭圆的任意点\((x,y)\),其切线的斜率\(m\)满足:\(m = -4x/y\),法线垂直于入射点的切线,下图的动画展现了激光束前十次反射的路径:

p144_2

求在激光离开白腔之前,它在椭圆的内表面上击中了多少次?

分析:这是一道有趣的光学问题,但实际上只需要使用高中的解析几何知识就可以比较容易的解决。基本思路是这样的:由于光线在椭圆内的反射遵守反射定律,即入射角等于反射角,因此当我们知道入射光线的方程后,就可以用它推导出反射光线的方程,再求它和椭圆的交点,累计与椭圆接触的次数。如果交点的横坐标\(|x|\le0.01\)且纵坐标\(y>0\),也即接触到了椭圆的缺口,光线就会射出了,这时候返回已经累计的接触次数即为题目所求。下面我们分两步来分析:第一步是用入射光线来求反射光线、第二步用反射光线来求与椭圆的接触点坐标。

一、求解反射光线

根据高中的解析知识,我们知道假设两条直线的斜率分别为\(k1,k2\),则设两条直线形成的夹角中的锐角为\(\theta\),则有:

$$ tan( \theta ) =\Bigl|\frac{k_{1} -k_{2}}{1+k_{1} k_{2}}\Bigl| $$

在如下示意图中,光线从\(A\)点射入,与椭圆接触于\(B\)点并反射,再与椭圆接触于\(D\)点。过\(B\)点做椭圆的切线,再过\(B\)点做切线的垂线,即为椭圆在\(B\)点的法线,设其为\(n\),则入射线\(i\)与法线\(n\)的夹角即为入射角\(\angle EBF\),反射线\(r\)与法线\(n\)的夹角即为反射角\(\angle FBG\)

假设入射光线的斜率为\(i\),法线的斜率为\(n\),反射线的斜率为\(r\),则易知:

$$ i=\frac{y_{1} -y_{0}}{x_{1} -x_{0}} ,\ n=\frac{-1}{-4x/y} =\frac{y}{4x} $$

则入射角与反射角的正切值可以表示为:

$$ tan( \angle EBF) =\frac{i-n}{1+in} ,\ tan( \angle FBG) =\frac{r-n}{1+rn} $$

因为入射解与反射解是相等的,因此相应的正切值也是相等的,则我们可以从入射线与法线的斜率推导出反射线的斜率:

$$ \frac{i-n}{1+in} =\frac{r-n}{1+rn} \Rightarrow r=\frac{-i+2n+in^{2}}{1+2in-n^{2}} $$

已知反射线的斜率且经过\(B(x_1,y_1)\)点,则可推出反射线的方程,设其为\(y=rx+m\),则知截距项\(m=y_1-rx_1\)

二、求反射线与椭圆的交点

已知反射线方程\(y=rx+m\),椭圆方程\(4x^2+y=100\),则将反射线代入椭圆方程可求交点:

$$ 4x^{2} +y^{2} =4x^{2} +( rx+m)^{2} =4x^{2} +r^{2} x^{2} +2rmx+m^{2} =100 $$

则可得二次方程:

$$ \left( 4+r^{2}\right) x^{2} +2rmx+m^{2} -100=0 $$

此二次方程有两个解,对应反射线与椭圆的两个交点,其中一个点是\(B\)点本身,而我们需要求的另外一个交点\(D\)点,根据韦达定理,则上述二次方程的两个解满足:

$$ x_{1} +x_{2} =-\frac{2rm}{4+r^{2}} $$

则我们根据这个关系用\(B\)点横坐标推导出\(D\)点的横坐标。

代码如下:

# time cost = 420 µs ± 322 ns

def next_point(x0,y0,x1,y1):
    i = (y1-y0)/(x1-x0)
    n = y1/(4*x1)
    r = (-i+2*n+i*n**2)/(1+2*i*n-n**2)
    m = y1-r*x1
    x_new = (-2*r*m)/(4+r**2)-x1
    y_new = r * x_new + m
    return x_new,y_new

def main():
    n = 1
    x0,y0,x1,y1 = 0,10.1,1.4,-9.6
    while True:
        x_new,y_new = next_point(x0,y0,x1,y1)
        if abs(x_new)<=0.01 and y_new>0:
            return n
        else:
            x0,y0,x1,y1 = x1,y1,x_new,y_new
            n += 1