女朋友突然找我帮她做线代的期末大作业,说是要用 matlab 写篇论文。我倒也一下懵了,虽说我的专业和打代码能沾点边,但我目前也只不熟练地了解 C++一门语言(各位大佬原谅我这个小白大一新生吧),完全没了解过 matlab,不过听说 matlab 对于工科学生还挺实用的,于是趁着这个机会也浅学一下吧。限于 ddl,我的 matlab 学习就从针对这道题入手。
题目
用 C++来实现
拿到这题倒还感觉挺简单的,至少对于 C++来说是这样。我想着就先用 C++来实现先吧,正好可以理一下思路。(有点南辕北辙,各位看官可跳过)
#include<iostream>
using namespace std;
int main()
{
int n, **a, tmp, *b, *c, max, max_j;
cout<<"请输入有几支参赛队伍";
cin>>n;
//申请动态空间并赋初值
a = new int*[n];
for (int i=0; i<n; i++)
a[i] = new int [n];
for (int i=0; i<n; i++)
for (int j=0; j<n; j++)
a[i][j]=0;
//获取数据
for (int i=0; i<n; i++)
{
cout<<"请输入第"<<i+1<<"支队伍战胜的队伍"<<endl;
cout<<"若战胜多支队伍,请用回车键隔开;若输入已完成,请输入0,然后输入回车;若一支队伍都没战胜,请输入0"<<endl;
for (int j=0; j<n; j++)
{
cin>>tmp;
if (tmp!=0)
{
a[i][j]=tmp;
}
else
break;
}
}
/**
for (int i=0; i<n; i++)
{
for (int j=0; j<n; j++)
{
cout<<a[i][j]<<' ';
}
cout<<endl;
}
**/
//获取直接胜数据
b = new int [n];
for (int i=0; i<n; i++)
b[i]=0;
for (int i=0; i<n; i++)
{
for (int j=0; j<n; j++)
{
if (a[i][j]!=0)
b[i]++;
else
break;
}
}
//获取间接胜数据
c = new int [n];
for (int i=0; i<n; i++)
c[i]=0;
for (int i=0; i<n; i++)
{
for (int j=0; j<n; j++)
if (a[i][j]!=0)
c[i]+=b[a[i][j]-1];//注意,数组中的行数和输入的行数不是一个东西
}
/**
for (int i=0; i<n; i++)
cout<<b[i]<<' ';
cout<<endl;
for (int i=0; i<n; i++)
cout <<c[i]<<endl;
**/
//排序并输出
for (int i=0; i<n; i++)
{
max = -1;
max_j = -1;
for (int j=0; j<n; j++)
{
if ((b[j]>max)||(b[j]==max&&c[j]>c[max_j]))
{
max = b[j];
max_j = j;
}
}
cout<<"排名第"<<i+1<<"名的队伍是"<<max_j+1<<endl;
b[max_j]=-1;
}
//删除动态申请的空间
for (int i=0; i<n; i++)
delete a[i];
delete b;
delete c;
return 0;
}
这段代码随手写的,大佬们随便笑话就可以了/捂脸。
不过写完 C++后怎么样呢?matlab 可以直接运行 cpp 程序呢?显然不太行。根据搜索引擎,可以通过接口让 matlab 运行 C++程序,但考虑到这就不是 matlab 编程了,也不知道她的老师认不认。至于有没有什么方法可以把 C++直接转换为 matlab 代码,好像也没有(不过把 matlab 代码转换为 C++代码反而是 matlab 软件自带的。
没办法,只能从头一点一点学 matlab,南辕北辙搞了个 C++程序倒也是没办法。强烈安利官方课程,就入门而言比看书看视频强太多了
输入命令
基本命令倒也不难。值得注意的是:
1.它可以实时交互
,给个命令就返回结果
2.每行代码结尾也不用加分号
(事实上分号另有作用,这在以后会介绍)
3.右边的工作区会显示各个变量的信息
4.不用定义变量,需要用某个新变量时直接用就可以了
更详细一点的说明如下:
简单命令
想算 3*5 就直接输入3*5
就可以了,系统会自动创建一个ans
变量来存储结果15
想把 3*5 这个运算结果给变量 m,不用定义变量可以直接写m = 3*5
至于变量的运算和变量之间的赋值就很简单了,大家看着示例便可以意会了
分号的作用
前面提到 matlab 中的命令不用像 C++中那样每行都要加分号,而且 matlab 会实时对你的命令做出反应,那 matlab 中的行末分号有什么用呢?如何让 matlab 不要实时对你的命令做出反应呢?答案已经暗示的很明显了,那就是在行末加分号可以让 matlab 不要实时对你的命令做出反应,不过在工作区中还是可以看到变量发生了变化。
重新调用以前的命令
这是我觉得最神奇的一点,大家现在按🔼
(向上键
)看看会发现什么。
竟然在这一行出现了之前行的代码!再继续摁向上键
,继续出现了更之前的代码!
matlab 突然有意思了起来。
获取变量的值
直接输入变量名
再按下enter
键就可以了!(好方便)
命名变量
这些都跟 C++中差不多,就不赘述了
保存和加载变量
save
可以把工作区中的变量保存到 MAT 文件的 MATLAB 特定格式文件中,比如要把工作区保存到 datafile.mat 文件中,可以直接输入save datafile
load
可以从 MAT 文件(就是 save 保存的文件)中加载变量,直接输入load datafile
如果只想要保存或加载部分变量,可以写成load datafile m
或save datafile m
clear
可以将工作区清空,直接输入clear
clc
可以清空命令行窗口
想要查看工作区中变量的内容?直接输入变量名就可以了
使用内置函数和常量
pi
在 matlab 就代表π
,尽管只显示了四位小数但其实他的精度很高
i
作为虚数也是 MATLAB 内置的类型之一
使用内置函数时,要用()
来括住函数的输入,例如sin(-5)
abs()
计算绝对值
eig()
计算特征值
sqrt()
计算平方根
format long
命令可以增加显示精度
format short
命令可以减小显示精度
MATLAB 桌面和编辑器
当前文件窗口,命令行窗口,工作区一看就知就不细说了
实时脚本
每次输入命令都有反馈固然方便,但显然如果要编写一个较复杂的程序这样就不太可行了,这时我们可以用到实时脚本
在实时脚本中,在灰色的代码框
中可以输入代码,在文本模式
中可以添加说明
按下运行
键就可以在右侧的区域出现输出结果,把点击某行代码或输上会发现他可以帮我们找到对应的输出或代码
通过分节符
还可以将代码分节,来分节运行,剩下的就都可以轻松理解,在这就不赘述了
向量和矩阵
手动输入数组
在 MATLAB 中就算是一个数它也是当作一个 1*1 的数组
,不过这个信息好像也没什么用(?
一个数的话单输入就可以了,那么如何创建一个含多个元素的数组呢?可以用方括号来实现:x = [3 5]
创建时需要注意的是:用
或,
分隔数值时,还是会把这些数放在同一行,用;
时则会换行
创建等间距向量
如果要输入的向量很长,一个一个输入就不太实际。
:
运算符可以仅指定起始值和最终值就得到向量,如y = 5:8
:
的使用还可以更加灵活,可以指定间距,如x = 20:2:26
有时候不知道间距,但是知道元素的数目,可以使用linspace
函数
linspace 函数中,第一个参数是起始值
,第二个参数是最终值
,第三个参数是元素个数
'
可以实现转置
(学了线代就一定要用起来 bushi),把行向量转化为列向量
'
还可以直接在创建行向量时把它转为列向量,但注意此时要加圆括号
数组创建函数
rand(n)
随机获取一个 n 阶方阵
rand(n*m)
随机获取一个 n 行 m 列的矩阵
zeros(n*m)
创建一个全零矩阵
size(x)
获取现有矩阵的大小,甚至可以用rand(size(x))
来创建一个与现有矩阵一样大小的随机矩阵
引索和修改数组
对数组进行索引
首先注意其对数组的标号,是从1
开始的!和 C++中从 0 开始完全不一样!
如果是要索引向量中的元素,只用输入x = a(2)
(索引 a 向量的第 2 个元素)
如果是要索引矩阵中的元素,只用输入x = A(5, 7)
(索引 A 矩阵中第 5 行第 7 列元素)
如果要索引某行或列的最后一个元素,只用输入x = A(end, 3)
(索引 A 矩阵中第三列的最后一个元素,甚至可以用end-1
来表示倒数第二个元素(倒数三四五以此类推
提取多个元素
如果要索引矩阵的某行,只用输入x = A(1, :)
(索引A 矩阵中第 1 行全行)
如果要索引矩阵的某列,只用输入x = A(:, 3)
(索引A 矩阵中第 3 列全列)
还记得1:3
是什么意思吗?这个表达式甚至可以和这块知识点结合起来,例如x = A(1:3, :)
(索引 A 矩阵第一到三行的所有元素)
在向量中也可以这样用!x = a(1:3)
(索引 a 数组中第一到三个元素)
更改数组中的值
这倒和 C++没有什么区别
数组计算
让数组中所有元素都+1:y = x+1
(把 x 数组中所有元素+1 再赋值给 y 对应位置的元素)(当然 1 页可以是任何数)
类似的,也可以让两个相同大小
的数组相加,甚至可以相乘或相除
max()
函数可以获取某向量中的最大值
可以直接把整个数组放入函数的参数位置,这样的话相当于对数组中的每个元素都进行函数操作y = round(x)
(对 x 向量中的每个元素都进行四舍五入操作)
*
进行矩阵的乘法
.*
进行元素中一一对应的乘法
调用函数
size()
函数可以获取矩阵的大小,这在前文也说明过
神奇的是,可以通过方括号
来获取多个输出,也就是,可以把获取到的结果分别存入变量[xrow, xcol] = size(x)
max()
可以确定向量的最大值及其对应的索引,例如[xMax, idx] = max(x)
有时候只想要函数多个输出值中的一个,就可以使用~
来忽略特定输出,例如[~,ivMax] = max(v2)
官方课程中还有获取帮助
,导入数据
和绘制数据图
的课程,鉴于这对我们解这道题没什么用,我们暂且不学,先学习后面的内容
逻辑数组
关系运算符其他的都一样,就是不等于从!=
变成了~=
,同时不要忘了它的返回值是0
和1
。其他的倒也没有什么其他特别要注意的了。
也可以把数组和一个数进行比较,惊喜不惊喜!这样得到的结果是一个由 0 和 1 组成的数组,里面的 0 和 1 代表着原数组中对应位置的数和给定数比较的结果
还有更秀的操作,甚至可以把逻辑数组
作为数组索引!这样的话 MATLAB 会提取所有索引为 true 的数组元素
甚至可以对两个不同的向量使用逻辑索引,例如我要获取 b 向量小于 4 的元素在 a 向量中对应位置的元素可以这样写x = a(b<4)
利用逻辑索引还可以对数组进行重新赋值,比如x(x==999)=1
把 x 中等于 999 的所有值都替换为 1
&
和|
是逻辑运算符且
和或
,对应于 C++中的&&
和||
决策分支
照例是if
else
elseif
等语句,但要注意的是条件不用用圆括号括起来,也不需要用大括号把代码块括起来,而是用end
来表示分支结束
for 循环
同样,条件不用用圆括号括起来,也不需要用大括号把代码块括起来,而是用end
来表示分支结束
另外条件可以写成这样x = 1:3
意思是循环三次(即从 1,2 到 3),也可以用x = 1:length(density)
来对未知长度的向量执行循环
开始解这道题
既然现在已经把程序的逻辑用 C++写出来了,也了解了 Matlab 的基本知识,就让我们开始正式解这道题吧
我们先输入5个球队的胜负情况。那么如何输入呢?我们可以设置一个5*5的矩阵,行的编号代表队的编号,每一行里面的数代表该队打赢的队伍。那么根据题目,输入的矩阵如下:
A = [2, 3, 0, 0, 0;
3, 4, 5, 0, 0;
0, 0, 0, 0, 0;
1, 3, 5, 0, 0;
1, 3, 0, 0, 0];
然后我们计算每个队直接胜的次数,对应到矩阵A中就是把每行有多少个非零元存入矩阵B中,那我们需要运用嵌套循环,一层来一行行遍历,一层来在每行中一列列遍历:
B = zeros(5*1);
for i=1:5
for j=1:5
if (A(i, j)~=0)
B(i) = B(i)+1;
end
end
end
再然后我们需要计算每个队的间接胜的次数,对应到矩阵A中就是我们要把A中第i行中的元素对应的直接胜加和到一起,同样也是通过嵌套循环来实现
C = zeros(5*1);
for i=1:5
for j=1:5
if (A(i, j)~=0)
C(i) = C(i)+B(A(i, j));
end
end
end
我们现在已经得到了每队直接胜和间接胜的数据,我们需要把他们排序,那我们再用一个矩阵D来存储。由于排序有两个约束条件,我们可采用简单的循环嵌套来逐次找到当前的第一名并把它更改
D = zeros(5*1);
for i=1:5
max = -2;
max_j = -1;
for j=1:5
if B(j)>max||(B(j)==max&&C(j)>C(max_j))
max = B(j);
max_j = j;
end
end
fprintf('第%d名是第%d队', i, max_j)
B(max_j) = -1;
end
这样子我们就获得排名顺序了!
至于如何要给 n 支球队排序呢?很简单,只要把 n 都替换成 5 就可以了
A = [2, 3, 0, 0, 0;
3, 4, 5, 0, 0;
0, 0, 0, 0, 0;
1, 3, 5, 0, 0;
1, 3, 0, 0, 0];
n = 5;
B = zeros(n*1);
for i=1:n
for j=1:n
if (A(i, j)~=0)
B(i) = B(i)+1;
end
end
end
C = zeros(n*1);
for i=1:n
for j=1:n
if (A(i, j)~=0)
C(i) = C(i)+B(A(i, j));
end
end
end
D = zeros(n*1);
for i=1:n
max = -2;
max_j = -1;
for j=1:n
if B(j)>max||(B(j)==max&&C(j)>C(max_j))
max = B(j);
max_j = j;
end
end
fprintf('第%d名是第%d队\n', i, max_j)
B(max_j) = -1;
end
如此一来我们只用输入队伍的支数 n 和记录其胜的矩阵我们就可以得到他们的排名了