是否可以定义轻松创建 N 维列表列表的函数?

Is it possible to define function that creates an N-dimensional list of lists easily?

我搜索并发现了这些问题:How to create a multi-dimensional list and 这暗示了我正在寻找的东西,但它们似乎只制作二维数组而不是 ND 数组。

问题:

我的问题是我能够为已知的 n 创建一个 n 维列表列表,但我不确定如何将它推广到 [=17= 的所有值].

考虑这个例子:

def makeList(n):
    return [[[n for _ in range(n)] 
                for _ in range(n)] 
                for _ in range(n)]
print(makeList(3))

输出:

[[[3, 3, 3], 
  [3, 3, 3], 
  [3, 3, 3]], 
 [[3, 3, 3], 
  [3, 3, 3], 
  [3, 3, 3]], 
 [[3, 3, 3], 
  [3, 3, 3], 
  [3, 3, 3]]]

这将创建一个包含 3 个 3 的 3x3x3 数组的列表列表,这是预期的结果,但是如果我们使用不同的 n:

print(makeList(2))

输出:

[[[2, 2], 
  [2, 2]], 
 [[2, 2], 
  [2, 2]]]

这将创建一个由 2 个 2 组成的 2x2x0 数组的列表列表,这不是预期的结果。相反,结果应该是一个由 2 组成的 2x2 数组的列表列表。

同样,如果我们设置 n = 4:

print(makeList(4))

当它应该给出 4x4x4x4 的列表列表时,它会给出一个 4x4x4 的列表列表。

主要问题是 for 循环的数量必须根据输入而改变,但显然代码不能“复活”并神奇地自行重新编码,因此我的问题。

我正在寻找一种简单的方法来获得这个结果。我确信我可以继续开发解决方案的想法,但我还没有想到任何简洁的东西。

我试过的:

我第一个想到的想法是使用递归,这是我的简单方法:

def makeList(n, output = []):
    if not output:
        output = [n for _ in range(n)]
    else:
        output = [output for _ in range(n)]
    if len(output) == n:
        return output
    else:
        return makeList(n, output)

这显然行不通,因为 if len(output) == n: 将执行第一次迭代,因为内部循环的长度等于最外层循环的长度。然而,即使有一个条件可以正确终止函数,这个解决方案仍然不是理想的,因为我可以 运行 进入具有大值 n 的最大递归错误。此外,即使这些问题得到解决,此代码仍然很长且耗时。

我考虑的另一个潜在观点(以及我发现有效的解决方案)是使用 dict。我的解决方案将列表的中间列表保存在 dict 中,以便可以用于下一次迭代:

def makeList(n):
    d = {str(i): None for i in range(n)}
    for i in range(n):
        if i == 0:
            d[str(i)] = [n for _ in range(n)]
        else:
            d[str(i)] = [d[str(i-1)] for _ in range(n)]
    return d[str(n-1)]

但同样,这很长而且看起来不是很 pythonic。

解法要求:

理想的解决方案将比我的更简洁,具有仅使用内置函数的高效时间复杂度。

只要答案的精神是尽力满足要求,其他接近这些要求的选项也会有所帮助。

然而,简洁是最重要的方面,这就是为什么我对目前的尝试并不完全满意。

您的最佳计划是为此使用 numpy。您可以使用 np.zeros( (4,4,4) ) 创建一个全为 0 的任意数组,或者使用 np.ones( (4,4,4) ) 创建一个全为 1 的数组。如果你要经常使用数组,你肯定会想要使用 numpy.

我已经草拟了一个可能适合的递归函数:

def nd_list(n, x=None):
    if x is None:
        x = n
    if x == 1:
        return [n] * n
    return [nd_list(n, x-1)] * n

两行不使用外部库!

def nd_list(x, z):
    return [z] * z if x == 1 else [nd_list(x-1, z)] * z

两行(仅)且没有 list of lists surprising behaviour:

def nd_list(x, z):
    return [0 for _ in range(z)] if x == 1 else [nd_list(x-1, z) for _ in range(z)]

tuple(n for _ in range(n))获取你想要的维度,用numpy生成多维数组:

import numpy as np

def make_array(n):
    return np.full(tuple(n for _ in range(n)), n)

for n in range(1,5):
    print(make_array(n))

输出:

[1]

[[2 2]
 [2 2]]

[[[3 3 3]
  [3 3 3]
  [3 3 3]]

 [[3 3 3]
  [3 3 3]
  [3 3 3]]

 [[3 3 3]
  [3 3 3]
  [3 3 3]]]

[[[[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]

  [[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]

  [[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]

  [[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]]


 [[[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]

  [[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]

  [[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]

  [[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]]


 [[[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]

  [[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]

  [[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]

  [[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]]


 [[[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]

  [[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]

  [[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]

  [[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]]]

使用 python 模式匹配支持

def nlist(shape: tuple[int, ...], fill_with=None):
    match shape:
        case (n,):
            return [fill_with] * n
        case (n, *rest):
            return [nlist(rest, fill_with) for _ in range(n)]
        case _:
            raise ValueError(f'Invalid value shape={shape}')


def makeList(n):
    return nlist((n,)*n, n)

我的意思是,我喜欢 numpy 和所有东西,但如果我想进行一般的动态编程,我不想 pip install 一个庞大的库。对于需要复杂 DP 的技术面试也会很吃力。

这是一个简单易读的函数,它利用了 Python 中的 built-in itertools 模块(它使用 repeat)和 copy 模块用于制作列表的深层副本(否则,您会看到修改一个条目会修改多个条目的“令人惊讶的”列表行为)。它在 API 方面也很正常:你可以用维度元组调用它(比如 numpy 数组的 shape 字段):

from itertools import repeat
from copy import deepcopy

def ndarray(shape, val=None):
    base = val
    for dim in reversed(shape):
        base = list(map(deepcopy, repeat(base, times=dim)))
    return base

这是制作的:

>>> import pprint
>>> pprint.pprint(ndarray((2, 3, 4), val=1))
[[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]],
 [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]]

对于您原来的问题,您可以轻松地 makeList(n) 围绕这个问题:

def makeList(n, val=None):
    return ndarray([n]*n, val=val)