問題文
平面上の点 $\mathrm{P,Q,R}$ が同一直線上にないとき、それらを3頂点とする三角形の面積を $\triangle{\mathrm{PQR}}$ で表す。また、 $\mathrm{P,Q,R}$ が同一直線上にあるときは、 $\triangle{\mathrm{PQR}}=0$ とする。
$\mathrm{A,B,C}$ を平面上の3点とし、 $\triangle{\mathrm{ABC}}=1$ とする。この平面上の点 $\mathrm{X}$ が $$2 \leqq \triangle{\mathrm{ABX}}+\triangle{\mathrm{BCX}}+\triangle{\mathrm{CAX}} \leqq 3$$ を満たしながら動くとき、 $\mathrm{X}$ の動きうる範囲の面積を求めよ。
(2020 東京大学 理系第2問)
$$\dfrac{15}{2}$$
※ この答えは学校側が公表したものではありません。個人が作成した非公式の答えです。
コード
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as ani
import matplotlib.patches as patches
import matplotlib.gridspec as gridspec
#定数たち
lw = 1.5
lc = "lightslategrey"
colors = [(1.0,0.7,0.4),(0.4,1.0,0.7),(0.7,0.4,1.0)]
hatches = ["\\\\\\","|||","///"]
left = -1.1
right = 2.1
bottom = -0.7
top = 1.3
height = 0.25
#紙の準備
fig = plt.figure()
fig.suptitle("2020東大数学 理系第2問",
color="0.5",x=0.97,y=0.95,ha="right",va="center")
gs = gridspec.GridSpec(4,1)
#図形を描く部分
ax1 = fig.add_subplot(gs[:3,0])
ax1.set_xlim(left,right)
ax1.set_ylim(bottom,top)
ax1.set_aspect("equal")
ax1.axis("off")
#面積メーターと解説の部分
ax2 = fig.add_subplot(gs[3,0])
ax2.set_xlim(0.0,3.0)
ax2.set_ylim(0.0,0.6)
ax2.set_aspect("equal")
plt.gca().spines['top'].set_visible(False)
plt.gca().spines['right'].set_visible(False)
plt.gca().spines['bottom'].set_visible(False)
plt.gca().spines['left'].set_visible(False)
ax2.yaxis.set_visible(False)
ax2.tick_params(colors=lc,width=lw,labelsize=12)
#点と線と三角形
a = np.array([0.7,0.6])
b = np.array([0.0,0.0])
c = np.array([1.0,0.0])
X, = ax1.plot([],[],marker="o",color="steelblue",zorder=10)
Xt, = ax1.plot([],[],color="dodgerblue",alpha=0.4,lw=4.5,
solid_capstyle="butt",solid_joinstyle="bevel",zorder=-10)
XA, = ax1.plot([],[],color=lc,lw=lw,solid_capstyle="round")
XB, = ax1.plot([],[],color=lc,lw=lw,solid_capstyle="round")
XC, = ax1.plot([],[],color=lc,lw=lw,solid_capstyle="round")
abc,= ax1.plot([a[0],b[0],c[0],a[0]],[a[1],b[1],c[1],a[1]],color=lc,lw=lw,
solid_capstyle="round",solid_joinstyle="round")
ABC = patches.Polygon([a,b,c],fc="0.9")
ABX = patches.Polygon([(0,0)],fill=False,lw=0,ec=colors[0],hatch=hatches[0])
BCX = patches.Polygon([(0,0)],fill=False,lw=0,ec=colors[1],hatch=hatches[1])
CAX = patches.Polygon([(0,0)],fill=False,lw=0,ec=colors[2],hatch=hatches[2])
for triangle in [ABC,ABX,BCX,CAX]:
ax1.add_patch(triangle)
objs_to_hide = [X,XA,XB,XC,ABX,BCX,CAX]
#杏仁豆腐
annin = []
for i in range(-4,6):
annin += ax1.plot([left,right],[0.3*i,0.3*i],color="0.6",lw=0.6,alpha=0.0)
annin += ax1.plot([left,right],[6/7*(left-0.5*i),6/7*(right-0.5*i)],color="0.6",lw=0.6,alpha=0.0)
annin += ax1.plot([left,right],[-2*(left-0.5*i),-2*(right-0.5*i)],color="0.6",lw=0.6,alpha=0.0)
hex1 = patches.Polygon([(3*a-c)/2,(3*b-c)/2,(3*b-a)/2,
(3*c-a)/2,(3*c-b)/2,(3*a-b)/2],
fill=False,lw=lw,ec=lc,joinstyle="round",alpha=0.0)
hex2 = patches.Polygon([2*a-c,2*b-c,2*b-a,
2*c-a,2*c-b,2*a-b],
fill=False,lw=lw,ec=lc,joinstyle="round",alpha=0.0)
for hexagon in [hex1,hex2]:
ax1.add_patch(hexagon)
annin += [hex1,hex2]
#面積メーター
bars = []
for i in range(3):
bar = patches.Rectangle((0,0),0,height,fill=False,ec=colors[i],hatch=hatches[i])
bars.append(bar)
waku = patches.Rectangle((0,0),0,height,fill=False,lw=lw,ec=lc,clip_on=False)
for obj in bars+[waku]:
ax2.add_patch(obj)
tates = []
for i in range(2):
tate,= ax2.plot([],[],color=lc,lw=lw)
tates.append(tate)
barname = ax2.text(1.5,height+0.15,"面積メーター",ha="center",
size=12,color=lc,fontfamily="HGSoeiKakupoptai")
objs_to_hide += bars + tates + [waku,barname]
objs_to_hide += ax2.xaxis.get_majorticklines()
objs_to_hide += ax2.xaxis.get_majorticklabels()
#解説パート
kaisetsu = []
fdict = dict(size=16,color="0.2",fontfamily="Meiryo",alpha=0.0)
kaisetsu += [ax2.text(0.0,0.3,"塗りつぶした面積",fontdict=fdict)]
kaisetsu += [ax2.text(1.5,0.3,"30枚",fontdict=fdict)]
kaisetsu += [ax2.text(0.0,0.0,"中央にある三角形",fontdict=fdict)]
kaisetsu += [ax2.text(1.66,0.0,"4枚",fontdict=fdict)]
kaisetsu += [ax2.annotate("",xy=(2.1,0.05),xytext=(2.1,0.35),
arrowprops=dict(arrowstyle="<|-",lw=lw,color="0.3",alpha=0.0,
connectionstyle="bar,fraction=-1.2")).arrow_patch]
kaisetsu += [ax2.text(2.6,0.15,"$\\dfrac{15}{2}\\:$倍",fontdict=fdict,color="navy")]
#点を動かす関数
def moving_X(i,daikei):
O,v1,v2,s1,s2,T = daikei
k = i/(T-1)
s = (s1+s2)/2+(s2-s1)/2*np.cos(T/10*np.pi*k)
x = O+s*((1-k)*v1+k*v2)
return x
#三角形を動かす関数
def update_triangle(x):
XA.set_data([x[0],a[0]],[x[1],a[1]])
XB.set_data([x[0],b[0]],[x[1],b[1]])
XC.set_data([x[0],c[0]],[x[1],c[1]])
ABX.set_xy([a,b,x])
BCX.set_xy([b,c,x])
CAX.set_xy([c,a,x])
#面積を塗りつぶす関数
def trajectory_X(x):
X.set_data(x[0],x[1])
xs = np.append(Xt.get_data()[0],x[0])
ys = np.append(Xt.get_data()[1],x[1])
Xt.set_data(xs,ys)
#面積メーターを動かす関数
def update_bar(x):
Sabc = 1/2*np.cross(a-b,a-c)
Sabx = 1/2*np.abs(np.cross(x-a,x-b))/Sabc
Sbcx = 1/2*np.abs(np.cross(x-b,x-c))/Sabc
Scax = 1/2*np.abs(np.cross(x-c,x-a))/Sabc
bars[0].set_width(Sabx)
bars[1].set_x(Sabx)
bars[1].set_width(Sbcx)
bars[2].set_x(Sabx+Sbcx)
bars[2].set_width(Scax)
waku.set_width(Sabx+Sbcx+Scax)
tates[0].set_data([Sabx,Sabx],[0,height])
tates[1].set_data([Sabx+Sbcx,Sabx+Sbcx],[0,height])
#お気に入りのイージング関数
def easing(x):
if x<0.5:
return 2*x**2
else:
return 1-(-2*x+2)**2/2
#台本
daihon = [
(a,a-(7*b+8*c)/15,a-c,0.5,1.0,140),
(c,a-c,b-c,1.5,2.0,700),
(b,b-c,b-a,0.5,1.0,300),
(a,b-a,c-a,1.5,2.0,700),
(c,c-a,c-b,0.5,1.0,300),
(b,c-b,a-b,1.5,2.0,700),
(a,a-b,a-(7*b+8*c)/15,0.5,1.0,160)
]
periods = [d[-1] for d in daihon]+[100]*2+[40]*6+[60]
ends = np.cumsum(periods)
ends = np.append(0,ends)
#上演
def update(i):
phase = np.argmin(~(i<ends))-1
if i<ends[7]:
daikei = daihon[phase]
x = moving_X(i-ends[phase],daikei)
update_triangle(x)
trajectory_X(x)
update_bar(x)
elif i<ends[8]:
alpha = 1-easing((i-ends[phase])/(periods[phase]-1))
for obj in objs_to_hide:
obj.set_alpha(alpha)
elif i<ends[9]:
alpha = easing((i-ends[phase])/(periods[phase]-1))
for obj in annin:
obj.set_alpha(alpha)
elif i<ends[-2]:
alpha = easing((i-ends[phase])/(periods[phase]-1))
kaisetsu[phase-9].set_alpha(alpha)
else:
pass
mov = ani.FuncAnimation(fig,update,ends[-1],interval=50)
plt.show()