01. Моделирование арифметического вектора [решение

Run Settings
LanguageC
Language Version
Run Command
// шпаргалка по массивам, указателям и динамическому выделению памяти // http://coolaf.com/run/snippets/enzt95gz29 // что еще не доделано // - проверка на компланарность. я хз как реализовать проверку на компланарность // системы из n векторов // - проверка образует ли n векторов замкнутую фигуру - зависит от проверки на // компланарность // - я не уверен насчет корректности работы векторного произведения в // размерностях отличных от 3 // про что можно написать в курсаче: // - динамическое выделение памяти (которое необходимо для реализации структуры // вектора) // https://ru.wikipedia.org/wiki/Динамическое_распределение_памяти // http://www.amse.ru/courses/cpp1/2009.10.19.html // // - про структуру вектора и функцию v_create // // - про функцию v_input, там есть, что скопировать в курсач // // - операции над векторами и системами векторов // // - векторное умножение в n-мерном пространстве, иллюстрация // https://mathoverflow.net/questions/94312/n-dimensional-cross-product-reference-request // w1 - wn = вектора // e1 - en = базисные вектора // det = детерминатор матрицы // // - функции реализованы так, что программа не будет вылетать при возникновении // серьезной ошибки, а попытается её проигнорировать или исправить. // для примера, посмотри на v_add, или v_input. можешь просто скопировать то, // что я там написал, и написать об этов в разделе "обработка ошибок" // // - функции разделены на две группы, и имеют свои префиксы в названиях: // * v_ - для работы векторами // * va_ - для работы с массивом векторов // ПОДКЛЮЧЕНИЕ ВНЕШНИХ ФАЙЛОВ -------------------------------------------------- #include <stdio.h> // ввод-вывод (printf, fgets, sscanf, fopen) #include <stdlib.h> // работа с паматью (malloc, realloc, free) // ОПРЕДЕЛЕНИЯ ----------------------------------------------------------------- // т.к. в си нету типа bool (true / false), заменяем их на 1 и 0 #define TRUE 1 // компилятор заменит все слова TRUE на 1 #define FALSE 0 // компилятор заменит все слова FALSE на 0 // длина буфера для хранения обрабатываемых строк #define BUFFER_LENGTH 80 // компилятор заменит эту строку на число 80 // СТРУКТУРЫ ------------------------------------------------------------------- struct Vector { // арефметический вектор int size; // размерность вектора (т.е. кол-во компонентов) double* data; // указатель на массив компонент вектора }; typedef struct Vector Vector; // позволяет писать Vector вместо struct Vector // можно было и сразу написать // typedef struct { ... } Vector // но так проще для понимания // ПРОТОТИПЫ ФУНКЦИЙ ---------------------------------------------------------- // компилятору лучше заранее указать прототипы функций. это позволит писать // их в любом порядке, не волнуясь о том, что одна функция вызывает другую, // которая может зависеть от третьей, и т.д. Vector v_create(int size, double* source); // создает новый вектор Vector v_input(); // новый вектор из консоли Vector v_load(char* path); // новый вектор из файла void v_save(Vector v, char* path); // записать вектор в файл void v_print(Vector v); // вывести вектор на экран void v_destroy(Vector v); // уничтожить вектор int v_equal(Vector a, Vector b); // два вектора равны void v_add(Vector v1, Vector v2); // прибавить v2 к v1 void v_sub(Vector v1, Vector v2); // вычесть v2 из v1 double v_dot(Vector v1, Vector v2); // скалярное произведение // двух векторов Vector va_cross(Vector* vectors, int count); // векторное произведение // массива векторов int v_collinear(Vector v1, Vector v2); // два вектора коллинеарны int va_collinear(Vector* vectors, int count); // массив векторов коллинеарен // т.е. линейно зависим int va_complanar(Vector* vectors, int count); // массив векторов компланарен // РЕАЛИЗАЦИЯ ФУНКЦИЙ ---------------------------------------------------------- /* возвращает новый вектор указанной размерности если в качестве source передать NULL, все компоненты вектора будут равны нулю если в качестве source передать массив, функция скопирует из него значения */ Vector v_create(int size, double* source) { // если вдруг передали отрицательную размерность, делаем её равной нулю if (size < 0) { size = 0; } // создаем экземпляр вектора Vector v; // прописываем размерность v.size = size; // Vector хранит поле data, которое должно содержать адрес массива чисел // типа double. изначально этот указатель пустой, да и массива нету. нужно // вручную выделить область в RAM под массив чисел, и дать этому указателю // адрес на него. используем malloc для такой операции. // кстати, если попытаться выделить 0 байт, malloc вернет NULL // каждое число double занимает sizeof(double) байт. нам нужно size чисел, // значит нужно умножить sizeof(double) на size, чтобы выделить нужное // количество байт v.data = malloc(size * sizeof(double)); // заполняем массив чисел нулями (иначе он будет заполнен мусором из RAM) for (int i = 0; i < v.size; i++) { v.data[i] = 0; } // если при вызове функции, в качестве source мы передали массив, то функция // скопирует из него значения. если передать NULL - ничего не произойдет. if (source != NULL) { for (int i = 0; i < v.size; i++) { // копируем size чисел из source v.data[i] = source[i]; // в v.data } } // возвращаем созданный вектор return v; } /* возвращает новый вектор из данных, введенных в консоль */ Vector v_input() { // размерность будущего вектора int size; // если просто использовать scanf для ввода всех данных из консоли, можно // наткнуться на кучу неочевидных ошибок. // поэтому для обработки ввода из консоли, лучше сначала считать строку, // которую ввел пользователь в буфер с помощью fgets (file get string), // а затем парсить сам буфер с помощью sscanf (string scan formatted, не // путать с обычной scanf!) // создаем буфер (читай массив) из BUFFER_LENGTH символов типа char // BUFFER_LENGTH определена в начале файла с помощью #define char buffer[BUFFER_LENGTH]; printf("Input Vector size (int): "); // вместо того, чтобы вылетать с ошибкой при неправильном вводе, можно // сделать так, чтобы программа распознавала ошибку и просила пользователя // ввести данные снова и снова, пока он не сделает это правильно. // если в условие цикла while прописать TRUE или 1, он будет выполняться // бесконечно, пока мы не остановим его принудительно. // если что, закрыть консольную программу можно по нажатию CTRL + C while (TRUE) { // считываем строку из stdin (стандартный ввод консоли) в буфер // fgets считает до BUFFER_LENGTH символов, пока не наткнется на перенос // строки (enter) fgets(buffer, BUFFER_LENGTH, stdin); // когда мы вызываем sscanf (или обычную scanf), она всегда возвращает // количество число корректно отпарсенных переменных. это число можно // использовать для проверки ошибок. // мы пытаемся считать одну переменную (size), соответственно sscanf_s // должна вернуть число 1. // проверяем это с помощью if if (sscanf(buffer, "%i", &size) == 1) { // если число, которое вернула функция sscanf_s равно 1, значит все // правильно. принудительно выходим из цикла, чтобы он не начался заново break; } // если мы дошли до этой строки, значит предыдущее условие не сработало. // сообщаем об ошибке, и просим пользователя ввести данные еще раз printf("Error while reading input.\nTry again with correct number (int): "); } // создаем пустой вектор указанной размерности Vector v = v_create(size, NULL); // просим пользователя ввести size чисел printf("Input %i values separated by new line (double): \n", size); // начинаем цикл for (int i = 0; i < size; i++) { // считываем строку, введенную пользователем fgets(buffer, BUFFER_LENGTH, stdin); // сканируем одно число, заодно проверяем, что нам вернет sscanf_s if (sscanf(buffer, "%lf", &v.data[i]) != 1) { // если sscanf_s вернула не 1, сообщаем об ошибке printf("Error while reading input.\nStarting over from #[%i] (double): ", i); // и делаем шаг назад в цикле. пользователю придется ввести это число // снова i--; } } // возвращаем заполненый вектор return v; } /* выводит содержание вектора */ void v_print(Vector v) { printf("{ "); // проходимся по компонентам вектора в цикле for (int i = 0; i < v.size; i++) { // %lf означает long float, используется для отображения данных типа double printf("%lf ", v.data[i]); } printf("}"); } /* возвращает вектор из текстового файла path - строка, путь к файлу формат файла такой: 3 // первая строка - размерность вектора 1.2345 // остальные строки - компоненты вектора 5.678 123.45 */ Vector v_load(char* path) { int size; Vector v; // FILE - это специальная структура из stdio.h, хранящая в себе информацию об // открытом файле и данные из него. // fopen создает структуру FILE, выделяет под неё память и возвращает адрес // этой структуры. адрес этот мы будем хранить в указателе на FILE, т.е. FILE* // открываем файл (с помощью флага "r") FILE* file = fopen(path, "r"); // буфер для хранения строк из файла (по сути просто строка, массив символов) // длинной в BUFFER_LENGTH символов char buffer[BUFFER_LENGTH]; // если произошла ошибка при создании FILE, fopen возвращает NULL if (file == NULL) { // в таком случае сообщаем об ошибке при открытии файла printf("Error while opening file [%s]. Returning empty vector...\n", path); // и возвращаем пустой вектор нулевой размерности return v_create(0, NULL); } // считываем до BUFFER_LENGTH символов из файла в буфер, пока не наткнемся // на новую строку fgets(buffer, BUFFER_LENGTH, file); // ищем в буфере целое число (размерность нового вектора). Если sscanf вернет // число отличное, от числа переменных, которые мы пытаемся считать (1), // расцениваем это как ошибку. if (sscanf(buffer, "%i", &size) != 1) { // сообщаем об ошибке при чтении размерности вектора printf("Error while reading vector size [%s]. Setting size to 0...\n", path); // устанавливаем нулевую размерность size = 0; } // если все ок, создаем пустой вектор заданной размерности v = v_create(size, NULL); // пытаемся считать компоненты вектора из файла for (int i = 0; i < size; i++) { // читаем следующую строку из файла в буфер fgets(buffer, BUFFER_LENGTH, file); // сканируем 1 число за раз, одновременно проверяя, что вернет sscanf if (sscanf(buffer, "%lf", &v.data[i]) != 1) { // если она вернула не 1, сообщаем об ошибке printf("Error while reading vector data [%s]. Returning empty vector...\n", path); // и уничтожаем содержимое вектора (высвобождаем память) v_destroy(v); } } // возвращаем получившийся вектор в любом случае, даже если была ошибка чтения return v; } /* сохраняет вектор в текстовый файл */ void v_save(Vector v, char* path) { // создаем файл (с помощью флага "w") FILE* file = fopen(path, "w"); // если файл не открылся для записи, сообщаем об ошибке if (file == NULL) { printf("Error while saving vector to file [%s]\n", path); // и возвращаемся из функции return; } // если все ок, пишем в файл размерность вектора с помощью fprintf // (file print formatted). используем %i для целого числа. fprintf(file, "%i\n", v.size); // и записываем в файл компоненты вектора for (int i = 0; i < v.size; i++) { fprintf(file, "%lf\n", v.data[i]); } printf("Vector saved [%s]\n", path); } /* высвобождает память, выделенную под вектор и делает его размерность нулевой. это важно делать, чтобы не засорять память ненужными векторами, или если нужно пересоздать вектор под той же переменной. */ void v_destroy(Vector v) { v.size = 0; free(v.data); } /* возвращает TRUE если векторы равны, FALSE в обратном случае */ int v_equal(Vector v1, Vector v2) { // если их размерность равна if (v1.size == v2.size) { // тогда проходимся по компонентам вектора в цикле for (int i = 0; i < v1.size; i++) { // и если наткнемся хоть на одно несовпадение, сразу возвращаем FALSE if (v1.data[i] != v2.data[i]) { return FALSE; } } // а если несовпадений не было, то векторы равны. тогда возвращаем TRUE return TRUE; } // если их размерность не равна, возвращаем FALSE. // else писать нет смысла мы в любом случае дойдем до сюда, если первое // условие не сработает return FALSE; } /* прибавляет v2 к v1 (не возращает сумму векторов, а прибавляет один к другому!) создать функцию sum, которая просто возвращает новый вектор, сложенный из двух других неполучится. вернее получится, но в подобном выражении: v = sum(v1, sum(v2, v3)) будет утечка памяти, т.к. память под создание вектора sum(v2, v3) выделится, но этот вектор является промежуточным результатом и мы его никуда не сохраняем. таким образом мы просто прое**м участок памяти, не сохранив ссылку на него в переменной, и программа больше не сможет его использовать. и освободить мы его не сможем тоже. то же самое с вычитанием. поэтому просто избегаем подобного функционала. */ void v_add(Vector v1, Vector v2) { // вообще, эта операция определена только на векторах одинаковой размерности // но мы сделаем немного удобнее // если размерность v2 больше, чем размерность v1, мы просто увеличим // размерность v1 так, чтобы она была такой же, как и у v2 // пример: // v1 = {1, 2, 3} // v2 = {1, 1, 1, 1, 1} // v_add(v1, v2) -> {1, 2, 3, 1, 1} <- теперь v1 не 3D, а 4D вектор, как и v2 // делается это с помощью realloc (memory reallocate). мы указываем ей какую // область памяти нужно перераспределить, и сколько байт потребуется (в нашем // случае кол-во байт будет равно v2.size * sizeof(double). // все элементы, что в области до перераспределения, сохранятся. // кстати, если с помощью realloc уменьшить размер выделенной области, // то все, что не влезет будет отброшено. if (v2.size > v1.size) { // участок памяти, который нужно перераспределить // v v1.data = realloc(v1.data, v2.size * sizeof(double)); // ^ ^ // кол-во элементов сколько байт занимает каждый элемент // заполняем новые ячейки нулями, начиная с ячейки v1.size, иначе там будет // мусор из RAM. пример: // v1 = {1, 2, 3} // v1 = {1, 2, 3, ?, ?} // i = 0 1 2 3 4 // ^ // начинаем отсюда for (int i = v1.size; i < v2.size; i++) { v1.data[i] = 0; } v1.size = v2.size; // и не забываем скопировать значение size из v2 } // пробегаемся по компонентам вектора for (int i = 0; i < v1.size; i++) { // и прибавляем элементы вектора v2 к элементам вектора v1 // операция (a += b) равносильна (a = a + b) v1.data[i] += v2.data[i]; } } /* вычитает v2 из v1 */ void v_sub(Vector v1, Vector v2) { // эта операция тоже определена только на векторах одинаковой размерности // и мы опять сделаем немного удобнее // делаем то же самое, что делали в v_add. если размер v2 больше, чем v1, то // мы увеличим размер v1, чтобы из него можно было вычесть все элементы v2 // пример: // v1 = {1, 2, 3} // v2 = {1, 1, 1, 1, 1} // v_sub(v1, v2) -> {0, 1, 2, -1, -1} if (v2.size > v1.size) { v1.data = realloc(v1.data, v2.size * sizeof(double)); for (int i = v1.size; i < v2.size; i++) { v1.data[i] = 0; } v1.size = v2.size; } // если все ок, пробегаемся по компонентам вектора for (int i = 0; i < v1.size; i++) { // вычитаем элементы вектора v2 из элементов вектора v1 // операция (a -= b) равносильна (a = a - b) v1.data[i] -= v2.data[i]; } } /* умножает все компоненты вектора на k */ void v_mul(Vector v, double k) { for (int i = 0; i < v.size; i++) { v.data[i] *= k; } } /* возвращает TRUE если векторы коллинеарны (пропорциональны) FALSE в обратном случае два вектора коллинеарны, если их векторное произведение равно нулевому вектору (у которого все компоненты равны нулю) */ int v_collinear(Vector v1, Vector v2) { // создаем массив из двух векторов Vector vectors[2] = {v1, v2}; // пихаем этот массив в va_cross, чтобы получить векторное произведение Vector cross = va_cross(vectors, 2); // смотрим на координаты получившегося вектора for (int i = 0; i < cross.size; i++) { if (cross.data[i] != 0) { // если хоть одна компонента вектора не равна нулю, возвращаем false return FALSE; } } // если все ок, возвращаем true return TRUE; } /* проверяет массив векторов (vector array) на коллинеарность в качестве аргументов указывается массив векторов (vectors) и количество векторов в нем (count) */ int va_collinear(Vector* vectors, int count) { // эта операция определена только на массиве векторов одинаковой размерности // мы будем использовать функцию v_collinear, которая отбрасывает векторы // разной размерности (возвращает FALSE) // начиная со второго вектора for (int i = 1; i < count; i++) { // проверяем на не коллинеарность первого вектора со всеми остальными // используя оператор "!" (логическое "не") и функцию v_collinear if (!v_collinear(vectors[i], vectors[0])) { // если хоть один из векторов не коллинеарен с первым, возвращаем FALSE return FALSE; } } // если все ок, возвращаем TRUE return TRUE; } /* проверяет массив векторов (vector array) на компланарность операция определена для двух и более векторов 2 вектора всегда компланарны 3 вектора компланарны, если их смешанное произведение равно нулю n векторов компланарны, если ??? */ int va_complanar(Vector* vectors, int count) { // НЕ ДОДЕЛАНА // два или один вектор всегда лежат в одной плоскости if (count <= 2) { return TRUE; } // три вектора if (count == 3) { // получаем векторное произведение b и c Vector bc[2] = {vectors[1], vectors[2]}; Vector cross = va_cross(bc, 2); // получаем скалярное произведение a и (b cross c) double dot = v_dot(vectors[0], cross); // cross уже не нужен, уничтожаем его, чтобы не прое**ть выделенную память v_destroy(cross); // если результат равен нулю, значит три вектора компланарны if (dot == 0) { return TRUE; } } return FALSE; } /* скалярное произведение векторов (англ. dot product) пример: v1 = {1, 2, 3} v2 = {4, 5, 6} v_dot(v1, v2) = 1*4 + 2*5 + 3*6 */ double v_dot(Vector v1, Vector v2) { // вообще, эта операция определена только на векторах одинаковой размерности // но если они отличаются, то мы просто перемножим те пары компонент, которые // есть у обоих векторов, а все остальное проигнорируем. // пример: // {5, 5} // {1, 2, 3, 4} // ^ ^ // игнорируем // вычислим, у какого из векторов размерность меньше int min_size = v1.size; if (v2.size < v1.size) { min_size = v2.size; } // в цикле сложим произведения только существующих пар компонентов double sum = 0; for (int i = 0; i < min_size; i++) { sum += v1.data[i] * v2.data[i]; } return sum; } /* векторное произведение векторов (англ. cross product) на входе принимает массив из векторов vectors и кол-во векторов count на выходе возвращает count-мерный вектор, перпендикулярный заданным в случае ошибки, возвращает 0-мерный вектор векторное произведение count векторов определено на count+1 мерном пространстве. для выполнения операции в 2D пространстве, тебе понадобится 1 вектор. в 3D, тебе понадобится 2 вектора, в 4D понадобится 3 вектора и т.д. в 2D пространстве ты находишь вектор, перпендикулярный заданной линии. в 3D ты находишь вектор, перпендикулярный заданной плоскости. в 4D, я предполагаю, что ты находишь вектор, перпендикулярный заданному объему в четырехмерном пространстве. теперь о том, как эта функция работает: по сути тут просто идет вычисление через детерминанты (det) суб-матриц, как и с обычным векторным произведением, только для пространства любой размерности. на всякий случай загляни в гугл картинки с запросом "определитель матрицы", так будет понятнее. пример: a = {a1, a2, a3, a4} b = {b1, b2, b3, b4} c = {c1, c2, c3, c4} e1 = {1, 0, 0, 0} e2 = {0, 1, 0, 0} e3 = {0, 0, 1, 0} e4 = {0, 0, 0, 1 |e1 e2 e3 e4| a x b x c = det|a1 a2 a3 a4| |b1 b2 b3 b4| |c1 c2 c3 c4| |a2 a3 a4| |a3 a4 a1| |a4 a1 a2| |a1 a2 a3| = e1*det|b2 b3 b4| + e2*det|b3 b4 b1| + e3*det|b4 b1 b2| + e4*det|b1 b2 b3| |c2 c3 c4| |c3 c4 c1| |c4 c1 c2| |c1 c2 c3| = e1*(a2*b3*c4 + a3*b4*c2 + a4*b2*c3 - a4*b3*c2 - a3*b2*c4 - a2*b4*c3) + e2* ... + e3* ... + e4* ... = {1, 0, 0, 0} * (a2*b3*c4 + a3*b4*c2 + a4*b2*c3 - a4*b3*c2 - a3*b2*c4 ...) + {0, 1, 0, 0} * (...) + {0, 0, 1, 0} * (...) + {0, 0, 0, 1} * (...) = {r1, r2, r3, r4} <= сложили 4 вектора и получили 1 вектор т.е. это обычный детерминант, только в качестве элементов первого ряда стоят так называемые "базис вектора" e1, e2, и т.д., и на них умножается крест- накрест компоненты векторов. */ Vector va_cross(Vector* vectors, int count) { // чтобы не вычислять постоянно размерность (count + 1), запишем в переменную int size = count + 1; // проверяем, у всех ли count векторов размерность равна count+1 for (int i = 0; i < count; i++) { if (vectors[i].size != size) { // ошибка, возвращаем пустой вектор нулевой размерности (0-мерный вектор) return v_create(0, NULL); } } // если все ок, то создаем (count+1)-мерный вектор. передаем NULL вместо // массива чисел, чтобы все компоненты заполнились нулями (см. v_create) Vector result = v_create(size, NULL); // сначала небольшой костыль для работы с 2D векторами: // vector = {x, y} // result = {y, -x} <- провто разворачиваем вектор на 90 градусов по часовой if (size == 2) { result.data[0] = vectors[0].data[1]; result.data[1] = -vectors[0].data[0]; return result; } // далее идет алгоритм который рассчитан на работу с размерностями начиная от // 3 и более. // вычисляем компоненты нового вектора for (int r = 0; r < size; r++) { // суб-детерминант. сюда будем прибавлять перемноженные накрест ячейки. // все переменные, объявленные внутри цикла, создаются заново каждый раз. double sub_det = 0; // вычисляем суб-детерминант для result[r] for (int offset = 0; offset < count; offset++) { // e0, e1, e2, ... en всегда равны 1. // мы перемножаем диагонали. основную диагональ (сверху-вниз-вправо) затем // прибавляем к суб-детерминанту, а побочную (снизу-вверх-вправо) вычитаем. // для каждой диагонали будет своя переменная, изначально равная 1, // которая потом умножается на числа ячейках. double positive_mul = 1; // основная double negative_mul = 1; // побочная // проходимся по массиву vectors for (int w = 0; w < count; w++) { // индекс вычислялся путем сдвига в зависимости от следующих параметров: // r - текущая компонента вектора result // w - текущий вектор из массива vectors // offset - сдвиг диагонали, по которой мы умножаем, вправо // // r0 r1 r2 // |1 1 1 | // w0 > | | // w1 > | | // ^ ^ ^ // offset // // при вычислении индекса необходимой ячейки используется оператор // остатка от целочисленного деления "%" // с помощью него мы "ходим по кругу" по ячейкам от 0 до size-1 // пример: // 0 % 3 = 0 // 1 % 3 = 1 // 2 % 3 = 2 // 3 % 3 = 0 // 4 % 3 = 1 // 5 % 3 = 2 // ... int index = (r + w + offset + 1) % size; // если мы наткнулись на ячейку столбца, который вычеркнут в данный // момент (для r = 0 вычеркнут весь столбец 0), то сдвигаем index на 2 // в правую сторону if (index == r) { index = (index + 2) % size; } // сверху-вниз вправо // v v positive_mul *= vectors[w].data[index]; negative_mul *= vectors[count - 1 - w].data[index]; // ^ ^ // снизу-вверх вправо } sub_det += positive_mul; sub_det -= negative_mul; } result.data[r] = sub_det; } return result; } /* main */ int main() { Vector a = v_create(3, (double[]) { 1, 2, 3 }); Vector b = v_create(3, (double[]) { 4, 5, 6 }); Vector vectors[] = {a, b}; Vector cross = va_cross(vectors, 2); printf("\nCross result:\n"); v_print(cross); /* // создаем пустой 2D вектор Vector a; a = v_create(3, NULL); // можно и сразу написать Vector a = v_create(2, NULL) // задаем координаты вручную a.data[0] = 1.0; a.data[1] = 2.0; a.data[3] = 3.0; // выводим его на экран v_print(a); printf("\n"); // уничтожаем вектор, освобождая выделенную под его компоненты память v_destroy(a); // теперь a.size == 0, a.data == NULL // теперь попробуем создать 2D вектор, скопировав значения из массива чисел double array[3] = {3.0, 1.0, 1.0}; a = v_create(3, array); // снова используем переменную a // так можно делать только после v_destroy(a), иначе // прое**м кусок памяти, выделенный ранее под a.data Vector b = v_create(3, (double[]) {2.0, 2.0, 3.0}); Vector vectors[2] = {a, b}; printf("Cross product\n"); Vector cross = va_cross(vectors, 2); printf("\nResult\n"); v_print(cross); printf("Dot product\n"); printf("%lf\n", v_dot(a, b)); */ /* // можно создать вектор, копируя значения из заранее созданного массива double numbers[3] = { 1, 2.5, 3.14 }; Vector a = v_create(3, numbers); // а можно задать массив чисел прямо в вызове функции Vector b = v_create(3, (double[]) { 2, 5.0, 6.28 }); // ^ // нужно заранее сказать компилятору, что это массив // double, иначе будет ошибка // выводим эти векторы printf("Vectors:\n"); v_print(a); printf("\n"); v_print(b); // првоерка на равность if (v_equal(a, b)) { printf("Vectors are equal!\n"); } // и на неравность (добавив "!", т.е. "не") if (!v_equal(a, b)) { printf("Vectors are not equal!\n"); } // пробуем считать вектор из файла и вывести его на экран: Vector v1 = v_load("v1.txt"); printf("v1 = "); v_print(v1); // прибавляем b к v1 и выводим результат на экран v_add(v1, b); printf("v1 + b = "); v_print(v1); // сохраняем v1 в файл result.txt v_save(v1, "result.txt"); // теперь попробуем создать вектор введя его из консоли printf("Reading from console:\n"); Vector v2 = v_input(); // <- эта функция запустит ввод из консоли // выводим получившийся вектор printf("v2 = "); v_print(v2); */ return 0; }
Editor Settings
Theme
Key bindings
Full width
Lines