有没有一种方法可以衡量列表的排序方式?


161

有没有一种方法可以衡量列表的排序方式?

我的意思是,这不是要知道列表是否已排序(布尔值),而是诸如“排序”之比,诸如统计中的相关系数之类的东西。

例如,

  • 如果列表中的项目按升序排列,则其比率为1.0

  • 如果列表降序排列,则其速率将为-1.0

  • 如果list几乎升序排列,则其比率将是0.9或接近1的某个值。

  • 如果列表根本不排序(随机),则其速率将接近0

我正在Scala中编写一个小型图书馆进行练习。我认为排序速率会很有用,但我找不到有关此类信息的任何信息。也许我不知道这个概念的适当术语。



4
这将用于确定对列表进行排序的理想算法吗?例如,对于接近0的值,QuickSort是理想的选择,但是在刻度两端(几乎排序或几乎反向排序)的值,MergeSort都快得多,因为在这种情况下,QC降为O(N ^ 2)。
Darrel Hoffman

8
+1为“同类比率”
0x499602D2 2013年

1
@Fuhrmanator算法的随机版本不必执行排序即可得出排序的概率估计。仅当您想要获得执行排序所需的精确度量时。
蒂莫西·希尔兹

1
讽刺但有趣的第一本能:您可以插入对列表进行排序,看看需要多长时间,然后将其与排序(现在已排序的)列表需要多长时间以及相反。
kqr13年

Answers:


142

您可以简单地计算列表中的反转次数。

反演

类型的元素序列中的一个反转T是一对序列元素,它们根据<的集合上的某些顺序乱序出现T

维基百科

正式地,让数字A(1), A(2), ..., A(n)序列n
如果i < jA(i) > A(j),然后在一对(i,j)被称为反转A

反转数的序列的是它的有序性的一个公共量度。
形式上,将反转数定义为反转数,即

定义

为了使这些定义更清楚,请考虑示例序列9, 5, 7, 6。该序列具有反演 (0,1), (0,2), (0,3), (2,3)反演编号 4

如果您想要介于0和之间的值1,则可以将反转数除以N choose 2

要实际创建一种算法来计算此分数对列表的排序方式,您有两种方法:

方法1(确定性)

修改您喜欢的排序算法,以跟踪其运行时正在纠正的反转次数。尽管这是不平凡的,并且根据您选择的排序算法有不同的实现方式,但是最终您得到的算法(就复杂性而言)不会比开始时的排序算法昂贵。

如果您采用这种方式,请注意,这并不像计算“掉期”那么简单。例如,Mergesort是最坏的情况O(N log N),但是如果它在以降序排列的列表上运行,它将纠正所有N choose 2反转。O(N^2)O(N log N)操作中纠正了这种反转。因此,某些操作不可避免地必须一次校正多个反转。您必须小心执行。注意:您可以非常O(N log N)复杂地完成此操作,这很棘手。

相关:计算排列中的“反转”数

方法2(随机)

  • 随机抽样对(i,j),其中i != j
  • 对于每对,确定是list[min(i,j)] < list[max(i,j)](0还是1)
  • 计算这些比较的平均值,然后通过 N choose 2

除非您对准确性有要求,否则我个人会采用随机方法-仅仅是因为它很容易实现。


如果您真正想要的是(降序排列)到(升序排列z')之间的值(),则可以使用以下公式将((升序排列)和(降序排列)之间的()以上的值简单地映射到此范围:-11z01

z' = -2 * z + 1

2
对列表排序(通常)是O(n * logn),而计算反转的幼稚/显而易见的方法是O(n ^ 2),这让我很着迷。我想知道是否有更好的算法来计算反转次数?
马克·贝西

5
这个SO问题中有两种有趣的方法:stackoverflow.com/questions/6523712/…基本上,它们等于对数组进行排序,以找出存在多少个反转。
马克·贝西

4
我天真地以为您只可以数出乱序的相邻对。但这将严重不足:1 2 3 1 2 3仅具有一个相邻的反演,但按更正确的方法将其反转50%。
Barmar

2
@Barmar我认为清单1 2 3 1 2 3将符合排序;-)
scunliffe 2013年

2
@TimothyShields,嗯,不,不是。但我不会毫不掩饰这一点。只是建议添加一个非正式的定义,该定义对于较少的符号倾向更易于使用。
克里斯·卡洛

24

列表(或其他顺序结构)的排序方式的传统度量是反转次数。

反转次数是a <b AND b <<a的st索引对(a,b)的对数。为了这些目的,<<表示您为特定排序选择的任何排序关系。

完全排序的列表没有反转,而完全颠倒的列表具有最大反转数。


5
从技术上讲,5 4 3 2 1由于未指定顺序,因此已完全排序,但我正在做
书呆子

7
@paxdiablo取决于的定义<
Marcin

@paxdiablo,那么一个人可以通过从反转数到最接近的0或的距离来衡量排序n choose 2
休恩

17

您可以使用实际的相关性。

假设您为排序列表中的每个项目分配了一个从零开始的整数等级。请注意,元素位置索引与等级的关系图看起来像直线上的点(位置与等级之间的相关系数为1.0)。

