Инициализировать двумерный vector в C++
В этой статье мы рассмотрим, как инициализировать двумерный vector заданным значением по умолчанию в C++.
В C++ мы можем определить двумерный vector целых чисел следующим образом:
std :: vector < std :: vector < int >> v ;
В результате получается пустой двумерный вектор. Чтобы использовать его, мы должны определить размер вектора и выделить память для его элементов. Существует несколько способов выращивания двумерного вектора с помощью конструктора заливки. resize() или же push_back() методы или с помощью списков инициализаторов.
1. Использование конструктора заливки
Рекомендуемый подход заключается в использовании конструктора заливки для инициализации двумерного вектора. Конструктор заполнения создает vector из указанного количества элементов и заполняет заданным значением.
Передача указателя на динамический двумерный массив в функцию в C++
Как передать указатель на динамический двумерный массив в функцию в C++?
puding ☆
20.08.13 19:49:22 MSK
← 1 2 3 4 →
Ответ на: комментарий от comp00 21.08.13 01:14:28 MSK
Чтобы быстрее понять указатели, не нужны никакие Шилдты, а тем более Страуструпы, которые еще больше запутают человека. Как вариант мне в свое время помогло осиливания того же x86 ассемблера, после чего сразу все стало понятно и больше вопросов не возникало.
grouzen ★★
( 21.08.13 01:27:16 MSK )
Ответ на: комментарий от cdshines 21.08.13 01:20:50 MSK
Почитал K&R, кое-что из «Глава 5. Указатели и массивы». Понял мало, но по своей проблеме узнал, что для того, чтобы передавать двумерный массив в функцию без указания размерности нужно передавать указатель на указатель. Завтра буду курить.
puding ☆
( 21.08.13 01:38:34 MSK ) автор топика
Ответ на: комментарий от yoghurt 20.08.13 22:04:50 MSK
Еще раз — где косвенность в твоих примерах?
tailgunner ★★★★★
( 21.08.13 08:18:18 MSK )
Еще один платиновый тред. Одни неучи пытаются рассказать другим неучам то, чего сами не знают. dev во всём великолепии.
Чтобы быстрее понять указатели, не нужны никакие Шилдты, а тем более Страуструпы, которые еще больше запутают человека. Как вариант мне в свое время помогло осиливания того же x86 ассемблера, после чего сразу все стало понятно и больше вопросов не возникало.
Как же хорошо, что я знакомство с компьютером именно с ассемблера и начинал. (А если точнее, со схемотехники ЭВМ и принципов работы процессора.)
Все, кто пытается программировать, не зная, во что реально превращаются все объектно-ориентированные быдлокоды — поетнциальные обезьяны с гранатами. Никогда не знаешь, в какой момент вылезет их невежество и во что оно выльется.
anonymous
( 21.08.13 08:49:03 MSK )
Ответ на: комментарий от tailgunner 21.08.13 08:18:18 MSK
Если ты говоришь о косвенности как о двойном (в общем случае — n-кратном) разыменовании указателя на указатель ([на указатель]*), то о какой косвенности может идти речь в случае
int arr[3][3] = , , >;
, о котором говорю я?
Очевидно оно разворачивается в что-то типа
movl $1, -48(%rbp) movl $2, -44(%rbp) movl $3, -40(%rbp) movl $4, -36(%rbp) movl $5, -32(%rbp) movl $6, -28(%rbp) movl $7, -24(%rbp) movl $8, -20(%rbp) movl $9, -16(%rbp)
т.о. доступ к элементу осуществляется именно по формуле из примера.
yoghurt ★★★★★
( 21.08.13 09:06:49 MSK )
Ответ на: комментарий от yoghurt 21.08.13 09:06:49 MSK
А, ты уже об этом случае говоришь. а ведь совсем недавно было:
yoghurt> Разве доступ к элементу i,j в Т*[] не работает как *(base + i*rows + j)
tailgunner ★★★★★
( 21.08.13 09:09:40 MSK )
Ответ на: комментарий от yoghurt 21.08.13 09:06:49 MSK
Между T[] и T[CONST] есть существенная разница.
anonymous
( 21.08.13 09:12:19 MSK )
Ответ на: комментарий от anonymous 21.08.13 09:12:19 MSK
Между T[] и T[CONST] есть существенная разница.
Да, ещё раз посыпая голову пеплом говорю, что неправильно назвал тип, о котором шла речь изначально.
yoghurt ★★★★★
( 21.08.13 09:22:13 MSK )
one_more_hokum ★★★
( 21.08.13 09:51:07 MSK )
Ответ на: комментарий от comp00 21.08.13 00:43:48 MSK
Ну смысл в том, что многомерные массивы, хранятся не в виде абстрактного тела (таблица/куб etc) как удобно изображать человеку, а в виде указателей на указатели.
Двумерный массів: int[10][20] — честный сплошной участок памяті
То что вы описали — это массив одномерных массивов.
dzidzitop ★★
( 21.08.13 10:39:32 MSK )
хоспади, куда смотрит наркоконтроль
anonymous
( 21.08.13 10:55:03 MSK )
Ответ на: хоспади, куда смотрит наркоконтроль от anonymous 21.08.13 10:55:03 MSK
Re: хоспади, куда смотрит наркоконтроль
Это не двумерный массив. Это вектор векторов.
anonymous
( 21.08.13 10:57:20 MSK )
Ответ на: комментарий от cdshines 21.08.13 01:24:32 MSK
На этой неделе вообще по плюсам столько вопросов от тех, кто не читал k&r. Как будто k&r — многотомное собрание сочинений кернигана и риччи в 1200 страниц каждое, и всего 24 тома.
nanoolinux ★★★★
( 21.08.13 11:52:48 MSK )
Ответ на: комментарий от yoghurt 21.08.13 00:51:41 MSK
Дружище, длинные и непонятные слова типа шаблоны и семантика меня угнетают, начинаю боятся выходить на улицу.
#include #include typedef int MYCLASS; #define WIDTH 3 #define HEIGHT 2 MYCLASS a[WIDTH][HEIGHT]; typedef MYCLASS A[WIDTH][HEIGHT]; void print(A*a , int x,int y) < for(int i=0;i> int main(void) < for(int i=0;iA * a2=(A*)new int(WIDTH*HEIGHT); for(int i=0;i print(&a,WIDTH,HEIGHT); printf("\n"); print(a2,WIDTH,HEIGHT); return 0; >
ilovewindows ★★★★★
( 21.08.13 12:11:47 MSK )
//Задать размерность int QwMatrixHeight = 20; int QwMatrixWidth = 20; //Создать матрицу QwMatrixElement*** QwMatrix;
//Выделить память под элементы матрицы for (int i = 0; i < QwMatrixHeight; i++) < for (int j = 0; j < QwMatrixWidth; j++) < QwMatrix[i][j] = new QwMatrixElement(); >>
//Передача матрицы в функцию void QwDoSomethingWithMatrix(QwMatrixElement*** matrix, int rows, int cols) < for (int i = 0; i < rows; i++) < for (int j = 0; j < cols; j++) < matrix[i][j]->someProperty = true; > > >
//Использование в программе QwDoSomethingWithMatrix(QwMatrix, QwMatrixHeight, QwMatrixWidth);
puding ☆
( 21.08.13 12:30:34 MSK ) автор топика
Ответ на: комментарий от puding 21.08.13 12:30:34 MSK
Это, как правильно сказали выше, массив массивов, а не многомерный массив.
tailgunner ★★★★★
( 21.08.13 12:36:29 MSK )
Ответ на: комментарий от tailgunner 21.08.13 12:36:29 MSK
Это, как правильно сказали выше, массив массивов, а не многомерный массив.
В его коде еще и выделение памяти под массивы отсутствует.
anonymous
( 21.08.13 12:50:07 MSK )
Ответ на: комментарий от anonymous 21.08.13 12:50:07 MSK
Точно, чего то вывыливается в SIGSEGV. Что дописать?
puding ☆
( 21.08.13 12:58:03 MSK ) автор топика
Ответ на: комментарий от puding 21.08.13 12:30:34 MSK
template void QwDoSomethingWithMatrix(T *matrix, int rows, int cols) < for (int i = 0; i < rows-1; i++) < for (int j = 0; j < cols-1; j++) < matrix[i][j]->someProperty = true; > > >
nanoolinux ★★★★
( 21.08.13 13:15:16 MSK )
Последнее исправление: nanoolinux 21.08.13 13:16:02 MSK (всего исправлений: 1)
Ответ на: комментарий от puding 21.08.13 12:58:03 MSK
anonymous
( 21.08.13 14:02:50 MSK )
Ответ на: комментарий от puding 21.08.13 12:30:34 MSK
#include #include template using Matrix = std::array, h>; struct Something < bool property; int value; Something() : property(true) <>>; template void doSomething(Matrix& mx) < int x = 0; for (auto& col : mx) for (auto& el : col) < printf("%p\n", &el); el.value = ++x; >> int main() < printf("sizeof cell = %ld\n", sizeof(Something)); constexpr size_t h = 3, w = 3; Matrixmx; // movb $1, (%rsp) // movb $1, 8(%rsp) // movb $1, 16(%rsp) // movb $1, 24(%rsp) // movb $1, 32(%rsp) // movb $1, 40(%rsp) // movb $1, 48(%rsp) // movb $1, 56(%rsp) // movb $1, 64(%rsp) doSomething(mx); for (size_t i = 0; i < h; ++i) < printf("< "); for (size_t j = 0; j < w; ++j) printf("(%p)", &mx[i][j], mx[i][j].property, mx[i][j].value); puts(">"); > > /* sizeof cell = 8 0x7ff0000d8 0x7ff0000e0 0x7ff0000e8 0x7ff0000f0 0x7ff0000f8 0x7ff000100 0x7ff000108 0x7ff000110 0x7ff000118 < (0x7ff0000d8)(0x7ff0000e0) (0x7ff0000e8) > < (0x7ff0000f0)(0x7ff0000f8) (0x7ff000100) > < (0x7ff000108)(0x7ff000110) (0x7ff000118) > */ /* total heap usage: 0 allocs, 0 frees, 0 bytes allocated */
Обрати внимание на 0 аллокаций в куче, на код аллокации на стеке, на дефолтный конструктор, на два способа обхода и на непрерывный layout элементов матрицы в памяти.
Если h и w неизвестны на момент компиляции, то нужно делать как предложил tailgunner, то есть писать свой класс с аллокацией одним куском и доступом с помощью арифметики i * w + j индексов. На самом деле (по стандарту), для статических T[h][w] и std::array, h> компилятор сам располагает их непрерывно в памяти и пишет ту же самую арифметику для индексов в ассемблер для [ i ][j].
quasimoto ★★★★
( 21.08.13 14:03:29 MSK )
Последнее исправление: quasimoto 21.08.13 14:05:29 MSK (всего исправлений: 1)
trex6 ★★★★★
( 21.08.13 14:45:23 MSK )
Ответ на: комментарий от quasimoto 21.08.13 14:03:29 MSK
это может приводить к code bloating если юзаются много інстансов с разнымі размерностями.
anonymous
( 21.08.13 15:06:13 MSK )
Ответ на: комментарий от trex6 21.08.13 14:45:23 MSK
std::vector> xs = , , , <>>; for (auto const& col : xs) < for (auto const& el : col) printf("(%p)%d ", &el, el); puts(""); >/* (0x59f91d0)1 (0x59f91d4)2 (0x59f91d8)3 (0x59f9220)1 (0x59f9224)2 (0x59f9270)1 */ /* total heap usage: 7 allocs, 7 frees, 144 bytes allocated */
144/4 = 36, куда ему столько? 🙂
int xs[][3] = , , , <>>; for (auto const& col : xs) < for (auto const& el : col) printf("(%p)%d ", &el, el); puts(""); >/* (0x7ff0000e0)1 (0x7ff0000e4)2 (0x7ff0000e8)3 (0x7ff0000ec)1 (0x7ff0000f0)2 (0x7ff0000f4)0 (0x7ff0000f8)1 (0x7ff0000fc)0 (0x7ff000100)0 (0x7ff000104)0 (0x7ff000108)0 (0x7ff00010c)0 */ /* total heap usage: 0 allocs, 0 frees, 0 bytes allocated */
(0x7ff00010c + 4 — 0x7ff0000e0) / 4 = 12 = 4 * 3.
quasimoto ★★★★
( 21.08.13 15:15:55 MSK )
Последнее исправление: quasimoto 21.08.13 15:23:46 MSK (всего исправлений: 1)
Ответ на: комментарий от anonymous 21.08.13 15:06:13 MSK
это может приводить к code bloating если юзаются много інстансов с разнымі размерностями
Если много разных h и w, то тоже — класс с size_t (const если нет resize -> realloc) h, w; T *data; и i * w + j в operator().
quasimoto ★★★★
( 21.08.13 15:20:32 MSK )
Последнее исправление: quasimoto 21.08.13 15:26:34 MSK (всего исправлений: 1)
Ответ на: комментарий от quasimoto 21.08.13 15:15:55 MSK
А если размерности нормальные взять? 500х500?
К тому же ходят слухи, что преждевременная оптимизация на спичках в большинстве своем дает только сложночитаемый код и ничего кроме.
trex6 ★★★★★
( 21.08.13 15:36:10 MSK )
Ответ на: комментарий от dzidzitop 21.08.13 10:39:32 MSK
многомерные массивы в памяти представляются именно как массивы одномерных массивов и никак иначе.
comp00 ★★★★
( 21.08.13 15:48:27 MSK )
Ответ на: комментарий от puding 21.08.13 12:58:03 MSK
Упрямый ты. Ну как хочешь : ССЗБ. Подсказываю последний раз: man new, delete
comp00 ★★★★
( 21.08.13 15:50:43 MSK )
Последнее исправление: comp00 21.08.13 15:50:53 MSK (всего исправлений: 1)
Ответ на: комментарий от comp00 21.08.13 15:48:27 MSK
рекомендую проверить. Сейчас под рукой GCC нет. Можно создать массив 2 на 2 и итерацией + печатью значений указателей на элементы проверіть.
anonymous
( 21.08.13 16:01:48 MSK )
Ответ на: комментарий от anonymous 21.08.13 16:01:48 MSK
1 #include 2 #define SIZE 4 3 int main(int argc, char **argv) < 4 int **array = new int*[SIZE]; 5 for(int i=0;i8 9 for(int i=0; i 13 std::cout 15 for(int i=0;i 18 delete [] array; 19 return 0; 20 >
./tst 0x1989040 0x1989044 0x1989048 0x198904c 0x1989060 0x1989064 0x1989068 0x198906c 0x1989080 0x1989084 0x1989088 0x198908c 0x19890a0 0x19890a4 0x19890a8 0x19890ac
Все правильно.
comp00 ★★★★
( 21.08.13 16:15:47 MSK )
Последнее исправление: comp00 21.08.13 16:16:00 MSK (всего исправлений: 1)
Ответ на: комментарий от anonymous 21.08.13 16:01:48 MSK
Я поясню: понятно, что память под вектор выделяются последовательно. Но совершенно не факт, что вектор буде идти один за другим.
comp00 ★★★★
( 21.08.13 16:21:59 MSK )
Ответ на: комментарий от comp00 21.08.13 16:15:47 MSK
это не многомерный массів.
Многомерный массів: int array[2][2] = , >;
anonymous
( 21.08.13 16:34:36 MSK )
Ответ на: комментарий от anonymous 21.08.13 16:34:36 MSK
Это (у меня) многомерный массив. А ты путаешь кучу со стеком
comp00 ★★★★
( 21.08.13 16:40:40 MSK )
Ответ на: комментарий от comp00 21.08.13 16:40:40 MSK
anonymous
( 21.08.13 16:42:33 MSK )
Ответ на: комментарий от anonymous 21.08.13 16:42:33 MSK
тупняк.
Массивы могут выделяться на стеке или на куче
естественно, что это совершенно разные структуры, и способы организации памяти у них разные.
Тот факт что в википедии под array подразумевают выделение на стеке, не означает что массивы, выделенные на куче не являются массивами.
Да по факту это не многомерный массив, в массив указателей. Но знаешь ли, компьютер тоже не с 0 и 1 работает, как в школе преподают.
comp00 ★★★★
( 21.08.13 16:59:06 MSK )
Последнее исправление: comp00 21.08.13 17:00:50 MSK (всего исправлений: 1)
Ответ на: комментарий от anonymous 21.08.13 08:49:03 MSK
однако понимание что ключ-значение бд есть включающая в себя указатели общность облегчает понимание указателей и вообще различение/общее среди имя-именуемое
qulinxao ★★☆
( 21.08.13 17:10:54 MSK )
Ответ на: комментарий от trex6 21.08.13 15:36:10 MSK
А если размерности нормальные взять?
А 3×3 или 4×4 это не нормальные?
Чтобы стек сорвать это мало, наверно. Нужно побольше.
#include #include template class Matrix < T *data; public: typedef T data_type; const size_t rows, cols; Matrix(const size_t rows, const size_t cols) : data(new T[rows * cols]), rows(rows), cols(cols) <>~Matrix() < delete[] data; >T& operator()(const size_t i, const size_t j) const < return data[i * cols + j]; >>; int main() < #ifdef VECTOR std::vector> mx(ROWS); for (size_t i = 0; i < mx.size(); ++i) < mx[i].resize(COLS); for (size_t j = 0; j < mx[i].size(); ++j) mx[i][j] = i * j; >long long sum = 0; for (size_t i = 0; i < mx.size(); ++i) for (size_t j = 0; j < mx[i].size(); ++j) sum += mx[i][j]; printf("%lld\n", sum); #else Matrixmx(ROWS, COLS); for (size_t i = 0; i
$ ~ g++ -DVECTOR -DROWS=15000 -DCOLS=20000 -O1 mx.cc $ ~ \time ./a.out 22497375075000000 1.01user 0.60system 0:01.69elapsed 95%CPU (0avgtext+0avgdata 4692848maxresident)k 0inputs+0outputs (0major+293433minor)pagefaults 0swaps $ ~ g++ -DROWS=15000 -DCOLS=20000 -O1 mx.cc $ ~ \time ./a.out 22497375075000000 0.59user 0.85system 0:01.48elapsed 97%CPU (0avgtext+0avgdata 4691488maxresident)k 128inputs+0outputs (1major+3035minor)pagefaults 0swaps $ ~ g++ -DVECTOR -DROWS=15000 -DCOLS=20000 -O2 mx.cc $ ~ \time ./a.out 22497375075000000 0.75user 0.56system 0:01.38elapsed 94%CPU (0avgtext+0avgdata 4692864maxresident)k 0inputs+0outputs (0major+293435minor)pagefaults 0swaps $ ~ g++ -DROWS=15000 -DCOLS=20000 -O2 mx.cc $ ~ \time ./a.out 22497375075000000 0.44user 0.33system 0:00.79elapsed 97%CPU (0avgtext+0avgdata 4690672maxresident)k 0inputs+0outputs (0major+1503minor)pagefaults 0swaps $ ~ g++ -DVECTOR -DROWS=15000 -DCOLS=20000 -Ofast mx.cc $ ~ \time ./a.out 22497375075000000 0.47user 0.60system 0:01.15elapsed 93%CPU (0avgtext+0avgdata 4692864maxresident)k 0inputs+0outputs (0major+293435minor)pagefaults 0swaps $ ~ g++ -DROWS=15000 -DCOLS=20000 -Ofast mx.cc $ ~ \time ./a.out 22497375075000000 0.40user 0.34system 0:00.76elapsed 98%CPU (0avgtext+0avgdata 4691456maxresident)k 0inputs+0outputs (0major+992minor)pagefaults 0swaps
NumPy: матрицы и операции над ними
В этом ноутбуке из сторонних библиотек нам понадобится только NumPy. Для удобства импортируем ее под более коротким именем:
import numpy as np
1. Создание матриц
Приведем несколько способов создания матриц в NumPy.
Самый простой способ — с помощью функции numpy.array(list, dtype=None, . ).
В качестве первого аргумента ей надо передать итерируемый объект, элементами которого являются другие итерируемые объекты одинаковой длины и содержащие данные одинакового типа.
Второй аргумент является опциональным и определяет тип данных матрицы. Его можно не задавать, тогда тип данных будет определен из типа элементов первого аргумента. При задании этого параметра будет произведена попытка приведения типов.
Например, матрицу из списка списков целых чисел можно создать следующим образом:
a = np.array([1, 2, 3]) # Создаем одномерный массив print(type(a)) # Prints "" print(a.shape) # Prints "(3,)" - кортеж с размерностями print(a[0], a[1], a[2]) # Prints "1 2 3" a[0] = 5 # Изменяем значение элемента массива print(a) # Prints "[5, 2, 3]" b = np.array([[1,2,3],[4,5,6]]) # Создаем двухмерный массив print(b.shape) # Prints "(2, 3)" print(b[0, 0], b[0, 1], b[1, 0]) # Prints "1 2 4" print(np.arange(1, 5)) #Cоздает вектор с эелементами от 1 до 4
(3,) 1 2 3 [5 2 3] (2, 3) 1 2 4 [1 2 3 4]
matrix = np.array([[1, 2, 3], [2, 5, 6], [6, 7, 4]]) print ("Матрица:\n", matrix)
Матрица: [[1 2 3] [2 5 6] [6 7 4]]
Второй способ создания — с помощью встроенных функций numpy.eye(N, M=None, . ), numpy.zeros(shape, . ), numpy.ones(shape, . ).
Первая функция создает единичную матрицу размера N×M ; если M не задан, то M = N .
Вторая и третья функции создают матрицы, состоящие целиком из нулей или единиц соответственно. В качестве первого аргумента необходимо задать размерность массива — кортеж целых чисел. В двумерном случае это набор из двух чисел: количество строк и столбцов матрицы.
Примеры:
b = np.eye(5) print ("Единичная матрица:\n", b)
Единичная матрица: [[1. 0. 0. 0. 0.] [0. 1. 0. 0. 0.] [0. 0. 1. 0. 0.] [0. 0. 0. 1. 0.] [0. 0. 0. 0. 1.]]
c = np.ones((7, 5)) print ("Матрица, состоящая из одних единиц:\n", c)
Матрица, состоящая из одних единиц: [[1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.]]
d = np.full((2,2), 7) # Создает матрицу (1, 2) заполненую заданным значением print(d) # Prints "[[ 7. 7.] # [ 7. 7.]]" e = np.random.random((2,2)) # Создает еденичную матрицу (2, 2) заполненую случаными числами (0, 1) print(e) # Might print "[[ 0.91940167 0.08143941] # [ 0.68744134 0.87236687]]"
[[7 7] [7 7]] [[0.25744383 0.48056466] [0.13767881 0.40578168]]
Обратите внимание: размерность массива задается не двумя аргументами функции, а одним — кортежем!
Вот так — np.ones(7, 5) — создать массив не получится, так как функции в качестве параметра shape передается 7, а не кортеж (7, 5).
И, наконец, третий способ — с помощью функции numpy.arange([start, ]stop, [step, ], . ), которая создает одномерный массив последовательных чисел из промежутка [start, stop) с заданным шагом step, и метода array.reshape(shape).
Параметр shape, как и в предыдущем примере, задает размерность матрицы (кортеж чисел). Логика работы метода ясна из следующего примера:
v = np.arange(0, 24, 2) print ("Вектор-столбец:\n", v)
Вектор-столбец: [ 0 2 4 6 8 10 12 14 16 18 20 22]
d = v.reshape((3, 4)) print ("Матрица:\n", d)
Матрица: [[ 0 2 4 6] [ 8 10 12 14] [16 18 20 22]]
Более подробно о том, как создавать массивы в NumPy, см. документацию.
2. Индексирование
Для получения элементов матрицы можно использовать несколько способов. Рассмотрим самые простые из них.
Для удобства напомним, как выглядит матрица d:
print ("Матрица:\n", d)
Матрица: [[ 0 2 4 6] [ 8 10 12 14] [16 18 20 22]]
Элемент на пересечении строки i и столбца j можно получить с помощью выражения array[i, j].
Обратите внимание: строки и столбцы нумеруются с нуля!
print ("Второй элемент третьей строки матрицы:", d[2, 1])
Второй элемент третьей строки матрицы: 18
Из матрицы можно получать целые строки или столбцы с помощью выражений array[i, :] или array[:, j] соответственно:
print ("Вторая строка матрицы d:\n", d[1, :]) print ("Четвертый столбец матрицы d:\n", d[:, 3])
Вторая строка матрицы d: [ 8 10 12 14] Четвертый столбец матрицы d: [ 6 14 22]
Еще один способ получения элементов — с помощью выражения array[list1, list2], где list1, list2 — некоторые списки целых чисел. При такой адресации одновременно просматриваются оба списка и возвращаются элементы матрицы с соответствующими координатами. Следующий пример более понятно объясняет механизм работы такого индексирования:
print ("Элементы матрицы d с координатами (1, 2) и (0, 3):\n", d[[1, 0], [2, 3]])
Элементы матрицы d с координатами (1, 2) и (0, 3): [12 6]
# Slicing # Создадим матрицу (3, 4) # [[ 1 2 3 4] # [ 5 6 7 8] # [ 9 10 11 12]] a = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]]) # Используя слайсинг, созадим матрицу b из элементов матрицы а # будем использовать 0 и 1 строку, а так же 1 и 2 столебц # [[2 3] # [6 7]] b = a[:2, 1:3] print(b) # ОБРАТИТЕ ВНИМАНИЕ НА ИЗМЕНЕНИЕ ИСХОДОЙ МАТРИЦЫ print(a[0, 1]) # Prints "2" b[0, 0] = 77 # b[0, 0] is the same piece of data as a[0, 1] print(a[0, 1]) # Prints "77"
[[2 3] [6 7]] 2 77
# Integer array indexing a = np.array([[1,2], [3, 4], [5, 6]]) print(a) print() # Пример Integer array indexing # В результате получится массив размерности (3,) # Обратите внимание, что до запятой идут индексы строк, после - столбцов print(a[[0, 1, 2], [0, 1, 0]]) # Prints "[1 4 5]" print() # По-другому пример можно записать так print(np.array([a[0, 0], a[1, 1], a[2, 0]])) # Prints "[1 4 5]"
[[1 2] [3 4] [5 6]] [1 4 5] [1 4 5]
Примеры использования слайсинга:
# Создадим новый маассив, из которого будем выбирать эллементы a = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]]) print(a) # prints "array([[ 1, 2, 3], # [ 4, 5, 6], # [ 7, 8, 9], # [10, 11, 12]])" # Создадим массив индексов b = np.array([0, 2, 0, 1]) # Выберем из каждой строки элемент с индексом из b (индекс столбца берется из b) print(a[np.arange(4), b]) # Prints "[ 1 6 7 11]" print() # Добавим к этим элементам 10 a[np.arange(4), b] += 10 print(a) # prints "array([[11, 2, 3], # [ 4, 5, 16], # [17, 8, 9], # [10, 21, 12]])
[[ 1 2 3] [ 4 5 6] [ 7 8 9] [10 11 12]] [ 1 6 7 11] [[11 2 3] [ 4 5 16] [17 8 9] [10 21 12]]
a = np.array([[1,2], [3, 4], [5, 6]]) bool_idx = (a > 2) # Найдем эллементы матрицы a, которые больше 2 # В результате получим матрицу b, такой же размерности, как и a print(bool_idx) # Prints "[[False False] print() # [ True True] # [ True True]]" # Воспользуемся полученным массивом для создания нового массива, ранга 1 print(a[bool_idx]) # Prints "[3 4 5 6]" # Аналогично print(a[a > 2]) # Prints "[3 4 5 6]"
[[False False] [ True True] [ True True]] [3 4 5 6] [3 4 5 6]
#Помните, что вы можете пользоваться сразу несколькими типами индексирования a = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]]) row_r1 = a[1, :] row_r2 = a[1:2, :] print(row_r1, row_r1.shape) # Prints "[5 6 7 8] (4,)" print(row_r2, row_r2.shape) # Prints "[[5 6 7 8]] (1, 4)"
[5 6 7 8] (4,) [[5 6 7 8]] (1, 4)
Более подробно о различных способах индексирования в массивах см. документацию.
3. Векторы, вектор-строки и вектор-столбцы
Следующие два способа задания массива кажутся одинаковыми:
a = np.array([1, 2, 3]) b = np.array([[1], [2], [3]])
Однако, на самом деле, это задание одномерного массива (то есть вектора) и двумерного массива:
print ("Вектор:\n", a) print ("Его размерность:\n", a.shape) print ("Двумерный массив:\n", b) print ("Его размерность:\n", b.shape)
Вектор: [1 2 3] Его размерность: (3,) Двумерный массив: [[1] [2] [3]] Его размерность: (3, 1)
Обратите внимание: вектор (одномерный массив) и вектор-столбец или вектор-строка (двумерные массивы) являются различными объектами в NumPy, хотя математически задают один и тот же объект. В случае одномерного массива кортеж shape состоит из одного числа и имеет вид (n,), где n — длина вектора. В случае двумерных векторов в shape присутствует еще одна размерность, равная единице.
В большинстве случаев неважно, какое представление использовать, потому что часто срабатывает приведение типов. Но некоторые операции не работают для одномерных массивов. Например, транспонирование (о нем пойдет речь ниже):
a = a.T b = b.T
print ("Вектор не изменился:\n", a) print ("Его размерность также не изменилась:\n", a.shape) print ("Транспонированный двумерный массив:\n", b) print ("Его размерность изменилась:\n", b.shape)
Вектор не изменился: [1 2 3] Его размерность также не изменилась: (3,) Транспонированный двумерный массив: [[1 2 3]] Его размерность изменилась: (1, 3)
4. Datatypes
Все элементы в массиве numpy принадлежат одному типу. В этом плане массивы ближе к C, чем к привычным вам листам питона. Numpy имеет множество встренных типов, подходящих для решения большинства задач.
x = np.array([1, 2]) # Автоматический выбор типа print(x.dtype) # Prints "int64" x = np.array([1.0, 2.0]) # Автоматический выбор типа print(x.dtype) # Prints "float64" x = np.array([1, 2], dtype=np.int64) # Принудительное выставление типа print(x.dtype) # Prints "int64"
int32 float64 int64
5. Математические операции
К массивам (матрицам) можно применять известные вам математические операции. Следут понимать, что при этом у элементов должны быть схожие размерности. Поведение в случае не совпадения размерностей хорошо описанно в документации numpy.
x = np.array([[1,2],[3,4]], dtype=np.float64) y = np.array([[5,6],[7,8]], dtype=np.float64) arr = np.array([1, 2])
# Сложение происходит поэлеметно # [[ 6.0 8.0] # [10.0 12.0]] print(x + y) print() print(np.add(x, y)) print('С числом') print(x + 1) print('C массивом другой размерности') print(x + arr)
[[ 6. 8.] [10. 12.]] [[ 6. 8.] [10. 12.]] С числом [[2. 3.] [4. 5.]] C массивом другой размерности [[2. 4.] [4. 6.]]
# Вычитание print(x - y) print(np.subtract(x, y))
[[-4. -4.] [-4. -4.]] [[-4. -4.] [-4. -4.]]
# Деление # [[ 0.2 0.33333333] # [ 0.42857143 0.5 ]] print(x / y) print(np.divide(x, y))
[[0.2 0.33333333] [0.42857143 0.5 ]] [[0.2 0.33333333] [0.42857143 0.5 ]]
# Другие функции # [[ 1. 1.41421356] # [ 1.73205081 2. ]] print(np.sqrt(x))
[[1. 1.41421356] [1.73205081 2. ]]
6. Умножение матриц и столбцов
Напоминание теории. Операция умножения определена для двух матриц, таких что число столбцов первой равно числу строк второй.
Пусть матрицы A и B таковы, что A ∈ ℝ n×k и B ∈ ℝ k×m . Произведением матриц A и B называется матрица C , такая что cij = ∑ k r = 1 airbrj , где cij — элемент матрицы C , стоящий на пересечении строки с номером i и столбца с номером j .
В NumPy произведение матриц вычисляется с помощью функции numpy.dot(a, b, . ) или с помощью метода array1.dot(array2), где array1 и array2 — перемножаемые матрицы.
a = np.array([[1, 0], [0, 1]]) b = np.array([[4, 1], [2, 2]]) r1 = np.dot(a, b) r2 = a.dot(b)
print ("Матрица A:\n", a) print ("Матрица B:\n", b) print ("Результат умножения функцией:\n", r1) print ("Результат умножения методом:\n", r2)
Матрица A: [[1 0] [0 1]] Матрица B: [[4 1] [2 2]] Результат умножения функцией: [[4 1] [2 2]] Результат умножения методом: [[4 1] [2 2]]
Матрицы в NumPy можно умножать и на векторы:
c = np.array([1, 2]) r3 = b.dot(c)
print ("Матрица:\n", b) print ("Вектор:\n", c) print ("Результат умножения:\n", r3)
Матрица: [[4 1] [2 2]] Вектор: [1 2] Результат умножения: [6 6]
Обратите внимание: операция * производит над матрицами покоординатное умножение, а не матричное!
r = a * b
print ("Матрица A:\n", a) print ("Матрица B:\n", b) print ("Результат покоординатного умножения через операцию умножения:\n", r)
Матрица A: [[1 0] [0 1]] Матрица B: [[4 1] [2 2]] Результат покоординатного умножения через операцию умножения: [[4 0] [0 2]]
Более подробно о матричном умножении в NumPy см. документацию.
7. Объединение массивов
Массивы можно Объединенять. Есть горизонтальное и вертикальное объединение.
a = np.floor(10*np.random.random((2,2))) b = np.floor(10*np.random.random((2,2))) print(a) print(b) print() print(np.vstack((a,b))) print() print(np.hstack((a,b)))
[[4. 0.] [1. 4.]] [[9. 7.] [2. 6.]] [[4. 0.] [1. 4.] [9. 7.] [2. 6.]] [[4. 0. 9. 7.] [1. 4. 2. 6.]]
Массивы можно переформировать при помощи метода, который задает новый многомерный массив. Следуя следующему примеру, мы переформатируем одномерный массив из десяти элементов во двумерный массив, состоящий из пяти строк и двух столбцов:
a = np.array(range(10), float) print(a) print() # Превратим в матрицу a = a.reshape((5, 2)) print(a) print() # Вернем обратно print(a.flatten()) # Другой вариант print(a.reshape((-1))) # Превратим в марицу (9, 1) print(a.reshape((-1, 1))) # Превратим в марицу (1, 9) print(a.reshape((1, -1)))
[0. 1. 2. 3. 4. 5. 6. 7. 8. 9.] [[0. 1.] [2. 3.] [4. 5.] [6. 7.] [8. 9.]] [0. 1. 2. 3. 4. 5. 6. 7. 8. 9.] [0. 1. 2. 3. 4. 5. 6. 7. 8. 9.] [[0.] [1.] [2.] [3.] [4.] [5.] [6.] [7.] [8.] [9.]] [[0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]]
Задания: (Блок 1)
Задание 1:
Решите без использования циклов средставми NumPy (каждый пункт решается в 1-2 строчки)
- Создайте вектор с элементами от 12 до 42
- Создайте вектор из нулей длины 12, но его пятый елемент должен быть равен 1
- Создайте матрицу (3, 3), заполненую от 0 до 8
- Найдите все положительные числа в np.array([1,2,0,0,4,0])
- Умножьте матрицу размерности (5, 3) на (3, 2)
- Создайте матрицу (10, 10) так, чтобы на границе были 0, а внтури 1
- Создайте рандомный вектор и отсортируйте его
- Каков эквивалент функции enumerate для numpy массивов?
- *Создайте рандомный вектор и выполните нормализацию столбцов (из каждого столбца вычесть среднее этого столбца, из каждого столбца вычесть sd этого столбца)
- *Для заданного числа найдите ближайший к нему элемент в векторе
- *Найдите N наибольших значений в векторе
# ваш код здесь
Задание 2:
Напишите полностью векторизованный вариант
Дан трёхмерный массив, содержащий изображение, размера (height, width, numChannels), а также вектор длины numChannels. Сложить каналы изображения с указанными весами, и вернуть результат в виде матрицы размера (height, width). Считать реальное изображение можно при помощи функции scipy.misc.imread (если изображение не в формате png, установите пакет pillow: conda install pillow). Преобразуйте цветное изображение в оттенки серого, использовав коэффициенты np.array([0.299, 0.587, 0.114]).
# ваш код здесь
8. Транспонирование матриц
Напоминание теории. Транспонированной матрицей A T называется матрица, полученная из исходной матрицы A заменой строк на столбцы. Формально: элементы матрицы A T определяются как a T ij = aji , где a T ij — элемент матрицы A T , стоящий на пересечении строки с номером i и столбца с номером j .
В NumPy транспонированная матрица вычисляется с помощью функции numpy.transpose() или с помощью метода array.T, где array — нужный двумерный массив.
a = np.array([[1, 2], [3, 4]]) b = np.transpose(a) c = a.T
print ("Матрица:\n", a) print ("Транспонирование функцией:\n", b) print ("Транспонирование методом:\n", c)
Матрица: [[1 2] [3 4]] Транспонирование функцией: [[1 3] [2 4]] Транспонирование методом: [[1 3] [2 4]]
В следующих разделах активно используется модуль numpy.linalg, реализующий некоторые приложения линейной алгебры. Более подробно о функциях, описанных ниже, и различных других функциях этого модуля можно посмотреть в его документации.
9. Определитель матрицы
Напоминание теории. Для квадратных матриц существует понятие определителя.
Пусть A — квадратная матрица. Определителем (или детерминантом) матрицы A ∈ ℝ n×n назовем число
detA = ∑ α1, α2, …, αn ( − 1) N(α1, α2, …, αn) ⋅aα11⋅⋅⋅aαnn,
где α1, α2, …, αn — перестановка чисел от 1 до n , N(α1, α2, …, αn) — число инверсий в перестановке, суммирование ведется по всем возможным перестановкам длины n .
Не стоит расстраиваться, если это определение понятно не до конца — в дальнейшем в таком виде оно не понадобится.
Например, для матрицы размера 2×2 получается:
det ⎛ ⎜ ⎝ a11 a12 a21 a22 ⎞ ⎟ ⎠ = a11a22 − a12a21
Вычисление определителя матрицы по определению требует порядка n! операций, поэтому разработаны методы, которые позволяют вычислять его быстро и эффективно.
В NumPy определитель матрицы вычисляется с помощью функции numpy.linalg.det(a), где a — исходная матрица.
a = np.array([[1, 2, 1], [1, 1, 4], [2, 3, 6]], dtype=np.float32) det = np.linalg.det(a)
print ("Матрица:\n", a) print ("Определитель:\n", det)
Матрица: [[1. 2. 1.] [1. 1. 4.] [2. 3. 6.]] Определитель: -1.0
Рассмотрим одно интересное свойство определителя. Пусть у нас есть параллелограмм с углами в точках (0, 0), (c, d), (a + c, b + d), (a, b) (углы даны в порядке обхода по часовой стрелке). Тогда площадь этого параллелограмма можно вычислить как модуль определителя матрицы ⎛ ⎜ ⎝ a c b d ⎞ ⎟ ⎠ . Похожим образом можно выразить и объем параллелепипеда через определитель матрицы размера 3×3 .
10. Ранг матрицы
Напоминание теории. Рангом матрицы A называется максимальное число линейно независимых строк (столбцов) этой матрицы.
В NumPy ранг матрицы вычисляется с помощью функции numpy.linalg.matrix_rank(M, tol=None), где M — матрица, tol — параметр, отвечающий за некоторую точность вычисления. В простом случае можно его не задавать, и функция сама определит подходящее значение этого параметра.
a = np.array([[1, 2, 3], [1, 1, 1], [2, 2, 2]]) r = np.linalg.matrix_rank(a)
print ("Матрица:\n", a) print ("Ранг матрицы:", r)
Матрица: [[1 2 3] [1 1 1] [2 2 2]] Ранг матрицы: 2
С помощью вычисления ранга матрицы можно проверять линейную независимость системы векторов.
Допустим, у нас есть несколько векторов. Составим из них матрицу, где наши векторы будут являться строками. Понятно, что векторы линейно независимы тогда и только тогда, когда ранг полученной матрицы совпадает с числом векторов. Приведем пример:
a = np.array([1, 2, 3]) b = np.array([1, 1, 1]) c = np.array([2, 3, 5]) m = np.array([a, b, c])
print (np.linalg.matrix_rank(m) == m.shape[0])
True
11. Системы линейных уравнений
Напоминание теории. Системой линейных алгебраических уравнений называется система вида Ax = b , где A ∈ ℝ n×m , x ∈ ℝ m×1 , b ∈ ℝ n×1 . В случае квадратной невырожденной матрицы A решение системы единственно.
В NumPy решение такой системы можно найти с помощью функции numpy.linalg.solve(a, b), где первый аргумент — матрица A , второй — столбец b .
a = np.array([[3, 1], [1, 2]]) b = np.array([9, 8]) x = np.linalg.solve(a, b)
print ("Матрица A:\n", a) print ("Вектор b:\n", b) print ("Решение системы:\n", x)
Матрица A: [[3 1] [1 2]] Вектор b: [9 8] Решение системы: [2. 3.]
Убедимся, что вектор x действительно является решением системы:
print (a.dot(x))
Бывают случаи, когда решение системы не существует. Но хотелось бы все равно “решить” такую систему. Логичным кажется искать такой вектор x , который минимизирует выражение ‖ Ax − b ‖ 2 — так мы приблизим выражение Ax к b .
В NumPy такое псевдорешение можно искать с помощью функции numpy.linalg.lstsq(a, b, . ), где первые два аргумента такие же, как и для функции numpy.linalg.solve(). Помимо решения функция возвращает еще три значения, которые нам сейчас не понадобятся.
a = np.array([[0, 1], [1, 1], [2, 1], [3, 1]]) b = np.array([-1, 0.2, 0.9, 2.1]) x, res, r, s = np.linalg.lstsq(a, b, rcond=None)
print ("Матрица A:\n", a) print ("Вектор b:\n", b) print ("Псевдорешение системы:\n", x)
Матрица A: [[0 1] [1 1] [2 1] [3 1]] Вектор b: [-1. 0.2 0.9 2.1] Псевдорешение системы: [ 1. -0.95]
12. Обращение матриц
Напоминание теории. Для квадратных невырожденных матриц определено понятие обратной матрицы.
Пусть A — квадратная невырожденная матрица. Матрица A − 1 называется обратной матрицей к A , если
AA − 1 = A − 1 A = I,
где I — единичная матрица.
В NumPy обратные матрицы вычисляются с помощью функции numpy.linalg.inv(a), где a — исходная матрица.
a = np.array([[1, 2, 1], [1, 1, 4], [2, 3, 6]], dtype=np.float32) b = np.linalg.inv(a)
print ("Матрица A:\n", a) print ("Обратная матрица к A:\n", b) print ("Произведение A на обратную должна быть единичной:\n", a.dot(b))
Матрица A: [[1. 2. 1.] [1. 1. 4.] [2. 3. 6.]] Обратная матрица к A: [[ 6. 9. -7.] [-2. -4. 3.] [-1. -1. 1.]] Произведение A на обратную должна быть единичной: [[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]]
13. Собственные числа и собственные вектора матрицы
Напоминание теории. Для квадратных матриц определены понятия собственного вектора и собственного числа.
Пусть A — квадратная матрица и A ∈ ℝ n×n . Собственным вектором матрицы A называется такой ненулевой вектор x ∈ ℝ n , что для некоторого λ ∈ ℝ выполняется равенство Ax = λx . При этом λ называется собственным числом матрицы A . Собственные числа и собственные векторы матрицы играют важную роль в теории линейной алгебры и ее практических приложениях.
В NumPy собственные числа и собственные векторы матрицы вычисляются с помощью функции numpy.linalg.eig(a), где a — исходная матрица. В качестве результата эта функция выдает одномерный массив w собственных чисел и двумерный массив v, в котором по столбцам записаны собственные вектора, так что вектор v[:, i] соотвествует собственному числу w[i].
a = np.array([[-1, -6], [2, 6]]) w, v = np.linalg.eig(a)
print ("Матрица A:\n", a) print ("Собственные числа:\n", w) print ("Собственные векторы:\n", v)
Матрица A: [[-1 -6] [ 2 6]] Собственные числа: [2. 3.] Собственные векторы: [[-0.89442719 0.83205029] [ 0.4472136 -0.5547002 ]]
Обратите внимание: у вещественной матрицы собственные значения или собственные векторы могут быть комплексными.
14. Расстояния между векторами
Вспомним некоторые нормы, которые можно ввести в пространстве ℝ n , и рассмотрим, с помощью каких библиотек и функций их можно вычислять в NumPy.
p-норма
p-норма (норма Гёльдера) для вектора x = (x1, …, xn) ∈ ℝ n вычисляется по формуле:
‖ x ‖ p = ( n ∑ i = 1 | xi | p ) 1 ⁄ p , p ≥ 1.
В частных случаях при: * p = 1 получаем ℓ1 норму * p = 2 получаем ℓ2 норму
Далее нам понабится модуль numpy.linalg, реализующий некоторые приложения линейной алгебры. Для вычисления различных норм мы используем функцию numpy.linalg.norm(x, ord=None, . ), где x — исходный вектор, ord — параметр, определяющий норму (мы рассмотрим два варианта его значений — 1 и 2). Импортируем эту функцию:
from numpy.linalg import norm
ℓ1 норма
ℓ1 норма (также известная как манхэттенское расстояние) для вектора x = (x1, …, xn) ∈ ℝ n вычисляется по формуле:
‖ x ‖ 1 = n ∑ i = 1 | xi | .
Ей в функции numpy.linalg.norm(x, ord=None, . ) соответствует параметр ord=1.
a = np.array([1, 2, -3]) print('Вектор a:', a)
Вектор a: [ 1 2 -3]
print('L1 норма вектора a:\n', norm(a, ord=1))
L1 норма вектора a: 6.0
ℓ2 норма
ℓ2 норма (также известная как евклидова норма) для вектора x = (x1, …, xn) ∈ ℝ n вычисляется по формуле:
‖ x ‖ 2 = √ ( n ∑ i = 1 ( xi ) 2 ) .
Ей в функции numpy.linalg.norm(x, ord=None, . ) соответствует параметр ord=2.
print ('L2 норма вектора a:\n', norm(a, ord=2))
L2 норма вектора a: 3.7416573867739413
Более подробно о том, какие еще нормы (в том числе матричные) можно вычислить, см. документацию.
15. Расстояния между векторами
Для двух векторов x = (x1, …, xn) ∈ ℝ n и y = (y1, …, yn) ∈ ℝ n ℓ1 и ℓ2 раccтояния вычисляются по следующим формулам соответственно:
ρ1 ( x, y ) = ‖ x − y ‖ 1 = n ∑ i = 1 | xi − yi |
ρ2 ( x, y ) = ‖ x − y ‖ 2 = √ ( n ∑ i = 1 ( xi − yi ) 2 ) .
a = np.array([1, 2, -3]) b = np.array([-4, 3, 8]) print ('Вектор a:', a) print ('Вектор b:', b)
Вектор a: [ 1 2 -3] Вектор b: [-4 3 8]
print ('L1 расстояние между векторами a и b:\n', norm(a - b, ord=1)) print ('L2 расстояние между векторами a и b:\n', norm(a - b, ord=2))
L1 расстояние между векторами a и b: 17.0 L2 расстояние между векторами a и b: 12.12435565298214
16. Скалярное произведение и угол между векторами
a = np.array([0, 5, -1]) b = np.array([-4, 9, 3]) print ('Вектор a:', a) print ('Вектор b:', b)
Вектор a: [ 0 5 -1] Вектор b: [-4 9 3]
Скалярное произведение в пространстве ℝ n для двух векторов x = (x1, …, xn) и y = (y1, …, yn) определяется как:
⟨x, y⟩ = n ∑ i = 1 xiyi.
Длиной вектора x = (x1, …, xn) ∈ ℝ n называется квадратный корень из скалярного произведения, то есть длина равна евклидовой норме вектора:
| x | = √ ( ⟨x, x⟩ ) = √ ( n ∑ i = 1 x 2 i ) = ‖ x ‖ 2.
Теперь, когда мы знаем расстояние между двумя ненулевыми векторами и их длины, мы можем вычислить угол между ними через скалярное произведение:
⟨x, y⟩ = | x | |y|cos(α) ⟹ cos(α) = ( ⟨x, y⟩ )/( | x | |y| ) ,
где α ∈ [0, π] — угол между векторами x и y .
cos_angle = np.dot(a, b) / norm(a) / norm(b) print ('Косинус угла между a и b:', cos_angle) print ('Сам угол:', np.arccos(cos_angle))
Косинус угла между a и b: 0.8000362836474323 Сам угол: 0.6434406336093618
17. Комплексные числа в питоне
Напоминание теории. Комплексными числами называются числа вида x + iy , где x и y — вещественные числа, а i — мнимая единица (величина, для которой выполняется равенство i 2 = − 1 ). Множество всех комплексных чисел обозначается буквой ℂ (подробнее про комплексные числа см. википедию).
В питоне комплескные числа можно задать следующим образом (j обозначает мнимую единицу):
a = 3 + 2j b = 1j
print ("Комплексное число a:\n", a) print ("Комплексное число b:\n", b)
Комплексное число a: (3+2j) Комплексное число b: 1j
С комплексными числами в питоне можно производить базовые арифметические операции так же, как и с вещественными числами:
c = a * a d = a / (4 - 5j)
print ("Комплексное число c:\n", c) print ("Комплексное число d:\n", d)
Комплексное число c: (5+12j) Комплексное число d: (0.0487804878048781+0.5609756097560976j)
Задания: (Блок 2)
Задание 3:
Рассмотрим сложную математическую функцию на отрезке [1, 15]:
f(x) = sin(x / 5) * exp(x / 10) + 5 * exp(-x / 2)
Она может описывать, например, зависимость оценок, которые выставляют определенному сорту вина эксперты, в зависимости от возраста этого вина. Мы хотим приблизить сложную зависимость с помощью функции из определенного семейства. В этом задании мы будем приближать указанную функцию с помощью многочленов.
Как известно, многочлен степени n (то есть w0 + w1x + w2x 2 + … + wnx n ) однозначно определяется любыми n + 1 различными точками, через которые он проходит. Это значит, что его коэффициенты w0 , … wn можно определить из следующей системы линейных уравнений:
где через x1, . xn, xn + 1 обозначены точки, через которые проходит многочлен, а через f(x1), . f(xn), f(xn + 1) — значения, которые он должен принимать в этих точках.
Воспользуемся описанным свойством, и будем находить приближение функции многочленом, решая систему линейных уравнений.
- Сформируйте систему линейных уравнений (то есть задайте матрицу коэффициентов A и свободный вектор b) для многочлена первой степени, который должен совпадать с функцией f в точках 1 и 15. Решите данную систему с помощью функции scipy.linalg.solve. Нарисуйте функцию f и полученный многочлен. Хорошо ли он приближает исходную функцию?
- Повторите те же шаги для многочлена второй степени, который совпадает с функцией f в точках 1, 8 и 15. Улучшилось ли качество аппроксимации?
- Повторите те же шаги для многочлена третьей степени, который совпадает с функцией f в точках 1, 4, 10 и 15. Хорошо ли он аппроксимирует функцию? Коэффициенты данного многочлена (четыре числа в следующем порядке: w_0, w_1, w_2, w_3) являются ответом на задачу. Округлять коэффициенты не обязательно, но при желании можете произвести округление до второго знака (т.е. до числа вида 0.42)
Сайт построен с использованием Pelican. За основу оформления взята тема от Smashing Magazine. Исходные тексты программ, приведённые на этом сайте, распространяются под лицензией GPLv3, все остальные материалы сайта распространяются под лицензией CC-BY.
Как передать двумерный вектор в функцию c
@user7860670, вот-вот, я почему с таким заблуждением о массивах до сих пор был. Я экспериментировал, и это не работало, а вот это работало *(*(array + row) + col) , поэтому тут всё очень запутанно.
@user7860670,с чего бы это? Что то новое или ваше субъективное мнение?
@ARHovsepyan Выше ссылку на стандарт кидал.
@HolyBlackCat Но ведь двумерный массив именно потому что блок без пропусков, представляет собой массив из d1*d2 элементов, так что все в порядке.
@Πανμέτρονάριστον У вас работало такое ТОЛЬКО для явно объявленной второй размерности array . А где она в функции?
@HolyBlackCat, теперь для каждого действия с указателем, нужно смотреть на стандарт? Мне он не нужен в таких случаях. Если массив статический,, то лучший способ передать в функцию указатель на первый элемент massiv[0] , что то же что и &massiv[0][0] . Вы наверное имеете ввиду, что таким способом не передать динамический массив.
@Harry С одной стороны да, но с другой по ссылке прямым текстом написано что-то типа "если указатель p указывает на i -ый элемент массива a размера n , и 0
Неопределенного поведения для статического массива не возможно наблюдать.
@ARHovsepyan "теперь для каждого действия с указателем, нужно смотреть на стандарт" Да, это ж C++, тут неопределенное поведение из всех щелей. :Р
@HolyBlackCat Именно - Никаких исключений для многомерных массивов нет! Именно! Потому что это просто массив с d1*d2*. элементами!
@Harry Не, это массив из более маленьких массивов. И &massiv[0][0] показывает на первый элемент маленького массива.
Бьерн Страуструп. Язык программирования на С++. третье издание. Приложение В. 7. 3.
@HolyBlackCat См. eel.is/c++draft/dcl.array#9 - по-моему, там ясно описана арифметика с указателями, как вполне разрешенная.
@HolyBlackCat, да, если вы учите все наизусть, чаще это не нужно, если вы выучили и поняли.
@HolyBlackCat Да, кстати, а как вы предлагаете передавать в функцию многомерные массивы? Без указания размерностей времени компиляции? А то минусов моему (работающему) решению накидали, а вот предложить что-то другое - почему-то не сумели 🙂 Причем учтите, что размерности могут быть известны во время выполнения, так что не спасут даже шаблоны.
@Harry Я так понял, это просто дополнительное объяснение, как работают многомерные массивы. Раз это "note", то оно не имеет "законной силы", а добавлено просто для ясности. Да и там не написано, что можно выходить за границы под-массивов.
"как вы предлагаете передавать в функцию многомерные массивы" Никак, нет у меня нормального решения. :/ Лучше всего заменить обычный многомерный массив на самодельный класс матрицы, у которого под капотом будет что-то типа одномерного std::vector а.
@HolyBlackCat Ну и как вы это сделаете в чисто С? Кроме как строя сложную структуру, что не годится, если вы получаете, скажем, двумерный массив из какого-то API. Словом, слушайте Оккама и не преумножайте сущности сверх необходимости 🙂 Это все работает как часы, именно потому, что в стандарте описано как единый массив с кучей элементов, к которому вполне применима нормальная адресная арифметика.
Вообще, можно просто сделать одномерный массив и засунуть его в структуру, которая будет хранить всё необходимое. Я думаю, что в сишных либах так и делается. Многомерные массивы практически нигде не используются на практике.
@HolyBlackCat Кстати, в С99 все проще - в силу разрешенности массивов переменной длины (или как они там. ) там вполне разрешена конструкция f(int N, int arr[][N]) .
@Harry В C с этим проблем нет, можно взять VLA: void foo(int a, int b, int array[a][b]) . Вот похожий вопрос на английском. Тут мнения насчет UB разошлись, но обнаружился сильный аргумент за то, что это UB: в constexpr -коде это вызывает ошибку компиляции: godbolt.org/z/8xrx5Y