// Математический помощник: Матрицы и Векторы
// Рекомендация: .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;
}
}