您可以计算此数据的相关性。对于反向排序,您将得到-1,依此类推。


1
对不起,但是这留下了太多无法解释的内容,就像您如何分配整数一样。
Marcin

2
您需要排序列表来分配整数。那只是项目的一个枚举。
卡兹(Kaz)2013年

1
正是我要建议的。确定对象在原始列表中的位置与其在已排序列表中的位置之间的相关性。坏消息是,关联例程可能在O(n ^ 2)中运行;好消息是它们对于您的环境而言可能是现成的。
彼得·韦伯


我很好奇...这种方法是否等同于扩展反转次数的计数?
克莱顿·斯坦利

4

答案很不错,我想在数学上增加完整性:

  • 您可以通过测量列表与已排序列表的相关程度来衡量列表的排序方式。为此,您可以使用等级相关性(最著名的是Spearman的),它与通常的相关性完全相同,但是它使用列表中元素的等级而不是其项目的模拟值。

  • 存在许多扩展,例如相关系数(精确排序为+1,精确反演为-1)

  • 这使您可以具有此度量的统计属性,例如置换中心极限定理,该定理可以让您知道此度量在随机列表中的分布。


3

除了倒数,对于数字列表,可以想象到距排序状态的均方距离:

#! ruby
d = -> a { a.zip( a.sort ).map { |u, v| ( u - v ) ** 2 }.reduce( :+ ) ** 0.5 }

a = 8, 7, 3, 4, 10, 9, 6, 2, 5, 1
d.( a ) #=> 15.556
d.( a.sort ) #=> 0.0
d.( a.sort.reverse ) # => 18.166 is the worrst case

我认为这是标准相关函数的平方,请参见en.wikipedia.org/wiki/Correlation_ratio。并同样适用于非数字列表;比较的两个值是对象在两个列表中的位置。
彼得·韦伯

我是一个简单的人。我什至不知道什么是相关比率。当我阅读顶部的Wikipedia文章时,被要求了解什么是“统计偏差”,然后是“标准差”,然后是“变异”,然后是“类间相关系数”。我几次,几次都学会了所有这些,我又忘记了。在我的这个务实的答案中,我只是用毕达哥拉斯定理来测量两个向量之间的距离,我记得这是从小学开始的。
鲍里斯·斯蒂尼克尼

1

我不确定“最佳”方法,但是一个简单的方法是将每个元素与其后的元素进行比较,如果element2> element 1(或您要测试的任何东西),则增加一个计数器,然后除以总数的元素。它应该给你一个百分比。


1

我会计算比较数并将其除以比较总数。这是一个简单的Python示例。

my_list = [1,4,5,6,9,-1,5,3,55,11,12,13,14]

right_comparison_count = 0

for i in range(len(my_list)-1):
    if my_list[i] < my_list[i+1]: # Assume you want to it ascending order
        right_comparison_count += 1

if right_comparison_count == 0:
    result = -1
else:
    result = float(right_comparison_count) / float((len(my_list) - 1))

print result

0

这样的事情怎么样?

#!/usr/bin/python3

def sign(x, y):
   if x < y:
      return 1
   elif x > y:
      return -1
   else:
      return 0

def mean(list_):
   return float(sum(list_)) / float(len(list_))

def main():
   list_ = [ 1, 2, 3, 4, 6, 5, 7, 8 ]
   signs = []
   # this zip is pairing up element 0, 1, then 1, 2, then 2, 3, etc...
   for elem1, elem2 in zip(list_[:-1], list_[1:]):
      signs.append(sign(elem1, elem2))

   # This should print 1 for a sorted list, -1 for a list that is in reverse order
   # and 0 for a run of the same numbers, like all 4's
   print(mean(signs))

main()

2
这仅计算相邻的反转。如果您查看其他答案,您会发现这还不够。
Konrad Rudolph

1
@KonradRudolph:我认为这个答案满足了所问的问题。其他答案更全面的事实并不意味着这个答案是不够的。这取决于OP的要求。
LarsH 2013年

0

如果您使用列表,请计算该列表中值的等级,然后调用该等级Y列表,然后调用另一个列表,X该列表包含从1到的整数length(Y),您可以通过计算相关系数来获得所需的排序量度。,,r两个列表之间。

r = \frac{\sum ^n _{i=1}(X_i - \bar{X})(Y_i - \bar{Y})}{\sqrt{\sum ^n _{i=1}(X_i - \bar{X})^2} \sqrt{\sum ^n _{i=1}(Y_i - \bar{Y})^2}} 

对于完全排序的列表,r = 1.0对于反向排序的列表r=-1.0,以及r这些限制之间的差异,以实现不同程度的排序。

取决于应用程序,此方法可能存在的问题是,计算列表中每个项目的等级等同于对其进行排序,因此它是O(n log n)操作。


但这不会忽略曲线形状。如果对他的数组进行排序,但是包含的值呈指数增长,则在他希望其为1.0的情况下,相关性会很小。
Lee Daniel Crocker

@LeeDanielCrocker:是的,这很不错。我修改了我的答案以通过对值进行排名来解决此问题。
西蒙(Simon)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.