这个问题来自知乎,我觉得挺有趣的。 在留给学生回答的同时,我也思考了一下,想出了三个解决办法。
主题如下:
以n=80为例,
———————————————————————
1、首先根据n确定矩阵的阶k。
如果你首先生成一个足够大的矩阵,然后删除所有零列,那就有点太低了。 那么首先想一个算法:
观察数据放置的特点,不难发现这样的关系:
\frac{k(k-1)}{2} < n \leq \frac{k(k+1)}{2}
形变:
k^2-k < 2n \leq k^2+k
观察到,如果 k 足够大,k^2 起着决定性作用,\sqrt{2n} 必须四舍五入为 k
k(或n)更小,对吗? 试一试。
测试 n=1 到 56:
n=1:56;
round(sqrt(2*n))
正好!
2. 按斜杠顺序放置 1 \sim n
无非是找到放置模式和放置数字。 放置意味着使用行和列标签来分配值,因此它是找到行和列标签模式。
首先写下一个数的方便观察模式,例如n=20
方法1.分别在行和列标签中查找模式
1 \sim n 按顺序赋值
行标签:1; 〜1,2; 〜1,2,3; ~1,2,3,4; ~1,\cdots 总共k组n个数字
列标签:1; 〜2,1;〜3,2,1; ~4,3,2,1; ~5, \cdots 总共 k 组 n 个数字
(因为有k个斜杠)
这种情况下,只需按照上述规则生成(拼接)行标签和列标签,然后赋值即可。
写入函数:
function A=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)
方法 2. 通过行和列标签一起查找模式
1 \sim n 按顺序赋值
行和列标签:(1,1); 〜(1,2),(2,1); 〜(1,3),(2,2),(3,1); 〜(1,4),(2,3),\cdots
首先是“sum is 2”,然后是“sum is 3”,然后是“sum is 4”,...; 在每个子组中,行标签从小到大排列。
因此,首先生成所有行和列标签组合(1\sim n 和 1 \sim n 的笛卡尔积)。 第一个关键字按“行和列标签之和”排序,然后第二个关键字按行标签排序。 ,按顺序取出前n个,依次赋值1\sim n。
写入函数:
function A=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;
测试功能(略)
方法三、使用循环语句实现
使用循环语句控制行列标签按照这个规则出现,并按顺序赋值:
(1,1) \quad k=1 \quad i=1 \quad j=1 \\ (1,2) \quad k=2 \quad i=1 \quad j=2 \\ (2,1) \四边形 k=2 \quad i=2 \quad j=1 \\ (1,3) \quad k=3 \quad i=1 \quad j=3 \\ (2,2) \quad k=3 \quad i=2 \quad j=2 \\ (3,1) \quad k=3 \quad i=3 \quad j=1 \\ \cdots \cdots
写入函数:
function A=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
注意,外层循环使用m来控制从第1列开始到第k列的对角线; 对于每条对角线,i首先从1循环到m; 并且总是存在 i+j=m+1。
测试功能(略)
3.稍微改进一下
仅分配 1 \sim n 不太实用。 如果我们改进它,将给定向量 V 的 V 中的值分配给对角线方向的矩阵会怎么样?
这很简单。 将参数从n改为向量V,那么V的长度就是需要的n,然后将最终赋值时使用的1:n换成V。
改进的第一个功能:
function A=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;
改进的第二个功能:
function A=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;
改进的第三个函数(略有不同):
function A=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)