АПД судя по комментариям, эта статья нуждается в некоторой мотивировке. Что именно и зачем мы делаем. Коротко говоря, производную все, кто её как-то запоминает (а запоминают немногие) помнят в дальнейшем как "другой график". Это "ещё одна функция", которая описывает исходную. Мы никогда почти не интересуемся производной поточечно. А между тем производная изначально - не столько отдельная функция, сколько характеристика исходного графика в разных точках. Именно эту привязку мы и проявим в статье. КОНЕЦ АПД
ВТОРОЙ АПД о касательной как пределе секущих и другой возможной питонизации темы см. в длинном комментарии под статьёй вот тут: https://habr.com/ru/post/599051/#comment_23894499 . КОНЕЦ ВТОРОГО АПД
Начнём с повторения математических терминов и нескольких опций питона. В питоне мы будем использовать черепашку, а также модуль математических функций math. От черепашки понадобятся:
shape(), color(), speed(), radians() - функции общей настройки
xcor(), ycor() - функции, сообщающие положение
goto() - для перемещения черепашки в точку
setheading() - для изменения направления головы черепашки
done() - функция замораживания экрана (нужна при работе в трезвом редакторе вроде VSCode. Если вы пишете в IDLE, она не нужна)
Кроме того, заметим, что поле у черепашки в пикселях. Если она побежит по параболе - убежит моментально за край листка. Так что введём какую-нибудь единицу UNIT = 100 пикселей, например, ну и вот это вот всё отнормируем в наших юнитах.
Из модуля math возьмём функцию atan() - арктангенс. Она возвращает радианную меру угла, не градусы. Дальше мы либо там же берём число pi - константу модуля math, тихо на неё делим, умножив потом на 180, либо мы повторяем, что значит "радианная мера угла", вернее - проходим её. Ваши ученики, скорее всего, про радианы не слышали.
А "производная" в данном случае - "тангенс угла наклона касательной", вот, что нам требуется. Очень похоже на канцелярит, "нанизывание родительных падежей" - "зам. главы отдела контроля качества цеха упаковки (и др.)". Будем эту конструкцию разбирать с конца:
есть у нас график функции y = f(x)
есть на нём точка, (a, f(a))
если туда пришла черепаха, если она (черепаха) бежит по графику, ей в этой точке надо смотреть... по касательной к графику, так что нам требуется уравнение этой касательной. Если последнее записать как y = kx+b, то это k как раз и есть производная: k = f'(a)
функция перенаправления головы черепашки умеет в углы, не в прямые. Чтобы сказать "посмотри в направлении этой прямой" мы должны передать не прямую, а "угол между прямой, по которой надо смотреть, и лучом Ox". И если k в пункте выше - тангенс того угла, тут нам понадобится арктангенс k: atan(f'(a))
Получим что-то такое вот для параболы:
from turtle import Turtle, done
from math import atan
UNIT = 100
alice = Turtle()
alice.color('green')
alice.shape('turtle')
alice.speed(1)
alice.radians()
for step in range(2 * UNIT):
x = alice.xcor() + 1
y = x**2 / UNIT
alice.goto(int(x), int(y))
k = 2*x / UNIT
alpha = atan(k)
alice.setheading(alpha)
done()
Что-то подобное я уже делала с несколькими +/- старшеклассниками. В целом неплохо: это можно прочесть, и код работает (проверьте сами). В чём тут проблемы:
не вполне ясно, где мы тут делим на юнит, как выбраны эти места
что нам придётся делать с юнитами, когда мы заменим график
можно ли всю картинку сдвинуть из точки (0, 0)?
как пересадить это в браузер с джаваскриптом и стилями?
В браузере мы можем создать картинку с помощью трёх div-ов: внешний со срезанными углами и глазками (это два вложенных div). Глазки куда-нибудь смещены, надо их перенаправить по курсу. Перемещать можно с помощью javascript:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
background: gray;
}
.beast {
position: absolute;
background: green;
width: 20px;
height: 20px;
border-radius: 50%;
}
.eye {
background: white;
width: 5px;
height: 5px;
border-radius: 2px;
transform: translateY(5px);
}
</style>
</head>
<body>
<div class="beast">
<div class="eye"></div>
<div class="eye"></div>
</div>
<script>
const UNIT = 100
const beast = document.getElementsByClassName('beast')[0]
let x = 0
let y = 0
let k = 0
let alpha = 0
let id = setInterval(
function() {
x++
y = x * x / UNIT
beast.style.left = x + 'px'
beast.style.top = y + 'px'
k = 2 * x / UNIT
alpha = Math.atan(k) * 180 / Math.PI + 180
beast.style.transform = 'rotate(' + alpha + 'deg)'
if (x > 2 * UNIT) clearInterval(id)
}, 50
)
</script>
</body>
</html>
С этого места код хочется разделить на файлы. Собственно, и питоновский код тоже хочется, и мы делили его: файл с основной логикой + файл отрисовка + файл калькулятор. В браузере, соответственно, плюс ещё два файла: разметка и стили.
Бонусом станет возможность на этом же материале пройти наследование. Или даже уже интерфейсы. Вы пишете интерфейс для воплощения функции и производной. Функционал отрисовки юзает интерфейс. А ваши классы с разными калькуляторами обсчитывают разные функции.
Пример с наследованием, примерно в таком вот виде пройденный с восьмиклассником в начале учебного года (парень - сын программиста, и увлекается программированием давно). У нас есть главный файл с основной логикой, рисовалка и папка с разными калькуляторами: базовым и наследующими под разные функции.
main.py
from painter import Painter
from calculators.calculatorSquare import CalculatorSquare
painter = Painter()
calculator = CalculatorSquare()
for x in range(200):
calculator.nextStep()
y = calculator.getY()
alpha = calculator.getAngleInRadians()
painter.showState(x, y, alpha)
painter.freeze()
painter.py
class Painter:
def __init__(self):
from turtle import Turtle, Screen
self.__beast = Turtle()
self.__beast.shape('turtle')
self.__beast.speed(10)
self.__beast.radians()
def showState(self, x, y, alpha):
self.__beast.goto(x,y)
self.__beast.setheading(alpha)
def freeze(self):
from turtle import done
done()
и папка calculators с файлами calculator.py
from math import atan
class Calculator:
def __init__(self):
self._UNIT = 100
self._x = 0
self._y = 0
self._alpha = 0
def nextStep(self):
self._x += 1
def getY(self) -> float:
pass
def getDerivative(self) -> float:
pass
def getAngleInRadians(self) -> float:
return atan(self.getDerivative())
и calculatorSquare.py
from .calculator import Calculator
class CalculatorSquare(Calculator):
def getY(self):
return self._x ** 2 / self._UNIT
def getDerivative(self):
return self._x * 2 / self._UNIT