起因
有一天畢業多年的大學同學在班級微信群裡問有沒有人能幫忙寫一段程式碼實現一個功能
然後貼了一段要實現的要求
我一看這段描述簡直就頭大了,程式設計師都比較害怕這種沒有格式的文字,甚至連個換行都沒有,說實話多看一眼就感覺莫名煩躁。我也就沒敢講話,即使有同學在群裡已經開始點名了,也始終一言不發。
找上門
後來同學就直接找上我了,雖然畢業6年交集很少,但是大學時關係還是蠻不錯的,所以就答應了。
然後他說明了一下,是他讀大學的弟弟的課程大作業,他弟弟學的是物理專業,要求用編碼完成一個物理作業。發過來一個文件比較詳細的說明了物理作業的內容。
物理大作業:
這個文件其實我也看的一頭霧水,第一是自從高中畢業之後就沒學過物理了,理解起來有點吃力;第二是看不懂所以很多物理知識不能抽象成程式設計程式碼的邏輯。
理解需求
結合他之前發的文件和他弟弟的多次溝通,先把需求弄明白了。
簡單來說就是如下:
隨機取30個點{a,b,c} a,b,c三個點都是0到10中隨機取得
要求a>b
q=a-b m=a+c
對每一個點進行如下操作
已知:
積分qCos(piy/2)dy 積分上下限0-1 + 積分 qe^y*dy 積分上下限1-2 = 1/2mv^2
問題一:求解出v的值
已知:
qvB=m*v^2/2R
問題二:求出R的值
已知:
角速度w=v/R 角度n=wt
若Pi/2<n<Pi,則x=R+RSin(Pi-n)+R*Cos(Pi-n)/Tan(Pi-n)
若n>Pi,則x=2R
若n<Pi/2,則無成績
問題三:取x中的前三最大值。
問題四:
在直角座標系中畫出他們的軌跡:
(x-R)2+y2=R^2
y>0,x<= R+RSin(Pi-n)
y=tan(Pi-n)(x- R+RSin(Pi-n)+RCos(Pi-n)/Tan(Pi-n))
到這裡總算搞明白了需求是什麼了,程式設計師最怕需求不明確,而且遇到水平不行的產品經理,真是遭老罪了。
物理問題轉化成數學問題
雖然搞明白了需求是什麼,但是讓我回憶高中物理知識並正確解答出來也是力不能及的事情。高中時期上知天文下知地理,中間知物理,現在只知道愛理不理。我擅長的是把公式用程式碼表達出來,要是公式都沒有,我也搞不定。
然後我就讓他用把物理題做一遍,有了做題的過程,我就好轉換成程式碼了。很快啊,他就把解題過程給我發過來了。
其中涉及到一些積分的運算,我努力回想多年前學習過的微積分,在記憶的深處找到一些答案。
最後整理出各個問題的解答方式:
問題一:求解出v的值 ,v的計算公式如上
問題二:求出R的值,R的計算公式如上
問題三:取x中的前三最大值。
問題四:在直角座標系中畫出他們的軌跡
寫程式碼
下面就是寫程式碼解決以上問題。知道了解答的過程和公式,接下來就是寫程式碼的過程。
毫無疑問python是最合適完成這個工作的語言,考慮到他的編碼基礎,我要求自己寫的程式碼結構清晰,註釋清楚,函式短小,變數明確,不炫技,不使用高階資料結構。
生成30個隨機數
def gen_30_point():
"""生成30個隨機數,3個為一組"""
point_list = []
while len(point_list) < TOTAL:
a = random.randint(0, 10)
b = random.randint(0, 10)
# 要求a必須大於b,生成隨機數如果a小於等於b,則丟棄該組資料
if a <= b:
continue
c = random.randint(0, 10)
point_list.append((a, b, c))
return point_list
根據隨機數計算出電荷量和電子質量
def calculate_q_m(point_list):
"""根據a,b,c計算出q,m"""
q_m_list = []
for a, b, c in point_list:
q = (a - b) * ELECTRON_CHARGE
m = (a + c) * ELECTRON_QUALITY
q_m_list.append((q, m))
return q_m_list
輕鬆計算出問題一的v
def calculate_v(q, m):
"""計算出速度v"""
v = 2 * q * (2 / PI + E ** 2 - E) / m
v = math.sqrt(v)
return v
計算出問題二的R
def calculate_r(q, m, v):
"""計算出半徑R"""
r = m * v / (2 * B * q)
return r
計算出問題三的x
def calculate_x(n, r):
"""計算出x"""
x = 0
if n > PI:
x = 2 * r
elif n < PI / 2:
x = 0
else:
x = r + r * math.sin(PI-n) + r * math.cos(PI-n)/math.tan(PI-n)
return x
解決問題四的直角座標系下的圖形
def draw_track(n, r):
# 設定圓的引數
radius = r # 半徑
circle_center = (-r, 2) # 圓心座標,例如(2, 3)
# 生成圓的x和y座標
theta = np.linspace(0, n, 100) # 生成0到2π的100個點
x = circle_center[0] + radius * np.cos(theta)
y = circle_center[1] + radius * np.sin(theta)
# 繪製圓
plt.plot(x, y, "k")
# 標記圓心
plt.plot(circle_center[0], circle_center[1], 'ko') # 藍色圓點標記圓心
# 設定座標軸範圍
plt.xlim(circle_center[0] - radius - 2, circle_center[0] + radius + 2)
plt.ylim(circle_center[1] - radius - 2, circle_center[1] + radius + 2)
# 直線
x = np.linspace(-30, x[-1], 20)
y = -x / math.tan(n) + r * math.sin(n) + 2 - r / math.tan(n) + r * math.cos(n) / math.tan(n)
plt.plot(x, y)
# y = 2 橫線
x = np.linspace(-4 * r, 2 * r, 10)
y = np.array([2] * len(x))
plt.plot(x, y, "k:")
# 座標系
new_ticks = np.linspace(-5, 5, 11)
plt.yticks(new_ticks)
ax = plt.gca()
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.spines['bottom'].set_position(('data', 0))
ax.yaxis.set_ticks_position('left')
ax.spines['left'].set_position(('data', 0))
# 隱藏掉y軸的0座標,不然和x軸重了,不好看,0位於從下到上第6個
yticks = ax.yaxis.get_major_ticks()
yticks[5].label1.set_visible(False)
# 設定座標軸標題
plt.title('electronics track')
# 顯示圖形
plt.show()
在繪製直角座標系時發現這個公式存在問題,於是和他討論溝通
最終的效果
最後他拿著程式碼在期末完成了作業的講演,順利過關。
小結
可以從過程中看到,寫程式碼的難度不高,主要是前期理解難度大,不管是物理知識,還是微積分在日常專案開發中真的很少見,更多的是API介面,Redis,MySQL啥的。透過不斷的溝通,正確理解需求,再將物理知識抽象成數學內容,最後用程式碼表達出來,這個過程何嘗不是一次專案開發的縮影呢。
最後,賺到一頓飯😁
附錄完整程式碼
"""
執行方法:
1. 安裝依賴檔案
pip3 install matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple
pip3 install numpy -i https://pypi.tuna.tsinghua.edu.cn/simple
2. 執行程式碼
python3 physics_code.py
題目:
步驟一: (完成)
隨機取30個點{a,b,c} a,b,c三個點都是0到10中隨機取得
要求 a>b, 計算:q=a-b m=a+c
步驟二:求出V (完成)
步驟三:求出R (完成)
步驟四:求出X,取top3 (完成)
步驟五:畫出軌跡 (完成)
"""
import math
import random
import matplotlib.pyplot as plt
import numpy as np
TOTAL = 30
PI = 3.14
E = 2.71
B = 10 ** -4
T = 2 * 10 ** -4
ELECTRON_CHARGE = 1.602 * 10 ** -19
ELECTRON_QUALITY = 1.672 * 10 ** -27
def gen_30_point():
"""生成30個隨機數,3個為一組"""
point_list = []
while len(point_list) < TOTAL:
a = random.randint(0, 10)
b = random.randint(0, 10)
# 要求a必須大於b,生成隨機數如果a小於等於b,則丟棄該組資料
if a <= b:
continue
c = random.randint(0, 10)
point_list.append((a, b, c))
return point_list
def calculate_q_m(point_list):
"""根據a,b,c計算出q,m"""
q_m_list = []
for a, b, c in point_list:
q = (a - b) * ELECTRON_CHARGE
m = (a + c) * ELECTRON_QUALITY
q_m_list.append((q, m))
return q_m_list
def calculate_v(q, m):
"""計算出速度v"""
v = 2 * q * (2 / PI + E ** 2 - E) / m
v = math.sqrt(v)
return v
def calculate_r(q, m, v):
"""計算出半徑R"""
r = m * v / (2 * B * q)
return r
def calculate_n(v, r):
"""計算出n"""
n = v * T / r
return n
def calculate_x(n, r):
"""計算出x"""
x = 0
if n > PI:
x = 2 * r
elif n < PI / 2:
x = 0
else:
x = r + r * math.sin(PI-n) + r * math.cos(PI-n)/math.tan(PI-n)
return x
def draw_track(n, r):
# 設定圓的引數
radius = r # 半徑
circle_center = (-r, 2) # 圓心座標,例如(2, 3)
# 生成圓的x和y座標
theta = np.linspace(0, n, 100) # 生成0到2π的100個點
x = circle_center[0] + radius * np.cos(theta)
y = circle_center[1] + radius * np.sin(theta)
# 繪製圓
plt.plot(x, y, "k")
# 標記圓心
plt.plot(circle_center[0], circle_center[1], 'ko') # 藍色圓點標記圓心
# 設定座標軸範圍
plt.xlim(circle_center[0] - radius - 2, circle_center[0] + radius + 2)
plt.ylim(circle_center[1] - radius - 2, circle_center[1] + radius + 2)
# 直線
x = np.linspace(-30, x[-1], 20)
y = -x / math.tan(n) + r * math.sin(n) + 2 - r / math.tan(n) + r * math.cos(n) / math.tan(n)
plt.plot(x, y)
# y = 2 橫線
x = np.linspace(-4 * r, 2 * r, 10)
y = np.array([2] * len(x))
plt.plot(x, y, "k:")
# 座標系
new_ticks = np.linspace(-5, 5, 11)
plt.yticks(new_ticks)
ax = plt.gca()
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.spines['bottom'].set_position(('data', 0))
ax.yaxis.set_ticks_position('left')
ax.spines['left'].set_position(('data', 0))
# 隱藏掉y軸的0座標,不然和x軸重了,不好看,0位於從下到上第6個
yticks = ax.yaxis.get_major_ticks()
yticks[5].label1.set_visible(False)
# 設定座標軸標題
plt.title('electronics track')
# 顯示圖形
plt.show()
if __name__ == "__main__":
point_list = gen_30_point()
q_m_list = calculate_q_m(point_list)
x_list = []
for q, m in q_m_list:
v = calculate_v(q, m)
r = calculate_r(q, m, v)
n = calculate_n(v, r)
x = calculate_x(n, r)
x_list.append((x, n, r))
print(f"q={q}, m={m}, v={v}, r={r}, n={n}, x={x}")
x_list.sort(reverse=True, key=lambda i : i[0])
for x, n, r in x_list[:3]:
print(f"x中最大的三個值:{x}")
for x, n, r in x_list[:3]:
draw_track(n, r)