import matplotlib.pyplot as plt
import numpy as np
# 1. 그래프 한글 폰트 및 마이너스 기호 설정
plt.rcParams [ 'font.family' ] = 'sans-serif'
plt.rcParams [ 'axes.unicode_minus' ] = False
# ---------------------------------------------------------
# [사용자 입력 부분]
# 직접 입력받고 싶다면 아래 input_data의 주석(#)을 해제하고 사용하세요.
# ---------------------------------------------------------
# print("날짜(t)와 측정값(y)을 공백으로 구분하여 입력하세요 (예: 0, 1, 2, 3...)")
# t_input = list(map(float, input("시간 t (일): ").split()))
# y_input = list(map(float, input("식물 높이 y (cm): ").split()))
# 예시용 샘플 데이터 (사용자가 임의로 수치만 바꾸어 넣으셔도 됩니다)
t_input = np.array ( [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ] ) # 경과 시간 (일)
y_input = np.array ( [ 2.0 , 3.5 , 7.0 , 15.0 , 26.0 , 37.0 , 45.0 , 49.0 , 50.0 ] ) # 식물 높이 (cm)
t_data = np.array ( t_input)
y_data = np.array ( y_input)
# 2. 수2 3차 다항함수로 데이터 피팅 (f(t) = a*t^3 + b*t^2 + c*t + d)
# numpy.polyfit을 사용하여 가장 잘 맞는 3차 다항식 계수를 추정합니다.
coefficients = np.polyfit ( t_data, y_data, 3 )
a, b, c, d = coefficients
# 3. 도함수 f'(t) 계수 구하기 (미분: f'(t) = 3a*t^2 + 2b*t + c)
a_prime = 3 * a
b_prime = 2 * b
c_prime = c
# 4. 연속적인 x축(시간) 구간 생성
t_dense = np.linspace ( min( t_data) , max( t_data) , 500 )
# 3차 생장 함수 f(t) 및 2차 도함수 f'(t) 계산
f_t = a * ( t_dense** 3 ) + b * ( t_dense** 2 ) + c * t_dense + d
f_prime_t = a_prime * ( t_dense** 2 ) + b_prime * t_dense + c_prime
# 5. 최대 성장 속도 시점(도함수의 극대점 / 원함수의 변곡점) 수치적 계산
# 2차함수 꼭짓점 축의 방정식: t = -b_prime / (2 * a_prime)
if a_prime != 0 :
t_max_growth = - b_prime / ( 2 * a_prime)
else :
t_max_growth = t_dense[ np.argmax ( f_prime_t) ]
# 범위 내에 있는지 확인 후 최대 속도 및 그때의 높이 계산
if min( t_data) <= t_max_growth <= max( t_data) :
max_rate = a_prime * ( t_max_growth** 2 ) + b_prime * t_max_growth + c_prime
height_at_max_rate = (
a * ( t_max_growth** 3 )
+ b * ( t_max_growth** 2 )
+ c * t_max_growth
+ d
)
else :
# 관측 범위 밖일 경우 가장 큰 값 선택
idx_max = np.argmax ( f_prime_t)
t_max_growth = t_dense[ idx_max]
max_rate = f_prime_t[ idx_max]
height_at_max_rate = f_t[ idx_max]
# 결과 출력
print( '==================================================' )
print( f'추정된 3차 생장 함수: f(t) = {a:.4f}t³ + {b:.4f}t² + {c:.4f}t + {d:.4f}' )
print( f'도함수 (성장 속도 함수): f\' (t) = {a_prime:.4f}t² + {b_prime:.4f}t + {c_prime:.4f}' )
print( '--------------------------------------------------' )
print( f'🌱 최대 성장 속도 발생 시점 : t = {t_max_growth:.2f} 일째' )
print( f'⚡ 이때의 순간 성장 속도 : {max_rate:.2f} cm/일' )
print( f'📏 이때의 식물 높이 : {height_at_max_rate:.2f} cm' )
print( '==================================================' )
# 6. 시각화 (입력 데이터 점 + 모델링 곡선 + 도함수 곡선)
fig, ( ax1, ax2) = plt.subplots ( 2 , 1 , figsize= ( 10 , 8 ) , sharex= True)
# [그래프 1] 원함수 f(t) & 실제 측정 데이터
ax1.scatter (
t_data,
y_data,
color= 'black' ,
s= 50 ,
zorder= 5 ,
label= 'Measured Data (입력 데이터)' ,
)
ax1.plot (
t_dense,
f_t,
color= 'forestgreen' ,
linewidth= 2.5 ,
label= f'Fitted Model f(t)' ,
)
ax1.axvline (
x= t_max_growth,
color= 'orange' ,
linestyle= '--' ,
label= f'Max Growth Point (t={t_max_growth:.2f})' ,
)
ax1.set_ylabel ( 'Plant Height (cm)' , fontsize= 12 )
ax1.set_title (
'Math II Modeling: User Data Fitting & Growth Rate Analysis' , fontsize= 14
)
ax1.grid ( True, linestyle= ':' , alpha= 0.7 )
ax1.legend ( loc= 'upper left' )
# [그래프 2] 도함수 f'(t) (순간 성장 속도)
ax2.plot (
t_dense,
f_prime_t,
color= 'dodgerblue' ,
linewidth= 2.5 ,
label= "Growth Rate f'(t)" ,
)
ax2.scatter (
[ t_max_growth] ,
[ max_rate] ,
color= 'red' ,
s= 80 ,
zorder= 5 ,
label= f'Max Rate ({max_rate:.2f} cm/day)' ,
)
ax2.axvline ( x= t_max_growth, color= 'orange' , linestyle= '--' )
ax2.set_xlabel ( 'Time t (days)' , fontsize= 12 )
ax2.set_ylabel ( 'Growth Rate (cm/day)' , fontsize= 12 )
ax2.grid ( True, linestyle= ':' , alpha= 0.7 )
ax2.legend ( loc= 'upper right' )
plt.tight_layout ( )
plt.show ( )
aW1wb3J0IG1hdHBsb3RsaWIucHlwbG90IGFzIHBsdAppbXBvcnQgbnVtcHkgYXMgbnAKCiMgMS4g6re4656Y7ZSEIO2VnOq4gCDtj7Dtirgg67CPIOuniOydtOuEiOyKpCDquLDtmLgg7ISk7KCVCnBsdC5yY1BhcmFtc1snZm9udC5mYW1pbHknXSA9ICdzYW5zLXNlcmlmJwpwbHQucmNQYXJhbXNbJ2F4ZXMudW5pY29kZV9taW51cyddID0gRmFsc2UKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgW+yCrOyaqeyekCDsnoXroKUg67aA67aEXQojIOyngeygkSDsnoXroKXrsJvqs6Ag7Iu264uk66m0IOyVhOuemCBpbnB1dF9kYXRh7J2YIOyjvOyEnSgjKeydhCDtlbTsoJztlZjqs6Ag7IKs7Jqp7ZWY7IS47JqULgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIHByaW50KCLrgqDsp5wodCnsmYAg7Lih7KCV6rCSKHkp7J2EIOqzteuwseycvOuhnCDqtazrtoTtlZjsl6wg7J6F66Cl7ZWY7IS47JqUICjsmIg6IDAsIDEsIDIsIDMuLi4pIikKIyB0X2lucHV0ID0gbGlzdChtYXAoZmxvYXQsIGlucHV0KCLsi5zqsIQgdCAo7J28KTogIikuc3BsaXQoKSkpCiMgeV9pbnB1dCA9IGxpc3QobWFwKGZsb2F0LCBpbnB1dCgi7Iud66y8IOuGkuydtCB5IChjbSk6ICIpLnNwbGl0KCkpKQoKIyDsmIjsi5zsmqkg7IOY7ZSMIOuNsOydtO2EsCAo7IKs7Jqp7J6Q6rCAIOyehOydmOuhnCDsiJjsuZjrp4wg67CU6r647Ja0IOuEo+ycvOyFlOuPhCDrkKnri4jri6QpCnRfaW5wdXQgPSBucC5hcnJheShbMCwgMSwgMiwgMywgNCwgNSwgNiwgNywgOF0pICAjIOqyveqzvCDsi5zqsIQgKOydvCkKeV9pbnB1dCA9IG5wLmFycmF5KFsyLjAsIDMuNSwgNy4wLCAxNS4wLCAyNi4wLCAzNy4wLCA0NS4wLCA0OS4wLCA1MC4wXSkgICMg7Iud66y8IOuGkuydtCAoY20pCgp0X2RhdGEgPSBucC5hcnJheSh0X2lucHV0KQp5X2RhdGEgPSBucC5hcnJheSh5X2lucHV0KQoKIyAyLiDsiJgyIDPssKgg64uk7ZWt7ZWo7IiY66GcIOuNsOydtO2EsCDtlLztjIUgKGYodCkgPSBhKnReMyArIGIqdF4yICsgYyp0ICsgZCkKIyBudW1weS5wb2x5Zml07J2EIOyCrOyaqe2VmOyXrCDqsIDsnqUg7J6YIOunnuuKlCAz7LCoIOuLpO2VreyLnSDqs4TsiJjrpbwg7LaU7KCV7ZWp64uI64ukLgpjb2VmZmljaWVudHMgPSBucC5wb2x5Zml0KHRfZGF0YSwgeV9kYXRhLCAzKQphLCBiLCBjLCBkID0gY29lZmZpY2llbnRzCgojIDMuIOuPhO2VqOyImCBmJyh0KSDqs4TsiJgg6rWs7ZWY6riwICjrr7jrtoQ6IGYnKHQpID0gM2EqdF4yICsgMmIqdCArIGMpCmFfcHJpbWUgPSAzICogYQpiX3ByaW1lID0gMiAqIGIKY19wcmltZSA9IGMKCiMgNC4g7Jew7IaN7KCB7J24IHjstpUo7Iuc6rCEKSDqtazqsIQg7IOd7ISxCnRfZGVuc2UgPSBucC5saW5zcGFjZShtaW4odF9kYXRhKSwgbWF4KHRfZGF0YSksIDUwMCkKCiMgM+ywqCDsg53snqUg7ZWo7IiYIGYodCkg67CPIDLssKgg64+E7ZWo7IiYIGYnKHQpIOqzhOyCsApmX3QgPSBhICogKHRfZGVuc2UqKjMpICsgYiAqICh0X2RlbnNlKioyKSArIGMgKiB0X2RlbnNlICsgZApmX3ByaW1lX3QgPSBhX3ByaW1lICogKHRfZGVuc2UqKjIpICsgYl9wcmltZSAqIHRfZGVuc2UgKyBjX3ByaW1lCgojIDUuIOy1nOuMgCDshLHsnqUg7IaN64+EIOyLnOygkCjrj4TtlajsiJjsnZgg6re564yA7KCQIC8g7JuQ7ZWo7IiY7J2YIOuzgOqzoeygkCkg7IiY7LmY7KCBIOqzhOyCsAojIDLssKjtlajsiJgg6ryt7KeT7KCQIOy2leydmCDrsKnsoJXsi506IHQgPSAtYl9wcmltZSAvICgyICogYV9wcmltZSkKaWYgYV9wcmltZSAhPSAwOgogICAgdF9tYXhfZ3Jvd3RoID0gLWJfcHJpbWUgLyAoMiAqIGFfcHJpbWUpCmVsc2U6CiAgICB0X21heF9ncm93dGggPSB0X2RlbnNlW25wLmFyZ21heChmX3ByaW1lX3QpXQoKIyDrspTsnIQg64K07JeQIOyeiOuKlOyngCDtmZXsnbgg7ZuEIOy1nOuMgCDsho3rj4Qg67CPIOq3uOuVjOydmCDrhpLsnbQg6rOE7IKwCmlmIG1pbih0X2RhdGEpIDw9IHRfbWF4X2dyb3d0aCA8PSBtYXgodF9kYXRhKToKICAgIG1heF9yYXRlID0gYV9wcmltZSAqICh0X21heF9ncm93dGgqKjIpICsgYl9wcmltZSAqIHRfbWF4X2dyb3d0aCArIGNfcHJpbWUKICAgIGhlaWdodF9hdF9tYXhfcmF0ZSA9ICgKICAgICAgICBhICogKHRfbWF4X2dyb3d0aCoqMykKICAgICAgICArIGIgKiAodF9tYXhfZ3Jvd3RoKioyKQogICAgICAgICsgYyAqIHRfbWF4X2dyb3d0aAogICAgICAgICsgZAogICAgKQplbHNlOgogICAgIyDqtIDsuKEg67KU7JyEIOuwluydvCDqsr3smrAg6rCA7J6lIO2BsCDqsJIg7ISg7YOdCiAgICBpZHhfbWF4ID0gbnAuYXJnbWF4KGZfcHJpbWVfdCkKICAgIHRfbWF4X2dyb3d0aCA9IHRfZGVuc2VbaWR4X21heF0KICAgIG1heF9yYXRlID0gZl9wcmltZV90W2lkeF9tYXhdCiAgICBoZWlnaHRfYXRfbWF4X3JhdGUgPSBmX3RbaWR4X21heF0KCiMg6rKw6rO8IOy2nOugpQpwcmludCgnPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0nKQpwcmludChmJ+y2lOygleuQnCAz7LCoIOyDneyepSDtlajsiJg6IGYodCkgPSB7YTouNGZ9dMKzICsge2I6LjRmfXTCsiArIHtjOi40Zn10ICsge2Q6LjRmfScpCnByaW50KGYn64+E7ZWo7IiYICjshLHsnqUg7IaN64+EIO2VqOyImCk6IGZcJyh0KSA9IHthX3ByaW1lOi40Zn10wrIgKyB7Yl9wcmltZTouNGZ9dCArIHtjX3ByaW1lOi40Zn0nKQpwcmludCgnLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0nKQpwcmludChmJ/CfjLEg7LWc64yAIOyEseyepSDsho3rj4Qg67Cc7IOdIOyLnOygkCA6IHQgPSB7dF9tYXhfZ3Jvd3RoOi4yZn0g7J287Ke4JykKcHJpbnQoZifimqEg7J2065WM7J2YIOyInOqwhCDshLHsnqUg7IaN64+EICAgICA6IHttYXhfcmF0ZTouMmZ9IGNtL+ydvCcpCnByaW50KGYn8J+TjyDsnbTrlYzsnZgg7Iud66y8IOuGkuydtCAgICAgICAgIDoge2hlaWdodF9hdF9tYXhfcmF0ZTouMmZ9IGNtJykKcHJpbnQoJz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09JykKCiMgNi4g7Iuc6rCB7ZmUICjsnoXroKUg642w7J207YSwIOygkCArIOuqqOuNuOungSDqs6HshKAgKyDrj4TtlajsiJgg6rOh7ISgKQpmaWcsIChheDEsIGF4MikgPSBwbHQuc3VicGxvdHMoMiwgMSwgZmlnc2l6ZT0oMTAsIDgpLCBzaGFyZXg9VHJ1ZSkKCiMgW+q3uOuemO2UhCAxXSDsm5DtlajsiJggZih0KSAmIOyLpOygnCDsuKHsoJUg642w7J207YSwCmF4MS5zY2F0dGVyKAogICAgdF9kYXRhLAogICAgeV9kYXRhLAogICAgY29sb3I9J2JsYWNrJywKICAgIHM9NTAsCiAgICB6b3JkZXI9NSwKICAgIGxhYmVsPSdNZWFzdXJlZCBEYXRhICjsnoXroKUg642w7J207YSwKScsCikKYXgxLnBsb3QoCiAgICB0X2RlbnNlLAogICAgZl90LAogICAgY29sb3I9J2ZvcmVzdGdyZWVuJywKICAgIGxpbmV3aWR0aD0yLjUsCiAgICBsYWJlbD1mJ0ZpdHRlZCBNb2RlbCBmKHQpJywKKQpheDEuYXh2bGluZSgKICAgIHg9dF9tYXhfZ3Jvd3RoLAogICAgY29sb3I9J29yYW5nZScsCiAgICBsaW5lc3R5bGU9Jy0tJywKICAgIGxhYmVsPWYnTWF4IEdyb3d0aCBQb2ludCAodD17dF9tYXhfZ3Jvd3RoOi4yZn0pJywKKQpheDEuc2V0X3lsYWJlbCgnUGxhbnQgSGVpZ2h0IChjbSknLCBmb250c2l6ZT0xMikKYXgxLnNldF90aXRsZSgKICAgICdNYXRoIElJIE1vZGVsaW5nOiBVc2VyIERhdGEgRml0dGluZyAmIEdyb3d0aCBSYXRlIEFuYWx5c2lzJywgZm9udHNpemU9MTQKKQpheDEuZ3JpZChUcnVlLCBsaW5lc3R5bGU9JzonLCBhbHBoYT0wLjcpCmF4MS5sZWdlbmQobG9jPSd1cHBlciBsZWZ0JykKCiMgW+q3uOuemO2UhCAyXSDrj4TtlajsiJggZicodCkgKOyInOqwhCDshLHsnqUg7IaN64+EKQpheDIucGxvdCgKICAgIHRfZGVuc2UsCiAgICBmX3ByaW1lX3QsCiAgICBjb2xvcj0nZG9kZ2VyYmx1ZScsCiAgICBsaW5ld2lkdGg9Mi41LAogICAgbGFiZWw9Ikdyb3d0aCBSYXRlIGYnKHQpIiwKKQpheDIuc2NhdHRlcigKICAgIFt0X21heF9ncm93dGhdLAogICAgW21heF9yYXRlXSwKICAgIGNvbG9yPSdyZWQnLAogICAgcz04MCwKICAgIHpvcmRlcj01LAogICAgbGFiZWw9ZidNYXggUmF0ZSAoe21heF9yYXRlOi4yZn0gY20vZGF5KScsCikKYXgyLmF4dmxpbmUoeD10X21heF9ncm93dGgsIGNvbG9yPSdvcmFuZ2UnLCBsaW5lc3R5bGU9Jy0tJykKYXgyLnNldF94bGFiZWwoJ1RpbWUgdCAoZGF5cyknLCBmb250c2l6ZT0xMikKYXgyLnNldF95bGFiZWwoJ0dyb3d0aCBSYXRlIChjbS9kYXkpJywgZm9udHNpemU9MTIpCmF4Mi5ncmlkKFRydWUsIGxpbmVzdHlsZT0nOicsIGFscGhhPTAuNykKYXgyLmxlZ2VuZChsb2M9J3VwcGVyIHJpZ2h0JykKCnBsdC50aWdodF9sYXlvdXQoKQpwbHQuc2hvdygpCg==