Введение#

Где и как применяются численные методы#

  • Любое моделирование


image.png

• Интерполяция

• Численное решение дифференциальных уравнений

• Теория погрешностей

• Умение составлять численный алгоритм для математической задачи

• …

  • Анализ реальных данных


image.png

• Регрессия (аппроксимация)

• Оптимизация (поиск минимумов, максимумов)

• Случайные процессы

• Численное интегрирование

• …

  • Современные алгоритмы (прим. нейронные сети)


image.png
  • Статистика

  • Параллельное программирование

  • Оптимизация



Как быстро по~д~нять Python?#


image.png

На случай, если вы сомневаетесь в своих силах, крайне рекоммендую потратить часов 5 на освоение базового курса Python на pythontutor.ru.

Постарайтесь прорешать как можно больше задач и закрепить синтаксис.

Крайне желательно установить себе jupyter notebook на ноутбук, но можно использовать онлайн редакторы (google colab и др.)

https://colab.research.google.com/notebooks/intro.ipynb

Чтобы там работать, нужно перейти по ссылке, дать разрешение на доступ к своему гугл-диску, ввести код:

from google.colab import drive

drive.mount(„/content/drive/“)

После этого войти с помощью гугл-аккаунта, появится код доступа, его надо ввести в появившуюся строку ввода

image.png

Если всё сделать правильно, то появится сообщение «Mounted at /content/drive/»

image.png

В Питоне есть огромное количество библиотек для анализа данных (описание некоторых ниже) с очень хорошей документацией.

NumPy — библиотека для работы с массивами и матрицами, в том числе матричные операции.

SciPy — библиотека для выполнения научных и инженерных расчётов.

Scikit­Learn (sklearn) — библиотека алгоритмов машинного обучения с множеством примеров и наборами данных.

Matplotlib — библиотека для визуализации данных.

SymPy - библиотека для символьных вычислений.

Желающие могут изучить популярный пакет Pandas для работы с табличными данными, но в нашем курсе он не понадобится.

Numpy, Matplotlib - наше всё#

В курсе будут использоваться некоторые важные библиотеки, без которых жить ну совсем тяжело. Они упростят все аспекты решений задач и позволят визуализировать результат.

В целом, использование сторонних библиотек поощрается при выполнении ДЗ, если не оговорено иное.

Numpy#

import numpy as np
  • основная библиотека для операций с массивами числовых данных, матрицами и объектами с большим количеством индексов

  • быстрые матричные операции

np_array = np.array([1, 2, 3, 4]) # Основной объект: numpy array (одинаковый тип чисел на весь массив)
np_array
array([1, 2, 3, 4])
np_array = np.array([1, 2, 3, 4.]) # Чем отличается от предыдущего?

np_array
array([1., 2., 3., 4.])
np_array.shape # Размерность массива
(4,)
np_array = np_array.reshape((2, 2)) # изменим размерность массива

np_array.shape
(2, 2)
np_array
array([[1., 2.],
       [3., 4.]])
np_array = np_array.reshape((1, -1)) # Превратим матрицу обратно в строку

np_array
array([[1., 2., 3., 4.]])
np_array = np_array.reshape((-1, 1)) # Или столбец

np_array
array([[1.],
       [2.],
       [3.],
       [4.]])

Удобное создание массивов#

# np.arange(a, b, s) - выводит одномерный массив чисел начиная с a (включительно) до b (невключительно) с шагом s

np.arange(1, 10, 0.2)
array([1. , 1.2, 1.4, 1.6, 1.8, 2. , 2.2, 2.4, 2.6, 2.8, 3. , 3.2, 3.4,
       3.6, 3.8, 4. , 4.2, 4.4, 4.6, 4.8, 5. , 5.2, 5.4, 5.6, 5.8, 6. ,
       6.2, 6.4, 6.6, 6.8, 7. , 7.2, 7.4, 7.6, 7.8, 8. , 8.2, 8.4, 8.6,
       8.8, 9. , 9.2, 9.4, 9.6, 9.8])
