如何对100万个数字进行排序,并仅打印Python中排名前10的数字?

如何对100万个数字进行排序,并仅打印Python中排名前10的数字?,python,Python,我有一个有一百万个数字的文件。我需要知道如何有效地对它进行排序,这样它就不会使计算机停机,并且只打印前10名 #!/usr/bin/python3 #Find the 10 largest integers #Don't store the whole list import sys def fOpen(fname): try: fd = open(fname,"r") except: prin

我有一个有一百万个数字的文件。我需要知道如何有效地对它进行排序,这样它就不会使计算机停机,并且只打印前10名

#!/usr/bin/python3

#Find the 10 largest integers
#Don't store the whole list

import sys

def fOpen(fname):
        try:
                fd = open(fname,"r")
        except:
                print("Couldn't open file.")
                sys.exit(0)
        all = fd.read().splitlines()
        fd.close()
        return all

words = fOpen(sys.argv[1])

big = 0
g = len(words)
count = 10

for i in range(0,g-1):
        pos = i
        for j in range(i+1,g):
                if words[j] > words[pos]:
                        pos = j
                if pos != i:
                        words[i],words[pos] = words[pos],words[i]
                count -= 1
                if count == 0:
                        print(words[0:10])

我知道这是选择排序,我不确定什么是最好的排序。

最好的排序是部分排序,可以在Python库中找到。

如果您只需要前10个值,那么您将浪费大量时间对每个数字进行排序

只需浏览数字列表,并记录迄今为止看到的前10个最大值。在浏览列表时更新前十名,并在结束时打印出来

这意味着您只需要对文件进行一次遍历(即θ(n)的时间复杂度)

一个更简单的问题

您可以将您的问题视为在数字列表中查找最大值的一般化。如果给您
{2,32,33,55,13,…}
并要求您查找最大值,您会怎么做?典型的解决方案是浏览列表,同时记住迄今为止遇到的最大数字,并将其与下一个数字进行比较

为了简单起见,让我们假设我们处理的是正数

Initialize max to 0
0 < 2, so max = 2
2 < 32, so max = 32
32 < 33, so max = 33
33 < 55, so max = 55
55 > 13, so max = 55
...
return max
将最大值初始化为0
0小于2,因此最大值=2
2<32,因此最大值=32
32<33,因此最大值=33
33<55,因此最大值=55
55>13,因此最大值=55
...
返回最大值
所以你看,我们可以在列表的一次遍历中找到最大值,而不是任何类型的比较排序

泛化

在列表中查找前10个值非常相似。唯一的区别是,我们需要跟踪前10名,而不仅仅是最大值(前1名)

底线是您需要一个包含10个值的容器。当您在庞大的数字列表中迭代时,您关心的10号容器中的唯一值是最小值。这是因为,如果你发现了一个新的数字,而这个数字值得进入到目前为止的前十名,那么这个数字将被替换

无论如何,事实证明,最适合快速查找min的数据结构是min堆。但是我不确定您是否已经了解了堆,并且将堆用于10个元素的开销可能会超过它的好处

任何能容纳10个元素并能在合理时间内获得最小值的容器都是一个好的开始。

您想要的是一个好的开始

import heapq

with open('nums.txt') as f:
    numbers=map(int,f.readlines())
    print heapq.nlargest(10,numbers)
    print heapq.nsmallest(10,numbers)
"""
[1132513251, 13252365, 23512, 2000, 1251, 1235, 324, 100, 82, 82]
[1, 1, 7, 13, 15, 21, 22, 22, 33, 82]
"""
以下python代码基于函数
partition()
分区将列表一分为二。小于“pivotValue”的值将移动到列表的开头。大于pivotValue的值将移动到列表的末尾。 这在O(N)操作中通过从头到尾遍历列表来完成,每次它查看一个值时,它都会将其移动到列表的开头附近,前提是该值小于透视值

(注意,在您的例子中,我们实际上会将较大的值移到列表的开头,因为您想要的是最大的值,而不是最小的值)

一旦我们在O(N)时间内对列表进行了分区,列表的开头就剩下m个大数字。如果m=10,那太好了,这就是你的十大数字。如果m大于10,那么我们需要再次划分m个最大数,从m个最大数中得到10个最大数。如果m小于10,那么我们需要多出10-m个数,所以我们对右边的部分进行划分,找到10-m个数,然后将它们添加到m个数中,得到我们需要的10个数

