第1关:矩阵(方阵)行列式
任务描述
本关任务:掌握特殊矩阵(方阵)的行列式的概念,编程实现n阶行列式的值。例如2阶方阵:,它的行列式的值为:Det(A)=1×4−2×3=−2。
相关知识
为了完成本关任务,你需要掌握:1.行列式的定义。
行列式的定义
当一个矩阵的行数和列数相等时,称这个矩阵为方阵,若行数为1,则称之为一阶方阵,若行数为2,则称之为二阶方阵,以此类推,若行数为n,则称之为n阶方阵。
一阶方阵只有一个元素,它的行列式的值就是本身。记方阵为A,行列式的值用D表示,则D=Det(A)。
二阶方阵每行每列都是两个元素,设二阶方阵,那么二阶行列式记为,并称a
ij
(i,j=1,2)为行列式的元素。二阶行列式即是由四个元素排列成两行两列,并按一定规则得到的一个值。例如:
三阶行列式则是形如的数据块,即由9个元素排成的三行三列,并按一定规则得到的一个值,记三阶行列式为D,则。例如:
n阶行列式为由n
2
个元素a
ij
(i,j=1,2,..,n)排成n行n列,并按一定规则得到的一个值,记为:,其值为n!项之和:,其中是将序列的元素次序交换k次所得到的一个序列,k即为交换后的序列相对原序列的逆序数(在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数)。
编程要求
本关的编程任务是补全右侧代码片段Determinant_1、Determinant_2、Determinant_3、Inverse_Number和Determinant_n中Begin至End中间的代码,具体要求如下:
在Determinant_1中,计算一阶方阵的行列式,并返回行列式的值。
在Determinant_2中,计算二阶方阵的行列式,并返回行列式的值。
在Determinant_3中,计算三阶方阵的行列式,并返回行列式的值。
在Inverse_Number中,计算由n个数组成的序列(整型数组a)的逆序数,并返回逆序数值。
在Determinant_n中,计算n阶方阵的行列式,并返回行列式的值。
测试说明
平台将自动编译补全后的代码,并生成若干组测试数据,接着根据程序的输出判断程序是否正确。
以下是平台的测试样例:
测试输入:
2
1 2
3 4
预期输出:
-2
开始你的任务吧,祝你成功!
//
// main.cpp
// step1
//
// Created by ljpc on 2018/11/29.
// Copyright © 2018年 ljpc. All rights reserved.
//
#include <iostream>
#include <algorithm>
using namespace std;
int det(int n, int** mat)
{
int mov = 0;
int flag;
int sum = 0;
if (n == 1) return mat[0][0];
int** b = new int* [n - 1];
for (int z = 0; z < n - 1; ++z)
b[z] = new int[n - 1];
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < n - 1; ++j) {
mov = i > j ? 0 : 1;
for (int k = 0; k < n - 1; ++k)
b[j][k] = mat[j + mov][k + 1];
}
if (i % 2 == 0) flag = 1;
else flag = -1;
sum += flag * mat[i][0] * det(n - 1, b);
}
delete[] b;
return sum;
}
struct Matrix{
int m, n;
int **val;
Matrix(){}
Matrix(int m_, int n_){
m = m_;
n = n_;
this->val = (int**)malloc(sizeof(int*)*m);
for(int i=0;i<m;i++){
this->val[i] = (int*)malloc(sizeof(int)*n);
}
}
void in(){
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
scanf("%d", &this->val[i][j]);
}
}
}
void out(){
for(int i=0;i<m;i++){
printf("%d", this->val[i][0]);
for(int j=1;j<n;j++){
printf(" %d", this->val[i][j]);
}
printf("\n");
}
}
int Determinant_1 (){
// 请在这里补充代码,完成本关任务
/********* Begin *********/
return val[0][0];
/********* End *********/
}
int Determinant_2 (){
// 请在这里补充代码,完成本关任务
/********* Begin *********/
return (val[0][0] * val[1][1] - val[0][1] * val[1][0]);
/********* End *********/
}
int Determinant_3 (){
// 请在这里补充代码,完成本关任务
/********* Begin *********/
int left = val[0][0] * val[1][1] * val[2][2] + val[0][1] * val[1][2] * val[2][0] + val[1][0] * val[2][1] * val[0][2];
int right = val[2][0] * val[1][1] * val[0][2] + val[1][0] * val[0][1] * val[2][2] + val[2][1] * val[1][2] * val[0][0];
return left - right;
/********* End *********/
}
int Inverse_Number(int n, int arr[]){
// 请在这里补充代码,完成本关任务
/********* Begin *********/
int count = 0;
for (int i = 0; i < n - 1; i++) {
for (int j = i + 1; j < n; j++) {
if (arr[i] > arr[j]) {
count++;
break;
}
}
}
return count;
/********* End *********/
}
int Determinant_n (){
// 请在这里补充代码,完成本关任务
/********* Begin *********/
int res = det(this->n, this->val);
return res;
/********* End *********/
}
int Determinant (){
if(this->n==1){
return Determinant_1();
}else if(this->n==2){
return Determinant_2();
}else if(this->n==3){
return Determinant_3();
}else {
return Determinant_n();
}
}
};
int main(int argc, const char * argv[]) {
int n;
scanf("%d", &n);
Matrix A(n,n);
A.in();
int det = A.Determinant();
printf("Det(A)=%d\n", det);
return 0;
}
第2关:矩阵逆运算
任务描述
本关任务:掌握特殊矩阵(方阵)的逆的概念,编程实现n阶矩阵的逆矩阵。
例如:矩阵,其逆矩阵。
相关知识
为了完成本关任务,你需要掌握:1.单位矩阵的定义,2.代数余子式,3.伴随矩阵,4.逆矩阵的定义。
单位矩阵的定义
单位矩阵的定义:从左上角到右下角的对角线(称为主对角线)上的元素均为1,除此以外全都为0。
代数余子式
设矩阵A=(a
ij
),将矩阵A的元素aij所在的第i行第j列去掉后,余下的(n−1)
2
项按原来的排列顺序组合成的(n−1)阶矩阵所确定的行列式称为元素aij的余子式,即为Mij,称Aij=(−1)
i+j
Mij为元素aij的代数余子式。
伴随矩阵
矩阵A=(a
ij
)
n×n
的各元素的代数余子式Aij所构成的如下矩阵A
∗
:
该矩阵A
∗
称为矩阵A的伴随矩阵。
逆矩阵的定义
给定整数a和b,且b
=0,则a除以b可写为:
b
a
=ab
−1
,在等式中,称b
−1
=
b
1
为b的逆元,显然有b
−1
b=bb
−1
=1。
对于矩阵来说,矩阵的除法可以归结为乘法运算。对于n阶矩阵(方阵)A,若存在n阶矩阵B,使得:AB=BA=I,其中I为单位矩阵,则称B为A的逆矩阵,并称A为可逆矩阵或可逆方阵,记A的逆矩阵为A
−1
。
特别的,由行列式的乘法可得等式∣A∣∣A
−1
∣=∣AA
−1
∣=∣I∣=1,因此∣A∣
=0。可见,要判断一个矩阵是否有逆矩阵存在,只需要判断该矩阵是否为非奇异矩阵(行列式的值不等于零)。
设矩阵A为n阶非奇异矩阵,则A有唯一的一个逆矩阵A
−1
:
其中,A
∗
为A的伴随矩阵,为行列式∣A∣中元素的代数余子式。
编程要求
本关的编程任务是补全右侧代码片段Cofactor、Adjugate_Matrix、Inverse_Matrix和Identity_Matrix中Begin至End中间的代码,具体要求如下:
在Cofactor中,根据矩阵余子式的定义,计算去掉矩阵第x行第y列元素后的余子式,并返回该余子式的值。
在Adjugate_Matrix中,根据伴随矩阵的定义,借助Cofactor函数计算矩阵(∗this)的伴随矩阵,并返回该伴随矩阵。
在Inverse_Matrix中,根据逆矩阵的定义,通过计算矩阵的行列式的值及其伴随矩阵,求得矩阵(∗this)的逆矩阵并返回。
在Identity_Matrix中,根据单位矩阵的定义,判断矩阵(∗this)是否为单位矩阵,若是,返回true,否则,返回false。
测试说明
平台将自动编译补全后的代码,并生成若干组测试数据,接着根据程序的输出判断程序是否正确。
以下是平台的测试样例:
测试输入:
2
1 2
3 4
预期输出:
-2.0000 1.0000
1.5000 -0.5000
开始你的任务吧,祝你成功!
//
// main.cpp
// step2
//
// Created by ljpc on 2018/11/29.
// Copyright © 2018年 ljpc. All rights reserved.
//
#include <iostream>
#include <algorithm>
using namespace std;
#define eps 1e-8
inline int sig(double x){return (x>eps)-(x<-eps); }
struct Matrix{
int m, n;
double **val;
Matrix(){}
Matrix(int m_, int n_){
m = m_;
n = n_;
this->val = (double**)malloc(sizeof(double*)*m);
for(int i=0;i<m;i++){
this->val[i] = (double*)malloc(sizeof(double)*n);
}
}
void in(){
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
scanf("%lf", &this->val[i][j]);
}
}
}
void out(){
for(int i=0;i<m;i++){
printf("%.4lf", this->val[i][0]);
for(int j=1;j<n;j++){
printf(" %.4lf", this->val[i][j]);
}
printf("\n");
}
}
Matrix operator * (const double r)const{
int m_ = this->m;
int n_ = this->n;
Matrix A = *this;
Matrix M(m_, n_);
for(int i=0;i<m_;i++){
for(int j=0;j<n_;j++){
M.val[i][j] = A.val[i][j] * r;
}
}
return M;
}
Matrix operator * (const Matrix B)const{
Matrix A = *this;
Matrix M(A.m, B.n);
if(A.n!=B.m){
printf("error\n");
}
for(int i=0;i<A.m;i++){
for(int j=0;j<B.n;j++){
double sum = 0;
for(int k=0;k<A.n;k++){
sum += A.val[i][k] * B.val[k][j];
}
M.val[i][j] = sum;
}
}
return M;
}
int Inverse_Number (int n, int arr[]) {
int num = 0;
for(int i=0;i<n;i++){
for(int j=0;j<i;j++){
if(arr[j]>arr[i]){
num++;
}
}
}
return num;
}
double Determinant () {
Matrix D = *this;
double det = 0;
int *arr;
arr = (int*)malloc(sizeof(int)*D.n);
for(int i=0;i<D.n;i++){
arr[i] = i;
}
do{
int inv = Inverse_Number(D.n, arr);
double tmp = (inv%2==0)?1:-1;
for(int i=0;i<D.n;i++){
tmp *= D.val[i][arr[i]];
}
det += tmp;
}while(next_permutation(arr, arr+D.n));
return det;
}
double Cofactor (int x, int y) {
// 请在这里补充代码,完成本关任务
/********* Begin *********/
Matrix C = Matrix(this->n-1,this->n-1);
for(int i=0;i<x;i++){
for(int j=0;j<y;j++){
C.val[i][j] = this->val[i][j];
}
}
for(int i=0;i<x;i++){
for(int j=y+1;j<this->n;j++){
C.val[i][j-1] = this->val[i][j];
}
}
for(int i=x+1;i<this->n;i++){
for(int j=0;j<y;j++){
C.val[i-1][j] = this->val[i][j];
}
}
for(int i=x+1;i<this->n;i++){
for(int j=y+1;j<this->n;j++){
C.val[i-1][j-1] = this->val[i][j];
}
}
double det_c = C.Determinant();
return det_c;
/********* End *********/
}
Matrix Adjugate_Matrix () {
// 请在这里补充代码,完成本关任务
/********* Begin *********/
Matrix A = Matrix(this->n, this->n);
for(int i=0;i<A.n;i++){
for(int j=0;j<A.n;j++){
int flag = ((i+j)%2==0)?1:-1;
int M_ij = Cofactor(i, j);
A.val[j][i] = flag * M_ij;
}
}
return A;
/********* End *********/
}
Matrix Inverse_Matrix (){
// 请在这里补充代码,完成本关任务
/********* Begin *********/
double det = Determinant();
if(det == 0){
printf("error\n");
return Matrix();
}
Matrix I = Matrix(this->n, this->n);
Matrix A = Matrix(this->n, this->n);
A = Adjugate_Matrix();
I = A * (1./det);
return I;
/********* End *********/
}
bool Identity_Matrix() {
// 请在这里补充代码,完成本关任务
/********* Begin *********/
for(int i=0;i<this->n;i++){
for(int j=0;j<this->n;j++){
if(i==j){
if(sig(this->val[i][j]-1)!=0){
return false;
}
}else {
if(sig(this->val[i][j]-0)!=0){
return false;
}
}
}
}
return true;
/********* End *********/
}
};
int main(int argc, const char * argv[]) {
int n;
scanf("%d", &n);
Matrix A(n,n);
A.in();
Matrix I(n,n);
I = A.Inverse_Matrix();
I.out();
Matrix E1(n,n);
Matrix E2(n,n);
E1 = A*I;
E2 = I*A;
if(E1.Identity_Matrix()==false){
printf("A*I error\n");
E1.out();
}
if(E2.Identity_Matrix()==false){
printf("I*A error\n");
E2.out();
}
return 0;
}
第3关:矩阵初等变换
任务描述
本关任务:掌握矩阵的初等变换的基本操作,并通过矩阵初等变换计算n阶矩阵的逆矩阵。
相关知识
为了完成本关任务,你需要掌握:1.初等变换的定义,2.初等行变换求逆矩阵,3.初等列变换求逆矩阵。
初等变换的定义
对一个矩阵施行下面的变换称之为矩阵的初等变换:
对换矩阵的某两行或两列;
任一非零常数乘以矩阵的某行或某列;
常数乘以矩阵的某行(列)元素加到另一行(列)的对应元素上。
根据施行在行或是列上,初等变换可分为初等行变换和初等列变换。
初等行变换求逆矩阵
通过初等行变换可以将形如的矩阵转换为形如的矩阵。具体步骤如下:
初始i=1,将第i行的所有元素a[i,j],j∈[i,n]除以a[i,i]。
枚举第i+1行至第m行,将第k∈[i+1,m]行的元素进行初等变换:
flag=a[k,i]
a[k,j]=a[k,j]−flag×a[i,j],j∈[i,n]
若i<=n,则i=i+1,跳回第1−2步。否则将得到如下形式的矩阵(左半部分左下元素全为0):
初始i=m,将第i行的所有元素a[i,j],j∈[i,n]除以a[i,i]。
枚举第1行至第i−1行,将第k∈[1,i−1]行的元素进行初等变换:
flag=a[k,i]
a[k,j]=a[k,j]−flag×a[i,j],j∈[k,n]
若i>0,则i=i−1,跳回第4−5步。否则将得到如下形式的矩阵(左半部分右上元素全为0):
最终,左半部分的矩阵即为单位矩阵,右半部分的矩阵即为A
−1
。
初等列变换求逆矩阵
通过初等列变换可以将形如的矩阵转换为形如的矩阵。具体步骤同初等行变换类似。
编程要求
本关的编程任务是补全右侧代码片段Elementary_Row_Transformation和Elementary_Col_Transformation中Begin至End中间的代码,具体要求如下:
在Elementary_Row_Transformation中,矩阵R的左半部分是原矩阵,右半部分是单位矩阵,借助矩阵初等行变换,将矩阵R的左半部分变换为单位矩阵,右半部分变换为原矩阵的逆矩阵。
在Elementary_Col_Transformation中,矩阵C的上半部分是原矩阵,下半部分是单位矩阵,借助矩阵初等列变换,将矩阵C的上半部分变换为单位矩阵,下半部分变换为原矩阵的逆矩阵。
测试说明
平台将自动编译补全后的代码,并生成若干组测试数据,接着根据程序的输出判断程序是否正确。
以下是平台的测试样例:
测试输入:
2
1 2
3 4
预期输出:
1.0000 0.0000 -2.0000 1.0000
0.0000 1.0000 1.5000 -0.5000
1.0000 0.0000
0.0000 1.0000
-2.0000 1.0000
1.5000 -0.5000
开始你的任务吧,祝你成功!
//
// main.cpp
// step3
//
// Created by ljpc on 2018/11/29.
// Copyright © 2018年 ljpc. All rights reserved.
//
#include <iostream>
#include <algorithm>
using namespace std;
#define eps 1e-8
inline int sig(double x){return (x>eps)-(x<-eps); }
struct Matrix{
int m, n;
double **val;
Matrix(){}
Matrix(int m_, int n_){
m = m_;
n = n_;
this->val = (double**)malloc(sizeof(double*)*m);
for(int i=0;i<m;i++){
this->val[i] = (double*)malloc(sizeof(double)*n);
}
}
void in(){
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
scanf("%lf", &this->val[i][j]);
}
}
}
void out(){
for(int i=0;i<m;i++){
printf("%.4lf", this->val[i][0]+eps);
for(int j=1;j<n;j++){
printf(" %.4lf", this->val[i][j]+eps);
}
printf("\n");
}
}
Matrix Elementary_Row_Transformation (){
Matrix R = Matrix(this->n, this->n*2);
for(int i=0;i<this->n;i++){
for(int j=0;j<this->n;j++)
R.val[i][j] = this->val[i][j];
for(int j=this->n;j<this->n*2;j++)
R.val[i][j] = ((j-this->n)==i)?1:0;
}
// 请在这里补充代码,完成本关任务
/********* Begin *********/
for(int i=0;i<R.m;i++){
double flag = R.val[i][i];
for(int j=i;j<R.n;j++){
R.val[i][j] /= flag;
}
for(int k=i+1;k<R.m;k++){
flag = R.val[k][i];
for(int j=i;j<R.n;j++){
R.val[k][j] -= flag * R.val[i][j];
}
}
}
for(int i=R.m-1;i>=0;i--){
double flag = R.val[i][i];
for(int j=i;j<R.n;j++){
R.val[i][j] /= flag;
}
for(int k=i-1;k>=0;k--){
flag = R.val[k][i];
for(int j=k;j<R.n;j++){
R.val[k][j] -= flag * R.val[i][j];
}
}
}
/********* End *********/
return R;
}
Matrix Elementary_Col_Transformation (){
Matrix C = Matrix(this->n*2, this->n);
for(int j=0;j<this->n;j++){
for(int i=0;i<this->n;i++)
C.val[i][j] = this->val[i][j];
for(int i=this->n;i<this->n*2;i++)
C.val[i][j] = ((i-this->n)==j)?1:0;
}
// 请在这里补充代码,完成本关任务
/********* Begin *********/
for(int j=0;j<C.n;j++){
double flag = C.val[j][j];
for(int i=j;i<C.m;i++){
C.val[i][j] /= flag;
}
for(int k=j+1;k<C.n;k++){
flag = C.val[j][k];
for(int i=j;i<C.m;i++){
C.val[i][k] -= flag * C.val[i][j];
}
}
}
for(int j=C.n-1;j>=0;j--){
double flag = C.val[j][j];
for(int i=j;i<C.m;i++){
C.val[i][j] /= flag;
}
for(int k=j-1;k>=0;k--){
double flag = C.val[j][k];
for(int i=j;i<C.m;i++){
C.val[i][k] -= flag * C.val[i][j];
}
}
}
/********* End *********/
return C;
}
};
int main(int argc, const char * argv[]) {
int n;
scanf("%d", &n);
Matrix A(n,n);
A.in();
Matrix R = Matrix(n, n*2);
R = A.Elementary_Row_Transformation();
R.out();
printf("\n");
Matrix C = Matrix(n*2, n);
C = A.Elementary_Col_Transformation();
C.out();
printf("\n");
return 0;
}