ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 效率神器,一键搞定繁琐工作
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
让更多数据处理,一键完成 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
查看: 1203|回复: 10

[分享] 螺旋式排列数组(py+循环+递归)

[复制链接]

TA的精华主题

TA的得分主题

发表于 2023-11-2 09:38 | 显示全部楼层 |阅读模式
本帖最后由 shaowu459 于 2023-11-2 10:56 编辑

螺旋式排列数组是编程中的一个中等难度问题,例如根据下图需要从源数组左上角第一个元素开始按顺时针从外到内输出数据,结果如右侧数组:


图片.png

螺旋式排列数组顺序也可以是按逆时针方向,也就是先列后行的方式。该题的解法很多,本帖以顺时针排列为例说明通过旋转数组方式输出结果的公式写法,为方便说明,返回结果是单列,未再折叠成多行多列的数组。

注意:源数组不一定是n*n的数组,可能是n*m。

螺旋数组转换-超人(顺时针、逆时针)发帖.rar

24.3 KB, 下载次数: 27

评分

6

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2023-11-2 09:42 | 显示全部楼层
由于要用到旋转数组,所以先看一下如何使用公式实现数组旋转。

如果需要将B2:E4单元格数组逆时针旋转90°,可以先将其转置:
  1. =TRANSPOSE(B2:E4)
复制代码

图片.png

对转置后的数组,再在行方向上倒序排列,最底下一行挪到最上面,倒数第二行挪到第二行,以此类推:
  1. =SORTBY(G2#,-SEQUENCE(COLUMNS(B2:E4)))
复制代码

图片.png

TA的精华主题

TA的得分主题

 楼主| 发表于 2023-11-2 10:06 | 显示全部楼层
简要说明一下本帖的解题思路:

1)提取当前数组的第一行,转化成列,堆积在结果数组下方;
2)将当前数组去掉第一行,生成剩余数组,剩余数组的最右侧一列就是下次需要提取的数据;
3)将剩余数组逆时针旋转90°,则下次需要提取的数据就变成了数组的第一行;
4)进入下次循环后,仍然提取当前数组的第一行转化成列,堆积在结果数组下方;再次生成剩余数组,逆时针将剩余数组旋转90°;
5)循环到最后只剩一行的时候,这一行直接输出,堆积在结果数组下方。

TA的精华主题

TA的得分主题

 楼主| 发表于 2023-11-2 10:13 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
下面通过单元格辅助区域对上楼的解题思路进行说明:

初始数组如下:

图片.png

提取初始数组首行放入结果列:

图片.png

去掉初始数组的第一行,得到剩余数组:

图片.png

将剩余数组逆时针旋转90°,将下次要提取的内容放到数组第一行:
图片.png

下次循环,提取第一行,转成列后堆积在结果数组下方:

图片.png

去掉当前数组第一行,再次生成剩余数组:

图片.png

再次将剩余数组逆时针旋转90°:

图片.png

之后的循环以此类推。

TA的精华主题

TA的得分主题

 楼主| 发表于 2023-11-2 10:18 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
在上面解题思路分析的基础上,下来看py代码,非常简洁,代码中有注释,不再过多解释:

  1. import numpy as np
  2. a = xl("A1:D5").values   #获取数据源
  3. r = []     #结果数组,初始为空
  4. while a.size > 0:     #当数组大于小于0时,也就是非空时执行下面的代码
  5.     r.extend(a[0])  # 获取第一行,放入结果数组
  6.     a = np.rot90(a[1:])  # 逆时针旋转数组并去掉第一行
  7. r    #输出结果数组
复制代码
图片.png



TA的精华主题

TA的得分主题

 楼主| 发表于 2023-11-2 10:30 | 显示全部楼层
递归公式写法:

图片.png

