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