为什么我在Python中的冒泡排序这么慢?

时间:2009-06-15 17:28:23

标签: python bubble-sort

我有以下代码使用冒泡排序来反转列表并且性能最差:

for i in xrange(len(l)):
    for j in xrange(len(l)):
        if l[i]>l[j]:
            l[i], l[j] = l[j], l[i]

在某些情况下(当len(l) = 100000时)代码花费超过2小时才能完成执行,我认为它很奇怪,请更正我的代码或提供一些建议。我们欢迎numpynumarray解决方案。

18 个答案:

答案 0 :(得分:25)

冒泡排序是一种可怕的排序算法。这很可能就是原因。如果需要速度,我会尝试另一种算法,如快速排序或合并排序。

答案 1 :(得分:13)

这不是一个泡泡排序...除非我犯了一个小错误,否则这将更接近python冒泡排序:

swapped = True
while swapped:
  swapped = False
  for i in xrange(len(l)-1):
    if l[i] > l[i+1]:
      l[i],l[i+1] = l[i+1],l[i]
      swapped = True

请注意,整个想法是“气泡”沿着数组移动,交换相邻值,直到它在列表中移动,没有任何交换。可以进行一些优化(例如缩小内部循环的大小),但是当你“面向家庭作业”时,它们通常只值得打扰。

编辑:固定长度() - > LEN()

答案 2 :(得分:6)

冒泡排序可能可怕等,但你更喜欢O(N ^ 2)算法超过100个项目,或O(1)一个需要拨号连接吗?

100个项目的清单不应该花费2个小时。我不知道python,但你做这些作业时是否有机会复制整个列表?

这是Python中的冒泡排序(来自Google因为我很懒):

def bubbleSort(theList, max):
    for n in range(0,max): #upper limit varies based on size of the list
        temp = 0
        for i in range(1, max): #keep this for bounds purposes
            temp = theList[i]
            if theList[i] < theList[i-1]:
                theList[i] = theList[i-1]
                theList[i-1] = temp

和另一个,来自维基百科:

def bubblesort(l):
    "Sorts l in place and returns it."
    for passesLeft in range(len(l)-1, 0, -1):
        for index in range(passesLeft):
            if l[index] < l[index + 1]:
               l[index], l[index + 1] = l[index + 1], l[index]
    return l

冒泡排序的顺序是N(N-1)。这基本上是N ^ 2,因为您需要扫描列表并比较每个元素。

顺便说一下,你可能会发现C ++是最快的,然后是Java,然后是Python。

答案 3 :(得分:5)

numpy解决方案是什么意思? Numpy有一些排序设施,对于那些相当小的阵列是不稳定的:

import numpy as np
a = np.random.randn(100000)
# Take a few ms on a decent computer
np.sort(a)

有3种排序算法可供使用,平均每种都是Nlog(N)。

答案 4 :(得分:4)

我相信你提到你试图用它作为比较速度的基准。

我认为Python通常比Ruby快一点,但实际上并不接近Java / C / C ++ / C#。 Java是C语言的2倍,但所有解释语言的速度都要慢100倍。

你可能会谷歌“编程语言游戏”进行大量的应用程序/语言等比较。查看Python JIT以获得更好的性能。

您也可以将它与Ruby进行比较,以查看更公平的测试。

编辑:只是为了好玩(与问题无关),请检查 -

