来自知乎问题,觉得挺有意思,留给学生解答之余,我也做了一番思考,得到三种解法。
题目如下:
以n=80为例,
————————————————————
一、先要根据
确定矩阵的阶数
如果先生成足够大矩阵,再删掉全为零的列,有点太low了。所以先思考一个算法:
观察数据放置的特点,不难发现这样一个关系:
变形:
观察,若
足够大,
起决定作用,
取整一定是
较小的
(或
) 对吗?试一试。
测试
至
:
n=1:56;
round(sqrt(2*n))
刚好是对的!
二、将
按斜线依次放置
无非是找到放置规律放数而已,放置就是用行标列标赋值,所以就是找行标、列标规律。
先写个数的方便观察规律,比如,
方法一. 行标、列标分别找规律
依次赋值的
行标:
共
组
个数
列标:
共
组
个数
(因为是
条斜线)
既然如此,按上述规律生成(拼接)行标、列标,再赋值即可。
编写函数:
functionA=DiagNum1(n)k=round(sqrt(2*n)); %确定矩阵阶数k
A=zeros(k);
%% 生成行标, 列标
I=[];
J=[];
for i=1:k
I=[I,1:i];
J=[J,i:-1:1];
end
IND=sub2ind(size(A),I,J); %二维索引转化为一维索引
A(IND(1:n))=1:n;
注意,生成行标列标索引后,[I, J]作为二维索引值,是不能直接用A([I,J])=1:n来赋值的,故做了二维索引到一维索引的转化。
测试函数:
DiagNum1(80)
方法二. 行标列标一起找规律
依次赋值的
行标和列标:
先是“和为
”的,再“和为
”的,再“和为
”的,……; 每个子组里面又是行标从小到大排列。
因此,先生成所有行标列标组合(
与
的笛卡尔积),第一关键字按"行标列标之和"排序,再第二关键字按行标排序,排好序的取出前
个,依次赋值
即可。
编写函数:
functionA=DiagNum2(n)k=round(sqrt(2*n)); %确定矩阵阶数k
A=zeros(k);
%% 生成所有下标组合(笛卡尔积)
[X,Y]=meshgrid(1:n,1:n);
subn=[X(:),Y(:)];
%% 选取索引并赋值
ind=sortrows([subn,sum(subn,2)],3); %对下标求和, 按该和排序, 已经满足要求
IND=sub2ind(size(A), ind(1:n,1),ind(1:n,2)); %选出前n个二维索引, 并转化为一维
A(IND)=1:n;
测试函数(略)
方法三. 用循环语句实现
用循环语句控制行标、列标按该规律出现,依次赋值:
编写函数:
functionA=DiagNum3(n)k=round(sqrt(2*n)); %确定矩阵阶数k
A=zeros(k);
val=1;
for m=1:k
for i=1:m
A(i,m+1-i)=val;
val=val+1;
if val>n
break;
end
end
end
注意,外层循环用
控制依次从第
列到第
列起始的斜线;对每条斜线,先循环
,从
到
;而始终有
.
测试函数(略)
三、稍微提升一下
只赋值
, 实用性不大,如果改进一下,对给定的一个向量
, 将
中的值依次按斜线方向赋值成矩阵呢?
非常简单,参数由
换成向量
, 则
的长度即为需要的
,再将最后赋值时用的
换成
即可。
改进的第一个函数:
functionA=DiagNum1(V)n=length(V);
k=round(sqrt(2*n)); %确定矩阵阶数k
A=zeros(k);
%% 生成行标, 列标
I=[];
J=[];
for i=1:k
I=[I,1:i];
J=[J,i:-1:1];
end
IND=sub2ind(size(A),I,J); %二维索引转化为一维索引
A(IND(1:n))=V;
改进的第二个函数:
functionA=DiagNum2(V)n=length(V);
k=round(sqrt(2*n)); %确定矩阵阶数k
A=zeros(k);
%% 生成所有下标组合(笛卡尔积)
[X,Y]=meshgrid(1:n,1:n);
subn=[X(:),Y(:)];
%% 选取索引并赋值
ind=sortrows([subn,sum(subn,2)],3); %对下标求和, 按该和排序
IND=sub2ind(size(A), ind(1:n,1),ind(1:n,2)); %选出前n个二维索引, 并转化为一维
A(IND)=V;
改进的第三个函数(稍有不同):
functionA=DiagNum3(V)n=length(V);
k=round(sqrt(2*n)); %确定矩阵阶数k
A=zeros(k);
val=1;
for m=1:k
for i=1:m
A(i,m+1-i)=V(val);
val=val+1;
if val>n
break;
end
end
end
若还要得到原问题的结果,只需这样调用函数即可:
DiagNum1(1:n)
DiagNum2(1:n)
DiagNum3(1:n)
原创文章,转载请注明。