# np.linspace(a, b, N) - равно делит [a, b] на N-1 часть и выводит массив с N граничными точками

np.linspace(1, 2, 10)
array([1.        , 1.11111111, 1.22222222, 1.33333333, 1.44444444,
       1.55555556, 1.66666667, 1.77777778, 1.88888889, 2.        ])
# np.logspace(a, b, base) - то же самое, что и linspace, но делит на

print(np.logspace(1, 2, 5))
[ 10.          17.7827941   31.6227766   56.23413252 100.        ]
np.ones((5,2))
array([[1., 1.],
       [1., 1.],
       [1., 1.],
       [1., 1.],
       [1., 1.]])
np.zeros((2, 3))
array([[0., 0., 0.],
       [0., 0., 0.]])
np.eye(3)
array([[1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]])

Случайные массивы#

np.random.rand(2, 3) # Равномерное распределение на [0, 1]
array([[0.58349704, 0.03717281, 0.49240429],
       [0.38980893, 0.15188871, 0.45001384]])
np.random.normal(4, 5, size=(4, 5)) # Гауссово распределение
array([[  4.12365561,  -3.03305192, -12.15134102,   7.83903711,
          3.15015423],
       [ -5.27771412,   6.0569352 ,   5.53217522,   4.44533889,
         15.98563729],
       [  5.51015709,   8.49372852,   2.59555899,   5.26353021,
          8.67348281],
       [ -8.51103734,   7.98617451,  -1.32595877,   6.56653338,
          8.50131234]])

Индексация и взятие среза#

a = np.arange(27).reshape(3, 9)

a
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8],
       [ 9, 10, 11, 12, 13, 14, 15, 16, 17],
       [18, 19, 20, 21, 22, 23, 24, 25, 26]])
#  В качестве срезов можно извлекать как столбцы так и строки

print(a[:, 0], a[:, 0].shape)    #  Извлекаем столбец, равносильно a[0:3, 0]
[ 0  9 18] (3,)
a[1:3, 2]    #  Можно извлечь часть столбца
array([11, 20])
a[0:2, 3:7]    #  Срез из строк и срез каждой строки
array([[ 3,  4,  5,  6],
       [12, 13, 14, 15]])
a[0, 1]    # Или просто обратиться к элементу
1

Помним про специфику копирования массивов в питоне#

A = np.array([1, 2, 3, 4])
B = A

print(A, B)

B[0] = -100
print(A, B)
[1 2 3 4] [1 2 3 4]
[-100    2    3    4] [-100    2    3    4]
A = np.array([1, 2, 3, 4])
B = A.copy()

print(A, B)

B[0] = -100
print(A, B)
[1 2 3 4] [1 2 3 4]
[1 2 3 4] [-100    2    3    4]

Обычные операции с матрицами выполняются поэлементно!#

C = A**2

print(A, C)
[1 2 3 4] [ 1  4  9 16]
np.sin(A)
array([ 0.84147098,  0.90929743,  0.14112001, -0.7568025 ])
A = A.reshape((2, 2))

A
array([[1, 2],
       [3, 4]])
A > 2    #  Создание масок
array([[False, False],
       [ True,  True]])
A[A > 2] = 0     #   Маски могут выполнять роль индексов

A
array([[1, 2],
       [0, 0]])
np.array([1, 2, 3]) / np.array([1, 2, 1])
array([1., 1., 3.])

Матричные операции#

np.dot([1, 2, 3], [1, 0, 1])     #   Скалярное произведение
4
print(np_array, np_array.T)     #   Транспонирование
[[1.]
 [2.]
 [3.]
 [4.]] [[1. 2. 3. 4.]]
print(np.array([1, 0]))
print("@")
print(A)

print("-----------")

