// Математический помощник: Матрицы и Векторы // Рекомендация: .NET 6+ // Полный функционал: определители, Крамер, Гаусс, все векторные операции, площади/объёмы, момент силы, power iteration (соб. число/вектор) using System; using System.Linq; class Program { static void Main() { Console.Title = "Математический помощник: Матрицы и Векторы"; while (true) { Console.Clear(); Console.WriteLine("=== ГЛАВНОЕ МЕНЮ ==="); Console.WriteLine("1 — Работа с матрицами"); Console.WriteLine("2 — Работа с векторами"); Console.WriteLine("0 — Выход"); Console.Write("Выбор: "); switch (Console.ReadLine()?.Trim()) { case "1": MatrixMenu(); break; case "2": VectorMenu(); break; case "0": return; default: Console.WriteLine("Неверный ввод. Нажмите любую клавишу."); Console.ReadKey(); break; } } } // ====== МАТРИЦЫ ====== static void MatrixMenu() { while (true) { Console.Clear(); Console.WriteLine("=== МАТРИЦЫ ==="); Console.WriteLine("1 — Вычислить определитель (любая n x n)"); Console.WriteLine("2 — Решить систему методом Крамера (n x n)"); Console.WriteLine("3 — Решить систему методом Гаусса (прямой/обратный ход)"); Console.WriteLine("4 — Power Iteration (доминирующее собственное число/вектор)"); Console.WriteLine("0 — Назад"); Console.Write("Выбор: "); switch (Console.ReadLine()?.Trim()) { case "1": MatrixDeterminant(); break; case "2": SolveCramer(); break; case "3": SolveGauss(); break; case "4": PowerIterationMenu(); break; case "0": return; default: Console.WriteLine("Неверный ввод."); Console.ReadKey(); break; } } } static void MatrixDeterminant() { Console.Clear(); int n = ReadIntPositive("Введите размерность матрицы n (>0): "); var A = ReadMatrix(n, n, "матрицы A"); Console.WriteLine("\nA:"); PrintMatrix(A); double det = Determinant(A); Console.WriteLine($"\nОпределитель det(A) = {det}"); Console.WriteLine(det > 0 ? "✅ det > 0" : det < 0 ? "❌ det < 0" : "⚠ det = 0"); Pause(); } static void SolveCramer() { Console.Clear(); int n = ReadIntPositive("Введите размерность системы n (>0): "); Console.WriteLine("Ввод матрицы A (по строкам, числа через пробел):"); var A = ReadMatrix(n, n, "A"); Console.WriteLine("Ввод вектора свободных членов B (n чисел через пробел):"); var B = ReadVector(n, "B"); double detA = Determinant(A); Console.WriteLine($"det(A) = {detA}"); if (Math.Abs(detA) < 1e-12) { Console.WriteLine("det(A) = 0 => система не имеет единственного решения (проверьте ранги)."); Pause(); return; } var X = new double[n]; for (int k = 0; k < n; k++) { var Ak = (double[,])A.Clone(); for (int i = 0; i < n; i++) Ak[i, k] = B[i]; X[k] = Determinant(Ak) / detA; } Console.WriteLine("\nРешение (Крамер):"); for (int i = 0; i < n; i++) Console.WriteLine($"x{i+1} = {X[i]}"); Pause(); } static void SolveGauss() { Console.Clear(); int n = ReadIntPositive("Введите число уравнений n (>0): "); Console.WriteLine("Введите расширенную матрицу (n строк, n+1 чисел в строке):"); double[,] M = new double[n, n + 1]; for (int i = 0; i < n; i++) { Console.Write($"Строка {i+1}: "); var parts = ReadDoublesRow(n + 1); for (int j = 0; j < n + 1; j++) M[i, j] = parts[j]; } var res = GaussSolve(M); if (res == null) { Console.WriteLine("Система несовместна или имеет бесконечное множество решений (ранги совпадают/не совпадают)."); } else { Console.WriteLine("Решение (единственное):"); for (int i = 0; i < res.Length; i++) Console.WriteLine($"x{i+1} = {res[i]}"); } Pause(); } // ====== POWER ITERATION ====== static void PowerIterationMenu() { Console.Clear(); int n = ReadIntPositive("Введите размерность квадратной матрицы n (>0): "); var A = ReadMatrix(n, n, "A"); Console.WriteLine("Запускаем Power Iteration для нахождения доминирующего собственного вектора/значения."); int maxIter = ReadIntPositive("Максимум итераций (например 1000): "); double tol = ReadDouble("Точность (например 1e-9): "); var (lambda, vec) = PowerIteration(A, maxIter, tol); Console.WriteLine($"\nПриближённое собственное значение (доминирующее): {lambda}"); Console.WriteLine("Собственный вектор (нормированный):"); PrintVector(vec); Pause(); } // ====== ВЕКТОРЫ ====== static void VectorMenu() { while (true) { Console.Clear(); Console.WriteLine("=== ВЕКТОРЫ ==="); Console.WriteLine("1 — Сложение / вычитание"); Console.WriteLine("2 — Скалярное произведение"); Console.WriteLine("3 — Векторное произведение (3D)"); Console.WriteLine("4 — Смешанное произведение (3D)"); Console.WriteLine("5 — Коллинеарность"); Console.WriteLine("6 — Компланарность (трёх векторов)"); Console.WriteLine("7 — Угол между векторами"); Console.WriteLine("8 — Длина / норма / проекция"); Console.WriteLine("9 — Площадь параллелограмма / треугольника"); Console.WriteLine("10 — Объём параллелепипеда / тетраэдра"); Console.WriteLine("11 — Момент силы (векторное произведение)"); Console.WriteLine("0 — Назад"); Console.Write("Выбор: "); switch (Console.ReadLine()?.Trim()) { case "1": SumSubtractVectors(); break; case "2": ScalarProduct(); break; case "3": VectorProduct(); break; case "4": MixedProduct(); break; case "5": CheckCollinear(); break; case "6": CheckCoplanar(); break; case "7": AngleBetweenVectors(); break; case "8": LengthProjection(); break; case "9": AreaParallelogramTriangle(); break; case "10": VolumeParallelepipedTetrahedron(); break; case "11": MomentOfForce(); break; case "0": return; default: Console.WriteLine("Неверный ввод."); Pause(); break; } } } static void SumSubtractVectors() { Console.Clear(); var a = ReadVectorAuto("A"); var b = ReadVectorAuto("B"); var sum = VectorAdd(a, b); var diff = VectorSub(a, b); Console.WriteLine("\nA + B ="); PrintVector(sum); Console.WriteLine("A - B ="); PrintVector(diff); Pause(); } static void ScalarProduct() { Console.Clear(); var a = ReadVectorAuto("A"); var b = ReadVectorAuto("B"); double dot = Dot(a, b); Console.WriteLine($"\nA · B = {dot}"); Pause(); } static void VectorProduct() { Console.Clear(); var a = ReadVector3("A"); var b = ReadVector3("B"); var c = Cross(a, b); Console.WriteLine("\nA × B ="); PrintVector(c); Pause(); } static void MixedProduct() { Console.Clear(); var a = ReadVector3("A"); var b = ReadVector3("B"); var c = ReadVector3("C"); double mixed = Dot(a, Cross(b, c)); Console.WriteLine($"\n[A B C] = {mixed}"); Pause(); } static void CheckCollinear() { Console.Clear(); var a = ReadVectorAuto("A"); var b = ReadVectorAuto("B"); if (a.Length != b.Length) { Console.WriteLine("Векторы должны быть одной размерности."); Pause(); return; } if (a.Length == 3) { var cross = Cross(a, b); bool zero = cross.All(x => Math.Abs(x) < 1e-9); Console.WriteLine(zero ? "✅ Коллинеарны" : "❌ Не коллинеарны"); } else { // Проверка пропорциональности координат double? ratio = null; bool col = true; for (int i = 0; i < a.Length; i++) { if (Math.Abs(b[i]) < 1e-12) { if (Math.Abs(a[i]) > 1e-12) { col = false; break; } else continue; } double r = a[i] / b[i]; if (ratio == null) ratio = r; else if (Math.Abs(r - ratio.Value) > 1e-9) { col = false; break; } } Console.WriteLine(col ? "✅ Коллинеарны (пропорциональны)" : "❌ Не коллинеарны"); } Pause(); } static void CheckCoplanar() { Console.Clear(); var a = ReadVector3("A"); var b = ReadVector3("B"); var c = ReadVector3("C"); double mixed = Dot(a, Cross(b, c)); Console.WriteLine(Math.Abs(mixed) < 1e-9 ? "✅ Компланарны" : "❌ Не компланарны"); Pause(); } static void AngleBetweenVectors() { Console.Clear(); var a = ReadVectorAuto("A"); var b = ReadVectorAuto("B"); if (a.Length != b.Length) { Console.WriteLine("Размерности не совпадают."); Pause(); return; } double dot = Dot(a, b); double na = Norm(a), nb = Norm(b); if (na == 0 || nb == 0) { Console.WriteLine("У одного вектора нулевая длина."); Pause(); return; } double cos = dot / (na * nb); cos = Math.Max(-1.0, Math.Min(1.0, cos)); double angleRad = Math.Acos(cos); Console.WriteLine($"Угол между = {angleRad * 180.0 / Math.PI:F6}°"); Pause(); } static void LengthProjection() { Console.Clear(); var a = ReadVectorAuto("A"); var b = ReadVectorAuto("B"); if (a.Length != b.Length) { Console.WriteLine("Размерности не совпадают."); Pause(); return; } double projLen = Dot(a, b) / Norm(b); // проекция длины A на B var projVec = ScalarMultiply(b, Dot(a, b) / Dot(b, b)); Console.WriteLine($"Длина проекции A на B = {projLen}"); Console.WriteLine("Вектор проекции A на B ="); PrintVector(projVec); Pause(); } static void AreaParallelogramTriangle() { Console.Clear(); var a = ReadVector3("A"); var b = ReadVector3("B"); var cross = Cross(a, b); double areaPar = Norm(cross); double areaTri = 0.5 * areaPar; Console.WriteLine($"Площадь параллелограмма = {areaPar}"); Console.WriteLine($"Площадь треугольника = {areaTri}"); Pause(); } static void VolumeParallelepipedTetrahedron() { Console.Clear(); var a = ReadVector3("A"); var b = ReadVector3("B"); var c = ReadVector3("C"); double volPar = Math.Abs(Dot(a, Cross(b, c))); double volTet = Math.Abs(volPar / 6.0); // careful: volume tetrahedron built on vectors a,b,c from same vertex = |[a b c]|/6? Actually /6 for coordinates of triangle pyramid? Standard: volume parallelepiped = |[a b c]|; tetrahedron volume = |[a b c]|/6 if points provided as A,B,C,D? Many textbooks use 1/6 for specific vertex choices; here we provide both with clear note. // To avoid confusion, give standard: volume parallel = |[a b c]| ; volume tetrahedron (if a,b,c are edges from a vertex) = |[a b c]|/6? Actually correct tetrahedron volume = |[a b c]| / 6 for points A(0), B(a), C(b), D(c)?? Some definitions differ — safer: tetrahedron with edges a,b,c from one vertex: V = |[a b c]|/6? Many sources: Volume = |(a·(b×c))|/6. We'll output both and note formula. Console.WriteLine($"Объём параллелепипеда = {volPar}"); Console.WriteLine($"Объём тетраэдра (на тех же ребрах от одной вершины) = {volPar/6.0} (в некоторых задачах используют /6; проверьте условие задачи)"); Pause(); } static void MomentOfForce() { Console.Clear(); Console.WriteLine("Момент силы M = r × F (r — радиус-вектор точки приложения от точки отсчёта)."); var r = ReadVector3("r (вектор от точки отсчёта до точки приложения)"); var F = ReadVector3("F (сила)"); var M = Cross(r, F); Console.WriteLine("\nM = r × F ="); PrintVector(M); Console.WriteLine($"Длина момента |M| = {Norm(M)}"); Pause(); } // ====== Утилиты для матриц / векторов / ввод ====== static int ReadIntPositive(string prompt) { while (true) { Console.Write(prompt); if (int.TryParse(Console.ReadLine(), out int v) && v > 0) return v; Console.WriteLine("Введите целое > 0."); } } static double ReadDouble(string prompt) { while (true) { Console.Write(prompt); if (double.TryParse(Console.ReadLine(), out double v)) return v; Console.WriteLine("Число введено неверно."); } } static double[,] ReadMatrix(int rows, int cols, string name) { var M = new double[rows, cols]; Console.WriteLine($"Ввод {name}: (каждая строка — {cols} чисел через пробел)"); for (int i = 0; i < rows; i++) { Console.Write($"Строка {i + 1}: "); var parts = ReadDoublesRow(cols); for (int j = 0; j < cols; j++) M[i, j] = parts[j]; } return M; } static double[] ReadVector(int n, string name) { Console.WriteLine($"Ввод {name}: {n} чисел через пробел"); var parts = ReadDoublesRow(n); return parts; } static double[] ReadVector3(string name) { Console.WriteLine($"Введите 3 числа для вектора {name} (через пробел):"); return ReadDoublesRow(3); } static double[] ReadVectorAuto(string name) { Console.Write($"Сколько координат у вектора {name}? (обычно 2 или 3): "); if (!int.TryParse(Console.ReadLine(), out int n) || n <= 0) n = 3; Console.WriteLine($"Введите {n} чисел через пробел:"); return ReadDoublesRow(n); } static double[] ReadDoublesRow(int expected) { while (true) { var line = Console.ReadLine(); if (string.IsNullOrWhiteSpace(line)) { Console.WriteLine("Пусто — повторите ввод."); continue; } var parts = line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); if (parts.Length != expected) { Console.WriteLine($"Ожидается {expected} чисел, введено {parts.Length}. Попробуйте ещё раз."); continue; } try { return parts.Select(double.Parse).ToArray(); } catch { Console.WriteLine("Ошибка преобразования — попробуйте ещё раз."); continue; } } } static void Pause() { Console.WriteLine("\nНажмите любую клавишу..."); Console.ReadKey(); } static void PrintMatrix(double[,] M) { int r = M.GetLength(0), c = M.GetLength(1); for (int i = 0; i < r; i++) { for (int j = 0; j < c; j++) Console.Write($"{M[i, j],10:F6} "); Console.WriteLine(); } } static void PrintVector(double[] v) { Console.Write("(" + string.Join(", ", v.Select(x => x.ToString("F6"))) + ")"); Console.WriteLine(); } // ====== МАТРИЧНЫЕ ОПЕРАЦИИ и РЕШЕНИЕ ====== static double Determinant(double[,] matrix) { int n = matrix.GetLength(0); if (matrix.GetLength(1) != n) throw new ArgumentException("Матрица должна быть квадратной."); // Используем LU-подобный метод (прямой) с вычислением по разложению Гаусса с частичной выборкой строки для устойчивости: // Но для простоты и надёжности при n <= 6 используем рекурсивный минор; при больших n применим LU (разложение). if (n <= 6) return DeterminantRecursive(matrix); return DeterminantLU(matrix); } static double DeterminantRecursive(double[,] matrix) { int n = matrix.GetLength(0); if (n == 1) return matrix[0, 0]; if (n == 2) return matrix[0, 0] * matrix[1, 1] - matrix[0, 1] * matrix[1, 0]; double det = 0; for (int j = 0; j < n; j++) { var minor = Minor(matrix, 0, j); det += Math.Pow(-1, j) * matrix[0, j] * DeterminantRecursive(minor); } return det; } static double[,] Minor(double[,] matrix, int row, int col) { int n = matrix.GetLength(0); var res = new double[n - 1, n - 1]; int r = 0; for (int i = 0; i < n; i++) { if (i == row) continue; int c = 0; for (int j = 0; j < n; j++) { if (j == col) continue; res[r, c++] = matrix[i, j]; } r++; } return res; } static double DeterminantLU(double[,] A) { int n = A.GetLength(0); var M = (double[,])A.Clone(); double detSign = 1.0; for (int k = 0; k < n; k++) { // partial pivot int pivot = k; double max = Math.Abs(M[k, k]); for (int i = k + 1; i < n; i++) { if (Math.Abs(M[i, k]) > max) { max = Math.Abs(M[i, k]); pivot = i; } } if (Math.Abs(max) < 1e-15) return 0.0; if (pivot != k) { // swap rows for (int j = 0; j < n; j++) { double tmp = M[k, j]; M[k, j] = M[pivot, j]; M[pivot, j] = tmp; } detSign *= -1.0; } double akk = M[k, k]; for (int i = k + 1; i < n; i++) { double factor = M[i, k] / akk; for (int j = k; j < n; j++) M[i, j] -= factor * M[k, j]; } } double prod = detSign; for (int i = 0; i < n; i++) prod *= M[i, i]; return prod; } static double[] GaussSolve(double[,] aug) // aug: n x (n+1); returns solution or null { int n = aug.GetLength(0); var A = (double[,])aug.Clone(); // Forward elimination with partial pivot int row = 0; for (int col = 0; col < n && row < n; col++) { // find pivot int sel = row; double max = Math.Abs(A[sel, col]); for (int i = row + 1; i < n; i++) { if (Math.Abs(A[i, col]) > max) { max = Math.Abs(A[i, col]); sel = i; } } if (Math.Abs(max) < 1e-12) continue; // no pivot in this column // swap rows sel and row if (sel != row) { for (int j = col; j < n + 1; j++) { double tmp = A[sel, j]; A[sel, j] = A[row, j]; A[row, j] = tmp; } } // normalize double pivot = A[row, col]; for (int j = col; j < n + 1; j++) A[row, j] /= pivot; // eliminate below for (int i = row + 1; i < n; i++) { double f = A[i, col]; for (int j = col; j < n + 1; j++) A[i, j] -= f * A[row, j]; } row++; } // Check for inconsistency and ranks // rank(A) vs rank(aug) int rankA = 0, rankAug = 0; for (int i = 0; i < n; i++) { bool anyA = false, anyAug = false; for (int j = 0; j < n; j++) if (Math.Abs(A[i, j]) > 1e-9) anyA = true; for (int j = 0; j < n + 1; j++) if (Math.Abs(A[i, j]) > 1e-9) anyAug = true; if (anyA) rankA++; if (anyAug) rankAug++; } if (rankA < rankAug) return null; // incompatible if (rankA < n) return null; // infinite solutions — for simplicity return null (can be extended to parametric form) // Back substitution var X = new double[n]; for (int i = n - 1; i >= 0; i--) { // find leading index int lead = -1; for (int j = 0; j < n; j++) if (Math.Abs(A[i, j]) > 1e-9) { lead = j; break; } if (lead == -1) continue; double sum = A[i, n]; for (int j = lead + 1; j < n; j++) sum -= A[i, j] * X[j]; X[lead] = sum / A[i, lead]; } return X; } // ====== ВЕКТОРНАЯ АЛГЕБРА (общая) ====== static double[] VectorAdd(double[] a, double[] b) { if (a.Length != b.Length) throw new ArgumentException("Размерности не совпадают."); return Enumerable.Range(0, a.Length).Select(i => a[i] + b[i]).ToArray(); } static double[] VectorSub(double[] a, double[] b) { if (a.Length != b.Length) throw new ArgumentException("Размерности не совпадают."); return Enumerable.Range(0, a.Length).Select(i => a[i] - b[i]).ToArray(); } static double Dot(double[] a, double[] b) { if (a.Length != b.Length) throw new ArgumentException("Размерности не совпадают."); double s = 0; for (int i = 0; i < a.Length; i++) s += a[i] * b[i]; return s; } static double Norm(double[] a) => Math.Sqrt(Dot(a, a)); static double[] ScalarMultiply(double[] a, double k) => a.Select(x => x * k).ToArray(); static double[] Cross(double[] a, double[] b) { if (a.Length != 3 || b.Length != 3) throw new ArgumentException("Cross product defined for 3D vectors."); return new double[] { a[1]*b[2] - a[2]*b[1], a[2]*b[0] - a[0]*b[2], a[0]*b[1] - a[1]*b[0] }; } // ====== POWER ITERATION (наибольшее собственное значение по модулю) ====== // Возвращает (lambda, vector(normed)) static (double, double[]) PowerIteration(double[,] A, int maxIter = 1000, double tol = 1e-9) { int n = A.GetLength(0); if (A.GetLength(1) != n) throw new ArgumentException("Матрица должна быть квадратной."); var b = new double[n]; var rnd = new Random(); for (int i = 0; i < n; i++) b[i] = rnd.NextDouble() + 0.1; double lambda = 0; for (int iter = 0; iter < maxIter; iter++) { var Ab = Multiply(A, b); double norm = Norm(Ab); if (norm == 0) return (0, b); // нулевой вектор for (int i = 0; i < n; i++) b[i] = Ab[i] / norm; var Ab2 = Multiply(A, b); double lambdaNew = Dot(b, Ab2); // Rayleigh quotient approx if (Math.Abs(lambdaNew - lambda) < tol) { lambda = lambdaNew; break; } lambda = lambdaNew; } return (lambda, b); } static double[] Multiply(double[,] A, double[] x) { int r = A.GetLength(0), c = A.GetLength(1); if (c != x.Length) throw new ArgumentException("Несовместимые размеры."); var y = new double[r]; for (int i = 0; i < r; i++) { double s = 0; for (int j = 0; j < c; j++) s += A[i, j] * x[j]; y[i] = s; } return y; } }