我有以下Python词典:
[(2, [3, 4, 5]), (3, [1, 0, 0, 0, 1]), (4, [-1]), (10, [1, 2, 3])]
现在我想根据字典值的总和对它们进行排序,因此对于第一个键,值的总和是3 + 4 + 5 = 12.
我编写了以下代码来完成这项工作:
def myComparator(a,b):
print "Values(a,b): ",(a,b)
sum_a=sum(a[1])
sum_b=sum(b[1])
print sum_a,sum_b
print "Comparision Returns:",cmp(sum_a,sum_b)
return cmp(sum_a,sum_b)
items.sort(myComparator)
print items
这是我在上面运行后获得的输出:
Values(a,b): ((3, [1, 0, 0, 0, 1]), (2, [3, 4, 5]))
2 12
Comparision Returns: -1
Values(a,b): ((4, [-1]), (3, [1, 0, 0, 0, 1]))
-1 2
Comparision Returns: -1
Values(a,b): ((10, [1, 2, 3]), (4, [-1]))
6 -1
Comparision Returns: 1
Values(a,b): ((10, [1, 2, 3]), (3, [1, 0, 0, 0, 1]))
6 2
Comparision Returns: 1
Values(a,b): ((10, [1, 2, 3]), (2, [3, 4, 5]))
6 12
Comparision Returns: -1
[(4, [-1]), (3, [1, 0, 0, 0, 1]), (10, [1, 2, 3]), (2, [3, 4, 5])]
现在我无法理解比较器是如何工作的,传递了哪两个值以及会发生多少次这样的比较?它是否在内部创建了一个排序的键列表,它跟踪每个比较?这种行为似乎也很随机。我很困惑,任何帮助都会受到赞赏。
答案 0 :(得分:2)
数字和进行的比较没有记录,事实上,它可以从不同的实现中自由改变。唯一的保证是,如果比较函数有意义,该方法将对列表进行排序。
CPython使用Timsort algorithm对列表进行排序,所以你看到的是该算法执行比较的顺序(如果我没有误认为很短的列表,Timsort只使用插入排序)< / p>
Python 不跟踪&#34;键&#34;。它只是在每次进行比较时调用您的比较函数。因此,您的函数可以调用超过len(items)
次。
如果要使用密钥,则应使用key
参数。事实上你可以这样做:
items.sort(key=lambda x: sum(x[1]))
这将创建键,然后使用键上的常用比较运算符进行排序。这可以保证key
仅len(items)
次传递函数。
鉴于您的清单是:
[a,b,c,d]
您看到的比较顺序是:
b < a # -1 true --> [b, a, c, d]
c < b # -1 true --> [c, b, a, d]
d < c # 1 false
d < b # 1 false
d < a # -1 true --> [c, b, d, a]
答案 1 :(得分:1)
比较器如何工作
这很好documented:
比较两个对象x和y并根据结果返回一个整数。如果x <1,则返回值为负。 y,如果x == y则为零,如果x> 0则严格为正收率
而不是调用cmp函数,你可以写:
sum_a=sum(a[1])
sum_b=sum(b[1])
if sum_a < sum_b:
return -1
elif sum_a == sum_b:
return 0
else:
return 1
传递了两个值
从您的打印语句中,您可以看到传递的两个值。让我们看看第一次迭代:
((3, [1, 0, 0, 0, 1]), (2, [3, 4, 5]))
这里打印的是一个元组(a,b),因此传递给比较函数的实际值是
a = (3, [1, 0, 0, 0, 1])
b = (2, [3, 4, 5]))
通过您的函数,您可以比较每个元组中两个列表的总和,您可以在代码中表示sum_a和sum_b。
会发生多少次这样的比较?
我猜你真正问的是:通过调用一个函数,排序是如何工作的?
简短的回答是:它使用Timsort算法,并且它调用比较函数O(n * log n)次(注意实际的调用次数是c * n * log n,其中c &gt; 0)。
要了解正在发生的事情,请想象自己对值列表进行排序,比如v = [4,2,6,3]
。如果你系统地讨论这个问题,你可能会这样做:
所以你得到了,i =
0: 2 < 4 => [2, 4, 6, 3] (swap)
1: 6 < 4 => [2, 4, 6, 3] (no swap)
2: 3 < 6 => [2, 4, 3, 6] (swap)
重新开始:
0: 4 < 2 => [2, 4, 3, 6] (no swap)
1: 3 < 4 => [2, 3, 4, 6] (swap)
2: 6 < 4 => [2, 3, 4, 6] (no swap)
再次开始 - 没有进一步的互换,所以停止。您的列表已排序。在这个例子中,我们已经完成了3次列表,并且有3 * 3 = 9次比较。
显然这不是很有效 - sort()
方法只调用你的比较器函数5次。原因是它采用了比上述简单算法更有效的排序算法。
此外,这种行为似乎非常随机。
请注意,通常不会定义传递给比较器函数的值序列。但是,sort函数会对它接收的iterable的任意两个值进行所有必要的比较。
是否在内部创建了一个按键的排序列表,以便跟踪每个比较结果?
不,它没有在内部保留密钥列表。相反,排序算法基本上遍历您给出的列表。事实上,它构建了列表的子集,以避免进行太多的比较 - 通过Visualising Sorting Algorithms: Python's timsort
可以很好地显示排序算法在Aldo Cortesi的工作方式。答案 2 :(得分:0)
基本上,对于简单的列表,例如[2,4,6,3,1]和您提供的复杂列表,排序算法是相同的。
唯一的区别是列表中元素的复杂性以及如何比较任何两个元素(例如您提供的myComparator
)的比较方案。
Python排序有一个很好的描述:https://wiki.python.org/moin/HowTo/Sorting
答案 3 :(得分:0)
首先,cmp()函数:
cmp(...)
cmp(x, y) -> integer
Return negative if x<y, zero if x==y, positive if x>y.
您使用的是以下行:items.sort(myComparator)
,相当于说:items.sort(-1)
或items.sort(0)
或items.sort(1)
由于您希望根据每个元组列表的总和进行排序,您可以这样做:
mylist = [(2, [3, 4, 5]), (3, [1, 0, 0, 0, 1]), (4, [-1]), (10, [1, 2, 3])]
sorted(mylist, key=lambda pair: sum(pair[1]))
我认为,这正是你想要的。根据每个元组列表的mylist
对sum()
进行排序