所以我们继续分区,直到我们有10个最大的数字。这是通过
select()
方法完成的。整个方法通常是非常快速的,因为每次我们做一个分区,我们剩下大约一半的数字要处理。(如果您经常将需要查看的数字除以2,这很好)。每次我们做一个产生10个以上较大数字的分区时,就会忽略一整堆太小的数字

代码如下:

def partition(_list,left,right,pivotIndex):
    pivotValue=_list[pivotIndex]
    _list[right],_list[pivotIndex]=pivotValue,_list[right]
    storeIndex=left
    for i in range(left,right):
        if _list[i] > pivotValue:
            _list[storeIndex],_list[i]=_list[i],_list[storeIndex]
            storeIndex+=1
    _list[right],_list[storeIndex]=_list[storeIndex],_list[right]
    return storeIndex

from random import randint
def select(_list,left,right,k):
    if left==right:
        return _list[:left+1]
    pivotIndex=randint(left,right)
    pivotNewIndex=partition(_list,left,right,pivotIndex)
    pivotDist=pivotNewIndex-left+1
    if pivotDist==k:
        return _list[:pivotNewIndex+1]
    elif k<pivotDist:
        return select(_list,left,pivotNewIndex-1,k)
    else:
        return select(_list,pivotNewIndex+1,right,k-pivotDist)

_list=[1,2,109,2234,23,6,1,234,11,4,12451,1]

left=0
right=len(_list)-1
pivotIndex=4

print _list
"[1, 2, 109, 2234, 23, 6, 1, 234, 11, 4, 12451, 1]"
print partition(_list,left,right,pivotIndex) #partition is order(N).
"7" #index 7, so the lowest number are in the first 7 numbers of the list [1, 2, 1, 6, 1, 11, 4, 23]
print _list
"[1, 2, 1, 6, 1, 11, 4, 23, 2234, 109, 12451, 234]"
print select(_list,left,right,10)
"[1, 2, 1, 1, 4, 11, 6, 23, 109, 234]"

with open('nums.txt') as f:
    numbers=map(int,f.readlines())
    print select(numbers,0,len(numbers)-1,10)
    "[1132513251, 2000, 23512, 13252365, 1235, 1251, 324, 100, 82, 82]"
def分区(_列表,左,右,数据透视索引):
数据透视值=_列表[数据透视索引]
_列表[右],_列表[数据透视索引]=数据透视值,_列表[右]
storeIndex=左
对于范围内的i(左、右):
如果_list[i]>数据透视值:
_列表[storeIndex],\u列表[i]=\u列表[i],\u列表[storeIndex]
storeIndex+=1
_列表[右],_列表[存储索引]=_列表[存储索引],_列表[右]
返回存储索引
从随机导入randint
def选择(_列表,左,右,k):
如果左==右:
返回列表[:左+1]
pivotIndex=randint(左、右)
pivotNewIndex=分区(_列表,左,右,pivotIndex)
pivotDist=pivotNewIndex left+1
如果pivotDist==k:
返回_列表[:pivotNewIndex+1]

艾利夫:这是家庭作业吗?或者是一本书上的练习?这显然是一个错误。问题不是排序,而是找到十个最大的整数。虽然可以通过先排序然后挑选前十个条目来找到它们,但这并不是最好的解决方案。最好的解决方案是百事可乐提供的。我不会说百事可乐的解决方案是“最好的”,也许是第一个现有的解决方案。事实上,他实际上没有提供任何工作代码,但确实表明这是一个XY问题。这样你就有了一个漂亮的O(n)解决方案,而不是一个O(nlogn)@julio.alegria:和O(1)内存。最好的一点是:你可以提供一个键函数,就像
排序
一样。谢谢你,罗伯特,这是我使用的解决方案。100万字,只需4秒钟。谢谢你!嗯,我原以为会比那快。也许你的IO比我的慢。无论如何,readlines()应该是读取行的最快方式,这可能是这里的瓶颈。您可以随意选择其他解决方案,也可以选择绿色解决方案tick@SethRainerKania让您知道,python内置解决方案可能不是您的老师想要的,也可能不会给您带来任何分数。我会考虑这一点。至少在我工作的时候