''' 动态循环即不定层数循环的两种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