python列表输入10个数、并排序-我该如何对一百万个数字进行排序,并且仅在Python中打印前十个数字?...
我有一個包含一百萬個數(shù)字的文件。 我需要知道如何有效地對其進行排序,以免使計算機停滯不前,并且僅打印前十名。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33#!/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])
我知道這是選擇排序,我不確定什么是最好的排序。
這是作業(yè)嗎? 還是一本書中的練習(xí)?
它的功課..
這顯然是一個XY問題。 問題不是排序,而是找到十個最大的整數(shù)。 盡管可以通過首先排序然后選擇前十個條目來找到它們,但這并不是最佳解決方案。 最好的解決方案是百事可樂提供的解決方案。
我不會說百事可樂的解決方案是"最好的",也許是第一個現(xiàn)有的解決方案。 實際上,他實際上沒有提供任何有效的代碼,但確實表明這是一個XY問題。
如果只需要前10個值,那么您將浪費大量時間對每個數(shù)字進行排序。
只需瀏覽數(shù)字列表,并跟蹤到目前為止看到的前10個最大值。在瀏覽列表時更新前十名,并在到達末尾時將其打印出來。
這意味著您只需要對文件進行一次遍歷(即theta(n)的時間復(fù)雜度)
一個更簡單的問題
您可以將您的問題看成是在數(shù)字列表中找到最大值的概括。如果給出{2,32,33,55,13, ...}并被要求找出最大值,那么您會怎么做?典型的解決方案是瀏覽列表,同時記住迄今為止遇到的最大數(shù)字,并將其與下一個數(shù)字進行比較。
為了簡單起見,讓我們假設(shè)我們正在處理正數(shù)。
1
2
3
4
5
6
7
8Initialize 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
如此看來,我們可以在列表的單個遍歷中找到最大值,這與任何類型的比較排序相反。
泛化
在列表中查找前10個值非常相似。唯一的區(qū)別是,我們需要跟蹤前10名,而不只是最大值(前1名)。
底線是您需要一些容納10個值的容器。當(dāng)您遍歷龐大的數(shù)字列表時,在大小為10的容器中關(guān)心的唯一值是最小值。這是因為,如果您發(fā)現(xiàn)了一個新號碼,該號碼應(yīng)該排在前十名之內(nèi),那么它將被替換。
無論如何,事實證明最適合快速找到分鐘的數(shù)據(jù)結(jié)構(gòu)是一個最小堆。但是我不確定您是否了解堆,而將堆用于10個元素的開銷可能會超過其好處。
任何容納10個元素并可以在合理的時間內(nèi)獲得最小值的容器都是一個好的開始。
這確實有可能會慢10倍,這可能意味著10毫秒而不是1毫秒。但這可能意味著10秒而不是1秒。
如果您想獲得前K個值,則為O(KN)(取決于您如何跟蹤前10個值),請查看en.wikipedia.org/wiki/Selection_algorithm,諸如中位數(shù)的中值為O(N )
@robertking:在OPs問題中,k給出為常數(shù)10,這就是為什么我將其簡化為theta(n)的原因。如果我們實際上關(guān)心前k個值的通用算法,則可以使用大小為k的堆來跟蹤前k個值,將其減少為theta(n * lg(k))。這可能也是heapq所做的。但是誰知道呢,也許管理堆的開銷大于遍歷大小為10的數(shù)組的開銷。您必須對其進行概要分析才能找到答案。
真正。我喜歡您的答案表明不需要對整個列表進行排序。但是,"僅追蹤前十大價值"并不像我認為的那么容易。更簡單地說,可以只取列表中的最小值,然后彈出最小值。這樣做十次,可能會很快。
抱歉,我仍在學(xué)習(xí)CS等算法。需要簡要說明一下如何處理100萬個數(shù)字中的10個嗎?
@pepsi:堆解決方案也不是最佳方案。與k無關(guān),選擇為O(n)。
@NeilG請參閱我的選擇方法答案。
@NeilG:的確如此,但是請記住在處理大文件中的數(shù)字時,OP代碼中有一條注釋,內(nèi)容為"不要存儲整個列表"。此處給出的方法只對文件進行一次遍歷,這意味著不需要立即將整個文件讀入內(nèi)存。另外,文件是順序讀取的,這利用了順序磁盤IO比隨機磁盤快得多的事實。鑒于IO很可能成為瓶頸,因此這一點很重要。
@robertking:您和larsmans是我支持的答案。
@pepsi:注意指出不存儲整個內(nèi)容的注釋(盡管一百萬個數(shù)字實際上并不是一個"大文件"),這是一個好主意。沒錯,您的解決方案只需執(zhí)行一次連續(xù)遍歷。但是," quickselect"也將順序地而不是隨機地訪問文件(但是它通常需要多次通過)。
@NeilG我的意思是我的其他答案。我做了兩個回答:P
最好的排序是部分排序,在Python庫中可以作為heapq.nlargest使用。
這樣,您就擁有了一個漂亮的O(n)解決方案,而不是一個O(nlogn)
@ julio.alegria:和O(1)內(nèi)存。
最好的事情是:您可以提供鍵功能,就像sorted一樣。
1
2
3
4
5
6
7
8
9
10import 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]
"""
謝謝羅伯特,這是我的解決方案。一百萬個單詞,只需要大約4秒鐘。謝謝!
嗯,我原以為會更快。也許您的IO比我的慢。無論如何,readlines()應(yīng)該是讀取行的最快方法,這可能是這里的瓶頸。隨意支持其他解決方案或給綠色勾號
@SethRainerKania只是讓您知道,python內(nèi)置解決方案可能不是您的老師正在尋找的解決方案,并且可能不會給您任何幫助。
虐待考慮到這一點。至少在研究新答案時,我有正確的前10名。
我建議您閱讀:en.wikipedia.org/wiki/Selection_algorithm另請注意O(N)和O(KN)之間的區(qū)別
創(chuàng)建數(shù)字列表的首選方法是numbers = map(int, f)。這樣可以避免將整個文件內(nèi)容存儲在內(nèi)存中(也可以節(jié)省一些鍵入內(nèi)容)。
謝謝斯文。那將是我的首選方式,尤其是在文件較大的情況下。
您想要的是一個好的選擇算法
以下python代碼基于功能partition()
分區(qū)將列表分為兩部分。小于" pivotValue"的值將移動到列表的開頭。大于ivotValue的值將移動到列表的末尾。
在O(N)操作中,這是通過從頭到尾遍歷列表來完成的,每次查看一個值時,它都會將其移動到列表的開頭附近(僅當(dāng)它小于樞軸值時)。
(請注意,在您的情況下,我們實際上將較大的值移到列表的開頭,因為您想要最大的值而不是最小的值)。
一旦我們以O(shè)(N)時間對列表進行了分區(qū),則在列表開始處剩下m個大數(shù)字。如果m = 10則很好,那就是您的十個最大數(shù)字。如果m大于10,則需要再次對m個最大數(shù)進行劃分,以從m個最大數(shù)中獲得10個最大數(shù)。如果m小于10,則我們需要再增加10-m個數(shù)字,因此我們將右邊的部分劃分為10-m個數(shù)字,并將它們添加到我們的m個數(shù)字中以獲得所需的10個數(shù)字。
因此,我們一直進行分區(qū),直到有10個最大的數(shù)字。這是通過select()方法完成的。整個方法通常非常快,因為每次執(zhí)行分區(qū)時,我們剩下的數(shù)量大約要處理一半。 (如果您不斷將需要查看的數(shù)字數(shù)除以2,那就很好了)。每次我們創(chuàng)建一個產(chǎn)生10個以上大數(shù)字的分區(qū)時,我們都會忽略整個數(shù)字堆,這些數(shù)字太小了。
這是代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44def 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
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]"
真好雖然,您可能應(yīng)該返回切片而不是復(fù)制列表,并且如果遵循pep 8,則代碼將更易于閱讀
感謝@NeilG Im立即閱讀pep 8。
總結(jié)
以上是生活随笔為你收集整理的python列表输入10个数、并排序-我该如何对一百万个数字进行排序,并且仅在Python中打印前十个数字?...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 操作系统课设之Linux 进程管理
- 下一篇: Android学习之网上商城(下)