【问题标题】:Recursive generic function for Cartesian product in PythonPython中笛卡尔积的递归泛型函数
【发布时间】:2022-01-19 19:50:29
【问题描述】:

我正在寻找一种方法来实现递归函数,以在不使用 itertools 包的情况下获得相同列表 n 次的通用笛卡尔积。 该函数应该将列表和 n 次作为参数。

输出示例:

>>> l = [0, 2]
>>> print([(x,y) for x in l for y in l])

>>> [(0, 0), (0, 2), (2, 0), (2, 2)]

还有:

>>> l = [0,2]
>>> print([(x,y,z) for x in l for y in l for z in l])
>>> [(0, 0, 0),(0, 0, 2),(0, 2, 0),(0, 2, 2),(2, 0, 0),(2, 0, 2),(2, 2, 0),(2, 2, 2)]

或者

>>> l = [4,5,8]
>>> print([(x,y) for x in l for y in l])
>>> [(4, 4), (4, 5), (4, 8), (5, 4), (5, 5), (5, 8), (8, 4), (8, 5), (8, 8)]

等等。

我想对每个通用列表和每个 n 元组进行概括。 我找到了不同的方法来迭代地实现这一点,但没有一种方法是递归的。希望任何人都可以帮助我。

【问题讨论】:

    标签: python recursion combinatorics cartesian-product


    【解决方案1】:

    直观

    比使用固定整数更好,我认为product(t) 应该采用可迭代的列表-

    1. 如果输入t 为空,则产生空产品()
    2. (inductive)t 至少有一个可迭代对象。对于子问题product(t[1:]) 的结果中的所有p,对于第一个可迭代t[0] 中的所有v,将v 添加到p 和yield
    def product(t):
      if not t:
        yield ()                   # 1. no iterables
      else:
        for p in product(t[1:]):   # 2. at least one iterable
          for v in t[0]:
            yield (v, *p)
    

    我将输入乘以*2 你仍然可以控制product 的输出 -

    for p in product([[1,2]] * 2):
      print(p)
    
    (1, 1)
    (2, 1)
    (1, 2)
    (2, 2)
    

    现在让我们乘以*3 -

    for p in product([[1,2]] * 3):
      print(p)
    
    (1, 1, 1)
    (2, 1, 1)
    (1, 2, 1)
    (2, 2, 1)
    (1, 1, 2)
    (2, 1, 2)
    (1, 2, 2)
    (2, 2, 2)
    

    灵活

    由于可以使用任何可迭代对象,因此您可以根据自己的喜好混合/匹配 -

    for p in product([range(2), [3,4], "hi", (9,)]):
      print(p)
    
    (0, 3, 'h', 9)
    (1, 3, 'h', 9)
    (0, 4, 'h', 9)
    (1, 4, 'h', 9)
    (0, 3, 'i', 9)
    (1, 3, 'i', 9)
    (0, 4, 'i', 9)
    (1, 4, 'i', 9)
    

    高效

    生成器的使用使product 在涉及组合数学的问题中有效使用。生成器允许我们在找到所需结果后暂停/取消并提供提前退出 -

    def solveTriangle(min, max):
      for (x,y,z) in product([list(range(min, max))] * 3):
        if x ** 2 + y ** 2 == z ** 2:
          return (x,y,z)               # <- return stops generator
      return None
    
    print(solveTriangle(10,30))
    
    (16, 12, 20)
    

    itertools

    注意itertools 模块提供product 作为内置函数。将product 用作练习很有趣,但如果您打算在生产代码中使用它,内置函数可能会提供最佳性能。

    【讨论】:

      【解决方案2】:

      试试这个

      def cartesian_product(lst, n):
          if n == 1:
              return [(x,) for x in lst]
          return [(x,) + t for t in cartesian_product(lst, n - 1) for x in lst]
      

      【讨论】:

      • 这是一个非常好的解决方案。来自我的 +1
      • 应该提到的是,这会在返回任何结果之前急切地计算 所有 产品。对于特定问题的大量输入,这可能意味着大量浪费的计算。由于 python 在整个标准库中对生成器和迭代器的强大支持,我认为最好为此使用生成器。我要补充一点,如果该函数可以计算不同列表的产品,而不仅仅是一个列表本身,它会更加灵活。
      • 另一个问题是cartesian_product,这里定义的不是total function。如果使用n = 0n &lt; 1 调用该函数,则会导致堆栈溢出。通过简单地将您的基本情况重写为n &lt;= 0return [tuple() for _ in lst],您可以使函数在所有可能的输入中正确运行。递归是一种功能遗产,因此注意这些小细节会产生最好的结果。
      猜你喜欢
      • 2013-07-19
      • 1970-01-01
      • 1970-01-01
      • 2019-04-20
      • 1970-01-01
      • 2012-02-24
      • 2021-05-18
      • 1970-01-01
      • 2016-02-15
      相关资源
      最近更新 更多