np.array([1, 0]) @ A  # Матричное умножение
[1 0]
@
[[1 2]
 [0 0]]
-----------
array([1, 2])
A = np.array([[1, 1, 1], [1, 1, 1]])
B = np.array([[2, 2, 2], [2, 2, 2]])

print(A, "\n\n", B)

np.concatenate((A, B), axis=0)    #  Конкатенация массивов
[[1 1 1]
 [1 1 1]] 

 [[2 2 2]
 [2 2 2]]
array([[1, 1, 1],
       [1, 1, 1],
       [2, 2, 2],
       [2, 2, 2]])
np.concatenate((A, B), axis=1)
array([[1, 1, 1, 2, 2, 2],
       [1, 1, 1, 2, 2, 2]])
np.concatenate((B, A), axis=1)
array([[2, 2, 2, 1, 1, 1],
       [2, 2, 2, 1, 1, 1]])

Матричные операции оптимизированы и выполняются гораздо быстрее, чем питоновские циклы

При выполении математических операций с numpy массивами никогда не следует пользоваться питоновскими циклами и другими операциями, которые работают поэлементно. Если вам кажется, что это невозможно, то с высокой вероятностью вы не правы

Однако в некоторых случаях это действительно невозможно. Тогда используют особые средства ускорения циклов, которые основаны на том, чтобы в Python использовать типы данных из C, компиллировать функции Python. Позже в этом семестре мы их тоже будем изучать.

long_array = np.random.normal(size=(10000 * 1000))
%%time
result = long_array * long_array
CPU times: user 46.9 ms, sys: 0 ns, total: 46.9 ms
Wall time: 62.6 ms
%%time
result = [x * y for x, y in zip(long_array, long_array)]
CPU times: user 844 ms, sys: 266 ms, total: 1.11 s
Wall time: 1.31 s

Собственные числа и вектора матрицы#

np.linalg.eig(
            np.array([[1, 2, 3], [1, 0, 1], [4, -5, 2]])
            )
(array([ 4.30277564, -2.        ,  0.69722436]),
 array([[-7.35289262e-01, -7.07106781e-01, -7.61041891e-01],
        [-3.10857367e-01,  1.03725802e-16, -5.01677599e-01],
        [-6.02260242e-01,  7.07106781e-01,  4.11260047e-01]]))

И так далее#

Если вам потребовалась какая-то операция над матрицами или другими массивами, то с высокой вероятностью она уже реализована в numpy.

Если вам потребовалось какое-то хитрое взятие индексов у объекта numpy, скорее всего, есть элегантный способ сделать это без привлечения функций или с минимальным количеством операций numpy

См. np.mean, np.std, np.unique, np.where, np.repeat, np.unravel_index итд.

Также к блокноту прилагается шпаргалка с основными функциями numpy.

Matplotlib#

Библиотека для отрисовки всевозможных графиков (и анимаций)

import matplotlib.pyplot as plt
# Обычный график

xs = np.linspace(0, 6, 50)

sin_ys = np.sin(xs)
cos_ys = np.cos(xs)

plt.plot(xs, sin_ys)
plt.plot(xs, cos_ys) 
[<matplotlib.lines.Line2D at 0x7fa8cee88430>]
../../_images/13b738ecbc40e4c2a3f22e8b70c20b6e2073aa7335020a14eaa1d64861315e3e.png
# C наворотами

plt.figure(figsize=(5,5))
plt.title('Cosine and sine graph')

plt.plot(xs, sin_ys, label='sin x')
plt.fill_between(xs, sin_ys - 0.1, sin_ys + 0.1, alpha=0.5)

plt.plot(xs, cos_ys, '--', c='green', label='cos x')

plt.xlim(0, 5)
plt.ylim(-1, 0.9)
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.show()
../../_images/132fcf0b499684da1aa034cbf939b1f72aeb798569cd79438d44abc2d49448be.png
# Отображение точек на графике

