在给定范围内查找列表的所有可能子列表的高效和 Pythonic 方法,以及将其中的所有元素相乘后的最小乘积?

Efficient & Pythonic way of finding all possible sublists of a list in given range and the minimum product after multipying all elements in them?

这两件事我都做到了

  1. 在给定范围 (i ,j) 中查找列表的所有可能子列表。

    A = [ 44, 55, 66, 77, 88, 99, 11, 22, 33 ] 
    Let, i = 2 and j = 4
    

    那么,给定范围(2,4)中列表"A"的可能子列表是:

    [66], [66,77], [66,77,88], [77], [77,88], [88]
    
  2. 并且,将子列表的所有元素相乘后的结果乘积的最小值:

    因此,将上述子列表中的所有元素相乘后的结果列表将变为

    X = [66, 5082, 447216, 77, 6776, 88]`    
    

    现在,上面列表的最小值,即 min(X)66

我的代码

i, j = 2, 4
A = [ 44, 55, 66, 77, 88, 99, 11, 22, 33 ] 
O, P = i, i
mini = A[O]
while O <= j and P <= j:
    if O == P:
        mini = min(mini, reduce(lambda x, y: x * y, [A[O]]))
    else:
        mini = min(mini, reduce(lambda x, y: x * y, A[O:P + 1]))
    P += 1
    if P > j:
        O += 1
        P = O
print(mini)

我的问题:

对于更大的列表和更大的范围,这段代码需要更多的时间来执行!
是否有任何可能的 "Pythonic" 方法来降低上述代码的时间复杂度?

提前致谢!

编辑:

知道了。但是,如果存在多个具有相同最小乘积的可能子列表,

  1. 我需要最长的子列表范围(i,j)
  2. 如果还有多个子列表具有相同的"longest sub range",我需要打印具有最低起始索引的子区间。


如果 (i,j) = (0,4),请考虑此列表 A = [2, 22, 10, 12, 2]
有一条领带。 Min product = 2 有两种可能性 '(0,0)' and '(4,4)'
两个子列表范围 = 0 [ (0-0) and (4-4) ]
在这种情况下,我需要 print (minproduct, [sublist-range]) = 2, [0,0]

尝试使用字典,它适用于某些输入但不适用于所有输入!如何做到这一点 'efficiently' ?
谢谢!

看一看itertools.combinations()

https://docs.python.org/3/library/itertools.html#itertools.combinations

循环调用它传递子列表,另一个参数从 1 到子列表的长度不等。

肯定要"more time to get executed for the Larger Lists and Larger Ranges",我觉得这是必然的。但可能比你的方法快得多。测量看看。

首先,给定列表和索引范围,我们可以得到子列表A[i : j + 1]

[66, 77, 88]

对于正整数aba * b不小于ab。所以你不需要做乘法,两个或多个元素相乘不可能有更小的结果。此列表的最小值所有相乘结果的最小值。

所以结果是:

min(A[i : j + 1])

为了生成子列表,它就像列表理解中的两个嵌套 for 循环一样简单:

def sublists(l,i,j):
    return [l[m:n+1] for m in range(i,j+1) for n in range(m,j+1)]

示例:

>>> sublists(A,2,4)
[[66], [66, 77], [66, 77, 88], [77], [77, 88], [88]]

寻找最小乘积:

>>> min(map(prod, sublists(A,2,4)))
66

(您从 numpy 导入 prod,或将其定义为 def prod(x): return reduce(lambda i,j:i*j,x)

#EDIT:快速解决方案:

min(A[i:j+1])

由于所有数字都是正整数,而你想求A[i:j+1]列表
所有可能子列表的最小乘积 切片,它还将包含长度为 1 的子列表。所有此类子列表的最小乘积将是 A[i:j+1] 切片中最小的数字。

另一种解法:

当您需要查找子列表的最大乘积或者您需要A[i:j+1]列表切片的所有可能组合时,下面的方法将很有用。

我们将使用 itertools.combinations 来解决这个问题。我们可以分 3 个步骤完成。

第一步:获取列表的切片

my_list = A[i:j+1]

这将为我们提供要处理的切片。

my_list = A[2:5]
my_list
[66, 77, 88]

Step-2 生成所有可能的组合:

import itertools

my_combinations = []

for x in range(1, len(my_list)+1):
    my_combinations.extend(list(itertools.combinations(my_list,x)))

my_combinations
[(66,), (77,), (88,), (66, 77), (66, 88), (77, 88), (66, 77, 88)]

iterools.combinations returns r length subsequences of elements from the input iterable

因此,我们将使用它来生成长度为 1 到长度等于 my_list 的子序列。我们将得到一个元组列表,每个元素都是一个子序列。

第 3 步:查找所有可能组合的最小乘积

products_list = [reduce(lambda i,j:i*j, x) for x in my_combinations]
[66, 77, 88, 5082, 5808, 6776, 447216]

min(products_list)
66

获得子序列后,我们应用列表理解和 reduce() 来获取 my_combinations 列表中所有子序列的产品列表。然后我们应用 min() 函数从 products_list 中得到最小乘积,这将给出我们的答案。

接受的答案对所有正整数都是正确的,因为您不能将最小元素乘以任何数字并得到较小的结果。如果您获得的所有切片长度都大于 1,这可能更有意义。

如果您要计算它,那么您可以使用 itertools.islice 获取每个切片并使用生成器表达式获取最小值:

from itertools import islice
from operator import mul

print(min(reduce(mul, islice(A, n, k + 1), 1)
          for n in range(i, j + 1) for k in range(n, j + 1)))

66

如果对于 i = 0 和 j = 4,您认为 (44, 55, 66, 88) 是合法的切片,那么您需要使用 itertools.combinations.

def solution(a_list):

    sub = [[]]

    for i in range(len(a_list)):
        for j in range(len(a_list)):
            if(i == j):
                sub.append([a_list[i]])
            elif(i > j):
                sub.append([a_list[j],a_list[i]])
    sub.append(a_list)
    return sub


solution([10, 20, 30])

[[], [10], [10, 20], [20], [10, 30], [20, 30], [30], [10, 20, 30]]