'''
动态循环即不定层数循环的两种Python3 实现(递归、纯循环)
# 注意:Python 默认只有list 类型,把它相当于C 的数组来理解即可。
# 要从下面3 个数组分别取3 个元素组成一个,并把可能的组合打印出来
data = [
[1, 2],
[3, 4, 5],
[6, 7, 8, 9]
]
# 意味着会有2*3*4=24 种可能即所有list 的迪卡乘积,解决的办法可以用三重循环实现例如:
for i in data[0]:
for j in data[1]:
for k in data[2]:
print([i, j, k])
但是如果data 里的数组个数不确定的话如果实现动态的不定层循环呢?
我将data 看成一个二维数组:
第一维是data 包含数组的个数即Y 轴,由0 开始自上而下逐行递增;
第二维是每个数组包含的元素个数即X 轴,由0 开始从左到右逐列递增;
然后分别用递归和纯循环的方式处理X、Y 这两个轴。
'''
'''
递归法
从Y 轴第一层即index=0 开始从上往下递归,到了最底层就用一个循环每次将最底层的一个元素加入到要返回的list 中。
注意:Python 对递归层数是有限制的,而且不同操作系统的限制数不一样,在100 层以上就要考虑换个算法了。
'''
# data 数据源, cur_y_idx 当前Y 轴值, lst_rst 返回结果list, lst_tmp 用于临时拼装lst_rst 的元素
def dynloop_rcsn(data, cur_y_idx = 0, lst_rst = [], lst_tmp = []):
max_y_idx = len(data) - 1 # 获取Y 轴最大索引值
for x_idx in range(len(data[cur_y_idx])): # 遍历当前层的X 轴
lst_tmp.append(data[cur_y_idx][x_idx]) # 将当前层X 轴的元素追加到lst_tmp 中
if cur_y_idx == max_y_idx: # 如果当前层是最底层则将lst_tmp 作为元素追加到lst_rst 中
lst_rst.append([*lst_tmp])
else: # 如果当前还不是最底层则Y 轴+1 继续往下递归,所以递归最大层数就是Y 轴的最大值
# lst_rst 和lst_tmp 的地址也传到下次递归中,这样不论在哪一层中修改的都是同一个list 对象
dynloop_rcsn(data, cur_y_idx+1, lst_rst, lst_tmp)
lst_tmp.pop() # 在本次循环最后,不管是递归回来的,还是最底层循环的,都要将lst_tmp 最后一个元素移除
return lst_rst
'''
循环法
将多层循环'拍平'成一层循环,即Y 轴只有0,只遍历X 轴,2 维数组变1 维数组。
难点在于每次循环都要计算每个被提取的元素的索引,以提取原2 维数组中的元素。
'''
def dynloop_loop(data):
# 变量初始化
max_y_idx = len(data) # 获取原2 维数组Y 轴最大值
row_max_idx = 1 # 记录X 轴的最大值,初始为1,下面会计算
arr_len, lst_row, lst_rst = [], [], []
arr_idx = [0] * max_y_idx # 保存每次提取max_y_idx 个元素的索引值的集合,初始值为[0, 0, 0, 0]
# 将2 维数组data 转换成1 维数组lst_row
for item in data:
_n = len(item) # 求原2 维数组中每一层的长度
arr_len.append(_n) # 保存原2 维数组中每一层的长度的集合
lst_row += item # 将原2 维数组的每个元素累加到1 维数组lst_row 中
row_max_idx *= _n # 记录1 维数组需要循环的总数
# 遍历1 维数组
for row_idx in range(row_max_idx):
# 求每个被提取的元素的索引值
for y_idx in range(max_y_idx):
# 遍历原2 维数组各层长度的'切片'集合,例如:lst = [1, 2, 3, 4]
# 则lst[2:] 为[3, 4] 即从下标2 开始后面全要;lst[:2] 为[1, 2] 即到下标2 之前都要
# _pdt 是product 乘积的缩写,记录原2 维数组当前层之下所有层长度的乘积
_pdt = 1
for n in arr_len[y_idx+1:]:
_pdt *= n
# _offset 是偏移量,记录原2 维数组当前层之上所有层长度的和
_offset = 0
for n in arr_len[:y_idx]:
_offset += n
# 计算元素提取索引:当前X 轴的值除以_pdt,再与原2 维数组当前层长度取余,最后加上偏移量
arr_idx[y_idx] = (row_idx // _pdt) % arr_len[y_idx] + _offset
# 遍历索引集合,从1 维数组中提选元素放入_lst_tmp 中
_lst_tmp = []
for idx in arr_idx:
_lst_tmp.append(lst_row[idx])
# 最后将_lst_tmp 作为元素追加到lst_rst 中
lst_rst.append(_lst_tmp)
return lst_rst
'''
相对来说,递归法更便于代码阅读,也比较符合思维的直觉;循环法就比较绕但是相对没有递归层数的限制。
以下是两种方法对于同一份数据的测试,可以看到返回的两个list 是拥有相同元素的。
'''
if __name__ == "__main__":
data = [
[1, 2],
[3, 4, 5],
[6, 7, 8, 9]
]
print('----------------')
lst1 = dynloop_loop(data)
print(len(lst1))
print(lst1)
print('----------------')
lst2 = dynloop_rcsn(data)
print(len(lst2))
print(lst2)
print('----------------')
# 如果两个list 拥有相同的元素则返回True
print(lst1 == lst2)
import itertools
from dynloop_loop_rcsn import dynloop_loop, dynloop_rcsn
if __name__ == "__main__":
data = [
[1, 2],
[3, 4, 5],
[6, 7, 8, 9],
[11, 12],
[13, 14, 15],
[16, 17, 18, 19],
[21, 22],
[23, 24, 25],
[26, 27, 28, 29]
]
print('----------------')
lst1 = dynloop_loop(data)
print(len(lst1))
#print(lst1)
print('----------------')
lst2 = dynloop_rcsn(data)
print(len(lst2))
#print(lst2)
print('----------------')
lst3 = list(map(list, (itertools.product(*data))))
print(len(lst3))
#print(lst3)
print('----------------')
print(lst1 == lst2, lst2 == lst3)
/Users/abc/PycharmProjects/testpy/venv/bin/python /Users/abc/PycharmProjects/testpy/test.py
----------------
13824
----------------
13824
----------------
13824
----------------
True True
Process finished with exit code 0