xs = np.linspace(0, 0.7, 30)
ys = xs**2 - 0.5 * xs
ys_shifted = ys + np.random.normal(scale=0.01, size=xs.shape)

plt.scatter(xs, ys_shifted, c='r')
plt.plot(xs, ys)
plt.show()
../../_images/b906f8c62a289283c91b518d004e0b0de592fffa9c0764934828ecd2b062cdde.png
# Гистограмма

np_array = np.random.normal(size=1000 * 1000)

plt.hist(np_array, bins=100)
plt.show()
../../_images/42baed197525abe738109391ce9200044f5eef3297f81a0fe3d5c56344e4170e.png
# После некоторой практики можно и выпендриваться (скопируйте этот код в свой блокнот для запуска)
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from ipywidgets import interact
import matplotlib

# Функции для координат x, y и z в зависимости от t
def x_func(t):
    return np.cos(t)

def y_func(t):
    return np.sin(t)

def z_func(t):
    return t

# Функция для рисования 3D графика с цветными сегментами
def plot_colored_spiral(azimuth, elevation):
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')

    t = np.linspace(0, 10, 100)
    colors = ["red", "orange", "yellow", "green", "blue", "purple"]  # Генерация цветов радуги

    segment_length = len(t) // 6

    for i in range(6):
        start_idx = i * segment_length
        end_idx = (i + 1) * segment_length
        color_segment = colors[i]
        ax.plot(x_func(t[start_idx:end_idx]),
                y_func(t[start_idx:end_idx]),
                z_func(t[start_idx:end_idx]),
                color=color_segment, lw=5)

    ax.view_init(elev=elevation, azim=azimuth)

    plt.show()

# Создание интерактивных ползунков
interact(plot_colored_spiral, azimuth=(0, 360), elevation=(-90, 90))
<function __main__.plot_colored_spiral(azimuth, elevation)>

plotly#

Для интерактивных виджетов желательно использовать библиотеку plotly - она сохраняет все состояния виджета при выполнении, тем самым устраняя проблемы подвисания. Питон всё же медленный. Удобнее написать рабочий прототип с ipywidgets и перевести на plotly используя какой-нибудь чат бот.

import numpy as np
import plotly.graph_objects as go

# Функции для координат x, y и z в зависимости от t
def x_func(t):
    return np.cos(t)

def y_func(t):
    return np.sin(t)

def z_func(t):
    return t

# Функция для рисования 3D графика с цветными сегментами
def plot_colored_spiral(azimuth, elevation):
    fig = go.Figure()

    t = np.linspace(0, 10, 100)
    colors = ["red", "orange", "yellow", "green", "blue", "purple"]  # Генерация цветов радуги

    segment_length = len(t) // 6

    for i in range(6):
        start_idx = i * segment_length
        end_idx = (i + 1) * segment_length
        color_segment = colors[i]
        fig.add_trace(go.Scatter3d(
            x=x_func(t[start_idx:end_idx]),
            y=y_func(t[start_idx:end_idx]),
            z=z_func(t[start_idx:end_idx]),
            mode='lines',
            line=dict(color=color_segment, width=5),
            showlegend=False
        ))

    fig.update_layout(scene=dict(
        xaxis=dict(title='X'),
        yaxis=dict(title='Y'),
        zaxis=dict(title='Z')
    ))

    fig.update_layout(scene_camera=dict(eye=dict(x=-1.25, y=-1.25, z=1.25),
                                        center=dict(x=0, y=0, z=0),
                                        up=dict(x=0, y=0, z=1),
                                        projection=dict(type='perspective')),
                      scene=dict(aspectmode='cube'))

    # Установка размеров канваса
    fig.update_layout(width=600, height=600)

    fig.show(width=600, height=600)  # Размещение по центру

# Создание интерактивных виджетов
plot_colored_spiral(azimuth=180, elevation=30)  # Начальное значение