見出し画像

Pythonで高校数学:ベクトルの一次結合,終点の存在範囲

 高校の数学の教科書には「一次結合」という用語は出てこない。「ベクトルの終点の存在範囲」となっている。見出し画像で,s,t が条件 s+t=1 を満たしながら変化するときのベクトルpの終点の存在する範囲が直線になる,という話。
 これは,「直線のベクトル方程式」につながるのだが,生徒が苦手とするところだ。ここはやはり実際に動かして,イメージを持つほうがいいだろう。
 こんな感じで動かす。はじめは見出し図の状態。スライダを動かすと

画像1

画像2

画像3

 いろいろ動かしていると,ベクトルpの終点が直線上にありそうだとわかる。そこで,軌跡ON/OFF ボタンをクリックすると,軌跡:直線が表示される。

画像4

 ということで,Tkinter で作成。matplotlib では,スライダで s を動かしたときの図の再描画がうまくいかなかった。
 矢線は matplotlib でも,Tkinter でも描ける。しかし,描いた図を操作するのに,Tkinter は,座標も属性も変更できるが,matlinplot ではどうすればいいかわからない。2次関数のグラフのときは set_xdata でできたのだが。
 ともかく,Tkinter ならできる。
とはいえ結構多くの図を描くことになる。
(1) ベクトルa,b
(2) ベクトルc :あとで位置が変わるので,タグを付けておく。
(3) a,b に係数 s, 1-s をかけたベクトル
(4) ベクトルを表す文字表示
(5) ベクトルの和を表すときの補助線
(6) 存在範囲の直線
(7) スライダと,そのコールバック関数
(8) ボタン都,そのコールバック関数

いくつか要点を。

・ベクトルの計算をするので,成分はリストではなく,numpy の配列にする。
 たとえば,

a = np.array([3, 1])
b = np.array([1, 2])
s = 0.5
c = s*a + (wa-s)*b

  のように計算する。wa は s+t の値。初期値は1。
・Tkinter は左上が原点 (0,0) なので,上下を逆転する。また,50dpi が座標平面の1に相当するように位置を計算する。
・(4) で,Tkinter では TeX書式が使えないようなので,ベクトルの矢印は別で描く。

このくらいか。あとは,ひたすらコーディングする。たとえば,ベクトルaに係数sをかけたベクトルは

canvas.create_line(
      orgx, orgy, orgx+s*a[0]*50, orgy-s*a[1]*50,
      arrow=tk.LAST,
      width=3,
      fill='blue',
      tag="aa"
  )

org は原点。arrow=tk.LAST で矢線にする。fill は描画色。

ベクトル a, b の成分と,s+t の値は簡単にかえられるようにしておく。これを 2 にすると

画像5

2分の1にすると

画像6

といった調子。実際に動かしている様子を動画にした。

全ソースコードを示す。130行近くある。

import numpy as np
import tkinter as tk
root = tk.Tk()
root.title('ベクトルの終点の存在領域')
root.geometry("450x450")
canvas = tk.Canvas(root, width=400, height=300, bg="white")
a = np.array([3, 1])
b = np.array([1, 2])
s = 0.5
# s+t=wa 初期値は 1
wa = 1
#原点
orgx = 130
orgy = 280
# === 以下は特に変えなくてもよい =====
c = s*a + (wa-s)*b
locus1 = 10*a + (wa-10)*b
locus2 = -9*a + (wa+9)*b
#式の表示
canvas.create_text(155, 35, text="→    →    → ", font=('', 24))
canvas.create_text(200, 50, text="p=s a+t b (s+t = "+str(wa)+")", font=('', 24))
# a, b の表示
canvas.create_line(
      orgx, orgy, orgx+a[0]*50, orgy-a[1]*50, 
      arrow=tk.LAST, 
  )
canvas.create_line(
      orgx, orgy, orgx+b[0]*50, orgy-b[1]*50, 
      arrow=tk.LAST, 
  )
canvas.create_text(orgx+a[0]*50, orgy-a[1]*50+10, text="→", font=('', 20))
canvas.create_text(orgx+a[0]*50, orgy-a[1]*50+20, text="a", font=('', 20))
canvas.create_text(orgx+b[0]*50-30, orgy-b[1]*50+5, text="→", font=('', 20))
canvas.create_text(orgx+b[0]*50-30, orgy-b[1]*50+20, text="b", font=('', 20))
# c などの表示。タグ付き
canvas.create_line(
      orgx, orgy, orgx+s*a[0]*50, orgy-s*a[1]*50, 
      arrow=tk.LAST, 
      width=3, 
      fill='blue', 
      tag="aa"
  )
canvas.create_line(
      orgx, orgy, orgx+(wa-s)*b[0]*50, orgy-(wa-s)*b[1]*50, 
      arrow=tk.LAST, 
      width=3, 
      fill='blue', 
      tag="bb"
  )
canvas.create_line(
      orgx, orgy, orgx+c[0]*50, orgy-c[1]*50, 
      arrow=tk.LAST, 
      tag="vc"
  )
canvas.create_text(orgx+c[0]*50+10, orgy-c[1]*50-10, 
                  text="→", 
                  font=('', 20), 
                  tag="vcarrow"
                 )
canvas.create_text(orgx+c[0]*50+10, orgy-c[1]*50, 
                  text="p", 
                  font=('', 20), 
                  tag="vcstr"
                 )
canvas.create_line(
      orgx+s*a[0]*50, orgy-s*a[1]*50, 
      orgx+c[0]*50, orgy-c[1]*50, 
      orgx+(wa-s)*b[0]*50, orgy-(wa-s)*b[1]*50, 
      dash=(5, 5), 
      fill='blue', 
      tag="guide"
  )
canvas.create_line(
      orgx+locus1[0]*50, orgy-locus1[1]*50, 
      orgx+locus2[0]*50, orgy-locus2[1]*50, 
      dash=(5, 5), 
      fill='white', 
      tag="locus"
  )
# ベクトルなどの書き換え
def drawvec(n):
   s = float(n)
   c = s*a + (wa-s)*b
   x0 = orgx
   y0 = orgy
   xa = orgx + s*a[0]*50
   ya = orgy - s*a[1]*50
   xb = orgx + (wa-s)*b[0]*50
   yb = orgy - (wa-s)*b[1]*50
   xc = orgx + c[0]*50
   yc = orgy - c[1]*50
   canvas.coords("aa", x0, y0, xa, ya)
   canvas.coords("bb", x0, y0, xb, yb)
   canvas.coords("vc", x0, y0, xc, yc)
   canvas.coords("vcarrow", xc+10, yc-10)
   canvas.coords("vcstr", xc+10, yc)
   canvas.coords("guide", xa, ya, xc, yc, xb, yb)
# 軌跡 ON/OFF
flag = True
def locus(enent):
   global flag
   if flag:
       canvas.itemconfig("locus", fill='red')
   else:
       canvas.itemconfig("locus", fill='white')
   flag = not flag
# スライダを作る
sc = tk.Scale(from_=-3, to=3, 
             length= 200, 
             orient="horizontal", 
             variable=tk.DoubleVar(value=0.5), 
             resolution=0.01, 
             label="sの値", 
             command=drawvec
            )
# ボタンを作る
btn = tk.Button(text="軌跡 ON/OFF")
#配置
canvas.place(x=20, y=20)
sc.place(x=90, y=350)
btn.place(x=300, y=350)
btn.bind("<ButtonPress>", locus)
root.mainloop()