根据各个对应元素的比例或基于第三个列表,在Python中对2个列表进行排序

时间:2017-07-19 16:33:35

标签: python algorithm list lambda

我正在尝试为小数knapsack problem编写不同的实现。

为此,我有2个阵列:

  1. 权重
  2. 元素值[n]对应于元素权重[n]。所以我们可以将value_per_unit计算为:

    for I in range(values):
        value_per_unit.append(values[I]/weights[I])
    value_per_unit.sort()
    

    我现在需要根据value_per_unit数组排序2个数组(值和权重)

    例如: 如果

    • values = [60,100,120]
    • weight = [20,50,30]

    然后

    • values_per_unit = [3.0,2.0,4.0]

    • 所以values_per_unit_sorted将是[2.0,3.0,4.0]

    我需要将值和权重数组变为:

    • values_sorted = [100,60,120]
    • weights_sorted = [50,20,30]

    有没有办法使用简单的lambda函数来实现这个目的?

    我仍然可以做这样的事情,但是每次我需要访问元素时效率都非常低:

    weights[(value_per_unit_sorted.index(max(value_per_unit_sorted)))]
    

4 个答案:

答案 0 :(得分:6)

在一行中:

values, weights = zip(*sorted(zip(values, weights), key=lambda t: t[0]/t[1]))

解释:首先,压缩列表以配对它们。

pairs = zip(values, weights) 
# [(60, 20), (100, 50), (120, 30)]

然后,按值与重量的商进行排序。

sorted_pairs = sorted(pairs, key=lambda t: t[0]/t[1]) 
# [(100, 50), (60, 20), (120, 30)]

最后,将它们解压缩回单独的列表中。

values, weights = zip(*sorted_pairs)
# (100, 60, 120), (50, 20, 30)

另一种方法是构造明确包含比率作为第一个元素的元组。

ratios, values, weights = zip(*sorted((v/w, v, w) for v, w in zip(values, weights)))

前者在某些快速测试中似乎稍快一些。如果您正在寻找最佳算法,那么您可能需要展开一些内容,而且解决方案也不会那么简洁。

要解决@TomWyllie的评论,如果你已经有比率列表,你可以使用:

ratios, values, weights = zip(*sorted(zip(ratios, values, weights)))

注意,在两对具有相等比率的情况下,最后两个解决方案与初始解决方案不同。这些解决方案将按值排序,而第一个解决方案将使项目保持与原始列表相同的顺序。

答案 1 :(得分:0)

对于更明确(但可以说更少pythonic)的解决方案,创建一个索引列表, value_per_unit中该索引处的值排序,并重新排序valuesweights因此。{/ p>

sorted_indices = [index for index, value in 
                  sorted(enumerate(value_per_unit), key=lambda x: x[1])]
values = [values[i] for i in sorted_indices]
weights = [weights[i] for i in sorted_indices]

print(values, weights)

输出:

([100, 60, 120], [50, 20, 30])

你可以整理一下,使用zip消除不必要的额外循环,并使用生成器表达式;

values, weights = zip(*((values[i], weights[i]) for i, value in
                  sorted(enumerate(value_per_unit), key=lambda x: x[1])))
print(values)
print(weights)

哪些输出;

(100, 60, 120)
(50, 20, 30)

请注意,这些最终值为tuples而不是lists。如果您确实需要将输出作为列表,那么简单的values, weights = map(list, (values, weights))就足够了。你甚至可以把它包装成一个班轮,虽然到那时它可能很难跟上发生的事情。

答案 2 :(得分:0)

执行此操作的一种优雅方法是使用值和权重创建多维列表:

for i in range(len(values)):
    values_and_weights.append([values[i], weights[i])
# The resulting list is [[60, 20], [100, 50], [120, 30]]

然后,使用以权重除以权重的排序方法作为关键字。

values_and_weights.sort(key=(lambda x: x[0]/x[1]))

答案 3 :(得分:-1)

您遇到的问题是在每个元素上使用计算字段(元素I将具有计算值values[I]/weights[I])。 要解决这个问题并且仍然非常容易理解,您可以将其转换为这种形式的元组:( calculated_value, (value, weight) ),每个元素。

这种方法使其易于阅读和理解。请看以下解决方案:

values = [60, 100, 120]
weights = [20, 50, 30]
value_per_unit = []

for I in range(len(values)):
    value_per_unit.append( (values[I]/weights[I], (values[I], weights[I])) )
sorted_value_per_unit = sorted(value_per_unit, key=lambda x: x[0])

sorted_values = []
sorted_weights = []
for I in range(len(values)):
    (value, weight) = sorted_value_per_unit[I][1]
    sorted_values.append(value)
    sorted_weights.append(weight)

print(str(sorted_values))
print(str(sorted_weights))

另外,请注意我从原始代码修改了循环:

range(values)已更改为range(len(values))

因为范围需要列表的长度,而不是列表本身。