public class Test {
    public static void main(String[]s) {
        int size=Integer.valueOf(s[0]).intValue();
        Random r=new Random();
        int[] l=new int[size];
        for(int i=0;i<size;i++)
            l[i]=r.nextInt();
        long ms=(new Date()).getTime();
        System.out.println("built");
        if(fast) {
            Arrays.sort(l);
        else {
            int temp;
            for(int i=0;i<size;i++)
                for(int j=0;j<size;j++)
                    if(l[i]>l[j]) {                        
                        temp=l[i];
                        l[j]=l[i];
                        l[j]=temp;                        
                    }
            }
        ms=(new Date()).getTime()-ms;
        System.out.println("done in "+ms/1000);
    }
}

有趣的是:Java运行时间大约为:

Array size  Slow Time   Fast time
 100k         2s          0s
  1M         23s          0s
 10M         39m          2s
100M         NO          23s

并不是说这个添加与问题有任何关系,但是内置的诅咒是神圣的快速。我认为生成比排序花费的时间更长(猜测通过调用随机和内存分配是有意义的。)

必须进入CLI并使用-Xmx1000M才能获得最后一个甚至运行。

答案 5 :(得分:3)

冒泡排序使O(N 2 )比较操作(或迭代)。对于N = 100,000,这意味着将有10,000,000,000次迭代。如果需要2个小时(称之为10,000秒),那么这意味着您每秒获得1,000,000次迭代 - 或每次迭代1微秒。这不是很快的速度,但也不算太糟糕。而且我挥手并忽略不断的乘法因素。

如果您使用了快速排序,那么您将获得Nlog(N)次迭代,这将意味着大约1,000,000次迭代,总共需要1秒。 (log 10 (N)为5;为简单起见,我将其四舍五入为。)

因此,您已经充分证明了为什么冒泡排序不适合大型数据集,而100,000个项目足以证明这一点。

答案 6 :(得分:2)

随着输入中元素数量的增加,Bubblesort通常无法很好地扩展到大多数可能的输入。 (即,它是O(N ^ 2)。)

随着N的增长,给定一个大小为N的随机输入数组,你不太可能得到一个用bubblesort快速排序的数组(例如,几乎排序的数组)。您更有可能获得需要很长时间才能排序的数组。

然而,真正的踢球者是你发布的代码不是冒泡排序。传统上,如果没有进行交换以及不尝试交换已经排序的值,bubblesort将提前终止。 (在P次传递之后,P最后的项目将按正确的顺序排列,因此您不需要处理它们。)实际发布的代码将始终检查数组中的每一对,因此它将始终运行内部循环N ^ 2次。对于100000个元素,这是10000000000次迭代。

答案 7 :(得分:2)

我认为你基本上是在浪费时间在这么大的数据集上浪费时间。有三个原因导致它变慢:

1)Python很慢 2)冒泡排序很慢 3)列出的冒泡排序编码不正确/效率低。

无论如何编码,它都是O(N ^ 2)。为什么不使用合并/树排序..或者如果你想尝试快速排序(也是最坏的情况O(N ^ 2)),你的特定数据集可能会更快。我相信如果数据已经有很多排序,快速排序在经验上会更快。

答案 8 :(得分:2)

首先,你做了太多的循环。你的内循环应该从i + 1进入列表的末尾,而不是从0开始。其次,如其他人所指出的,冒泡排序具有O(N ^ 2)复杂度,因此对于100000个元素,你循环10,000,000,000次。循环是解释语言性能最差的领域之一,这一点更加复杂。这一切都加起来令人难以置信的糟糕表现。这就是为什么任何需要这种紧密循环的计算通常用C / C ++编写并包装以供Python等语言使用。

答案 9 :(得分:2)

这里有一些代码我将基本冒泡排序与更简化的版本(基础版与修改版)进行比较 - 修改后的速度提高了约2-3倍,但仍然是慢速排序,但速度更快

from array import *
from random import *
from time import *

def randarray(typecode, numElements, minValue, maxValue):
    a = array(typecode)
    for i in xrange(0, numElements):
        a.append(randint(minValue, maxValue))
    return a

def basesort(l):
    for i in xrange(len(l)):
        for j in xrange(len(l)):
            if l[i]<l[j]:
                l[i], l[j] = l[j], l[i]
    return l

def modifiedsort(l):
    NotComplete = True
    i = 0
    arange = xrange(len(l))
    while NotComplete:
        NotComplete = False
        for j in xrange(len(l) - i):
            if l[i]<l[j]:
                l[i], l[j] = l[j], l[i]
                NotComplete = True
        i += 1

Num = 1000
b = randarray('i', Num, 1, 100000)
m = b[:]

print 'perform base bubble sort'
t = time()
basesort(b)
basetime =  time() - t
print basetime
#print a
print 'complete'

print 'perform modified bubble sort'
t = time()
modifiedsort(m)
modtime =  time() - t
print modtime
#print a
print 'complete'

print 'mod sort is ', basetime / modtime,' fast then base sort'

答案 10 :(得分:1)

首先,为了这个回复的目的,我假设 - 因为你自己声称 - 你只是这样做来对不同的语言进行基准测试。所以我不会进入“泡泡排序只是缓慢”的领域。真正的问题是为什么它在Python中要慢得多。

答案是Python本质上比C ++甚至Java慢得多。您没有在典型的事件驱动或I / O绑定应用程序中看到它,因为在等待输入或等待I / O调用完成时花费大部分时间闲置。但是,在您的情况下,算法完全受CPU限制,因此您直接测量Python字节码解释器的性能。据估计,这比执行相应的本机代码慢20-30倍,这就是C ++和Java所发生的情况。

通常,只要你在Python中编写一个长时间运行的CPU绑定循环,就应该期待这种性能。修复此问题的唯一方法是将整个循环移动到C中。仅移动主体(例如使用NumPy)对您没有多大帮助,因为循环迭代本身仍将由Python解释器执行。