公式简要说明如下:
=LAMBDA(数组,      传入当前需要处理的数组
    IF(
        ROWS(数组) = 1,     当数组只有一行的时候,就可以直接输出
        TOCOL(数组),        直接将行转化为列输出
        LET(
            首行, TAKE(数组, 1),     提取当前数组的第一行
            剩余数组, DROP(数组, 1), 去掉当前数组的第一行,生成剩余数组
            旋转数组, SORTBY(        使用SORTBY函数将剩余数组逆时针旋转90°
                TRANSPOSE(剩余数组),
                -SEQUENCE(COLUMNS(剩余数组))
            ),
            VSTACK(TOCOL(首行), fx(旋转数组))   堆积当前数组第一行,再次对剩余数组执行同样的操作。
        )
    )
)

TA的精华主题

TA的得分主题

发表于 2023-11-2 10:40 | 显示全部楼层

TA的精华主题

TA的得分主题

 楼主| 发表于 2023-11-2 10:45 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
本帖最后由 shaowu459 于 2023-11-2 10:48 编辑

使用REDUCE函数循环,我的公式稍微复杂一些。循环不像递归,可以将剩余数组直接传递下去,我的思路是将旋转后的剩余数组堆积在x顶端,下次循环的时候提取出来使用。使用完毕后生成的旋转后的剩余数组,再次堆积到x顶端。这样就需要有一个标识,从x顶端提取多少行。我的方法是在源数组下面第一列下面堆积一个0,每次以0的位置判断并提取数组。

例如,初始状态如下:

图片.png

第一次循环后,源数组第一行堆积在0下面,旋转后的剩余数组再堆积在0上面:

图片.png

再次循环的时候,只需要判断x第一列0的位置,然后将上面的数组提取出来,然后将提取的数组第一行转成列堆积在x中第一列0下面的数组下方。再将逆时针旋转后的剩余数组堆积在0的上方,即可进入下次循环。


当数组的所有数据都已经堆积在x第一列下方后,0上方已经没有需要待处理的数组了,因此再提取和运算会出错,这时可以简单的将出错的结果屏蔽为0,循环的时候只要判断x顶端数据为0就可以不再执行进一步运算,保持x这个最终结果即可。因为初始x下方堆积了一个0,屏蔽错误值也会堆积一个0,因此,最终x上方会有2个多余的0,使用DROP函数去掉即可。

TA的精华主题

TA的得分主题

 楼主| 发表于 2023-11-2 10:54 | 显示全部楼层
上楼中REDUCE函数循环思路参考公式如下:

图片.png

公式简要说明如下:

=DROP(      去掉多余的两行0
    REDUCE(
        VSTACK(A1:D5, 0),   源数组下方堆积一个0,作为标识
        ROW(1:99),          循环足够多次数,超过次数保留x不变
        LAMBDA(x, y,
            IF(
                @x = 0,     x顶端是0时保持x不变,不执行后面运算
                x,
                LET(
                    x顶端数组高度, MATCH(0, TAKE(x, , 1), ) - 1,   从上到下匹配0的位置
                    x顶端数组, TAKE(x, x顶端数组高度),              提取出x顶端待处理数组
                    结果列, DROP(TAKE(x, , 1), x顶端数组高度),      提取出x第一列中0下面的数组,也就是结果堆积的数组
                    剩余数组, DROP(x顶端数组, 1),                   去掉待处理数组的第一行,生成剩余数组
                    VSTACK(
                        IFERROR(
                            SORTBY(TRANSPOSE(剩余数组), -SEQUENCE(COLUMNS(剩余数组))),    将剩余数组逆时针旋转90°堆积在结果列上方
                            0          如果没有需要处理的数组了,返回0
                        ),
                        结果列,     堆积上一步的结果列
                        TOCOL(TAKE(x顶端数组, 1))    将当前数组的第一行转成列,堆积在上一步的结果列下方
                    )
                )
            )
        )
    ),
    2
)



TA的精华主题

TA的得分主题

 楼主| 发表于 2023-11-2 10:56 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
上面是以顺时针方向螺旋输出数组为例介绍的公式写法,也可以逆时针从第一列开始输出,参考公式在附件中,公式都大体相同,只不过旋转方向变化,提取第一行变成提取第一列。
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-12-4 01:34 , Processed in 0.055165 second(s), 8 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表