答案 11 :(得分:1)

因为它将执行比较并且可能交换100,000 x 100,000次。如果计算机足够快,每秒执行最内层语句1,000,000次,那么仍然是167分钟,略短于3小时。

另一方面,为什么有这么多关于SO的无聊问题呢? Isn't being able to do simple algebra a prerequisite for programming?; - )

答案 12 :(得分:1)

对我来说,这看起来不像是冒泡,如果是,那么它的执行效率非常低。

答案 13 :(得分:1)

如果您对自己的排序感兴趣,可以使用几行代码将冒泡排序更改为梳子排序。梳子排序几乎和最好的排序一样好。当然,做出自己的排序最好留作学习练习。

  

梳子排序改进了冒泡排序,和   竞争对手的速度更加复杂   算法如Quicksort。

http://en.wikipedia.org/wiki/Comb_sort

答案 14 :(得分:0)

如果您必须自己编码,请使用插入排序。它的代码量大致相同,但速度要快几倍。

答案 15 :(得分:0)

就像其他帖子所说的那样,冒泡排序太可怕了。几乎应该不惜一切代价避免由于你所经历的糟糕表现而导致的不良后果 幸运的是,有很多其他排序算法,http://en.wikipedia.org/wiki/Sorting_algorithm,例如。

根据我在学校的经验,快速排序和合并排序是在冒泡排序中或之后不久引入的另外两种基本排序算法。因此,我建议您研究那些用于学习更有效的排序方法。

答案 16 :(得分:0)

我忘了添加,如果您对数据集的大小和密钥的分布有所了解,那么您可以使用基数排序,即O(N)。为了得到基数排序的想法,考虑你正在排序的数据或多或少分布在0到100,000之间的情况。然后你只需创建一个类似于哈希表的东西,比如一个包含100,000个列表的数组,并将每个数字添加到存储桶中。这是我在几分钟内编写的一个实现,它生成一些随机数据,对其进行排序,并打印出一个随机段。对于100,000个整数的数组执行时间不到1秒。

选项严格打开 选项明确的

模块模块1

Private Const MAX_SIZE As Integer = 100000
Private m_input(MAX_SIZE) As Integer
Private m_table(MAX_SIZE) As List(Of Integer)
Private m_randomGen As New Random()
Private m_operations As Integer = 0

Private Sub generateData()
    ' fill with random numbers between 0 and MAX_SIZE - 1
    For i = 0 To MAX_SIZE - 1
        m_input(i) = m_randomGen.Next(0, MAX_SIZE - 1)
    Next

End Sub

Private Sub sortData()
    For i As Integer = 0 To MAX_SIZE - 1
        Dim x = m_input(i)
        If m_table(x) Is Nothing Then
            m_table(x) = New List(Of Integer)
        End If
        m_table(x).Add(x)
        ' clearly this is simply going to be MAX_SIZE -1
        m_operations = m_operations + 1
    Next
End Sub

 Private Sub printData(ByVal start As Integer, ByVal finish As Integer)
    If start < 0 Or start > MAX_SIZE - 1 Then
        Throw New Exception("printData - start out of range")
    End If
    If finish < 0 Or finish > MAX_SIZE - 1 Then
        Throw New Exception("printData - finish out of range")
    End If
    For i As Integer = start To finish
        If m_table(i) IsNot Nothing Then
            For Each x In m_table(i)
                Console.WriteLine(x)
            Next
        End If
    Next
End Sub

' run the entire sort, but just print out the first 100 for verification purposes
Private Sub test()
    m_operations = 0
    generateData()
    Console.WriteLine("Time started = " & Now.ToString())
    sortData()
    Console.WriteLine("Time finished = " & Now.ToString & " Number of operations = " & m_operations.ToString())
    ' print out a random 100 segment from the sorted array
    Dim start As Integer = m_randomGen.Next(0, MAX_SIZE - 101)
    printData(start, start + 100)
End Sub

Sub Main()
    test()
    Console.ReadLine()
End Sub

结束模块 时间开始= 2009年6月15日下午4:04:08 完成时间= 6/15/2009 4:04:08 PM操作次数= 100000 21429 21430 21430 21431 21431 21432 21433 21435 21435 21435 21436 21437 21437 21439 21441 ...

答案 17 :(得分:0)

你可以做到

l.reverse()

脚本 ee.py

l = []
for i in xrange(100000):
    l.append(i)

l.reverse()

lyrae @ localhost:〜/ Desktop $ time python ee.py

real    0m0.047s
user    0m0.044s
sys    0m0.004s