オブジェクトのリストは2つあります。各リストは、日時型のオブジェクトのプロパティによって既に並べ替えられています。 2つのリストを1つのソート済みリストに結合したいと思います。ソートを行うだけの最良の方法ですか、それともPythonでこれを行うよりスマートな方法がありますか?
人々はこれを複雑にしすぎているようです。
_>>> l1 = [1, 3, 4, 7]
>>> l2 = [0, 2, 5, 6, 8, 9]
>>> l1.extend(l2)
>>> sorted(l1)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
_
..またはそれより短い(および_l1
_を変更せずに):
_>>> sorted(l1 + l2)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
_
..簡単!さらに、2つの組み込み関数のみを使用しているため、リストが適切なサイズであると仮定すると、ループでの並べ替え/マージの実装よりも高速になります。さらに重要なことに、上記のコードははるかに少なく、非常に読みやすくなっています。
リストが大きい場合(数十万を超えると思います)、代替/カスタムのソート方法を使用する方が速いかもしれませんが、他の最適化が最初に行われる可能性があります(たとえば、数百万のdatetime
オブジェクト)
timeit.Timer().repeat()
(関数を1000000回繰り返す)を使用して、 ghoseb's ソリューションに対して緩やかにベンチマークを行い、sorted(l1+l2)
を大幅に高速化しました。
_merge_sorted_lists
_がかかりました。
_[9.7439379692077637, 9.8844599723815918, 9.552299976348877]
_
sorted(l1+l2)
take ..
_[2.860386848449707, 2.7589840888977051, 2.7682540416717529]
_
pythonでこれを行うよりスマートな方法はありますか
これは言及されていないので、先に進みます-python 2.6+のheapqモジュールに merge stdlib function があります。もちろん、自分で実装したい場合は、merge-sortのマージが最適です。
>>> list1 = [1, 5, 8, 10, 50]
>>> list2 = [3, 4, 29, 41, 45, 49]
>>> from heapq import merge
>>> list(merge(list1, list2))
[1, 3, 4, 5, 8, 10, 29, 41, 45, 49, 50]
ドキュメント です。
len(l1 + l2) ~ 1000000
を使用しない限り、長い話:
L = l1 + l2
L.sort()
図とソースコードの説明は、 こちら にあります。
この図は、次のコマンドによって生成されました。
$ python make-figures.py --nsublists 2 --maxn=0x100000 -s merge_funcs.merge_26 -s merge_funcs.sort_builtin
これは単にマージされます。各リストをスタックのように扱い、2つのスタックヘッドのうち小さい方を連続的にポップし、スタックの1つが空になるまで、結果リストにアイテムを追加します。次に、残りのすべてのアイテムを結果リストに追加します。
ghoseb's ソリューションにわずかな欠陥があり、O(n)ではなくO(n ** 2)になります。
問題は、これが実行していることです:
item = l1.pop(0)
リンクされたリストまたは両端キューでは、これはO(1)操作であるため、複雑さには影響しませんが、pythonリストはベクトルとして実装されるため、 l1の残りの要素を1スペース左にコピーし、O(n)操作。これはリストを通過するたびに行われるため、O(n) O(n ** 2)アルゴリズムへの変換これは、ソースリストを変更せず、現在の位置を追跡するだけの方法を使用して修正できます。
dbr で示唆されているように、単純なソート済み(l1 + l2)と修正済みアルゴリズムのベンチマークを試しました。
def merge(l1,l2):
if not l1: return list(l2)
if not l2: return list(l1)
# l2 will contain last element.
if l1[-1] > l2[-1]:
l1,l2 = l2,l1
it = iter(l2)
y = it.next()
result = []
for x in l1:
while y < x:
result.append(y)
y = it.next()
result.append(x)
result.append(y)
result.extend(it)
return result
これらを生成したリストでテストしました
l1 = sorted([random.random() for i in range(NITEMS)])
l2 = sorted([random.random() for i in range(NITEMS)])
リストのさまざまなサイズについて、次のタイミングを取得します(100回繰り返す):
# items: 1000 10000 100000 1000000
merge : 0.079 0.798 9.763 109.044
sort : 0.020 0.217 5.948 106.882
したがって、実際には、dbrが正しいように見えます。非常に大きなリストを期待している場合を除き、sorted()を使用することをお勧めしますが、アルゴリズムの複雑さは劣ります。損益分岐点は、各ソースリストの約100万アイテム(合計200万アイテム)にあります。
ただし、マージアプローチの利点の1つは、ジェネレーターとして書き換えるのが簡単であり、実質的に少ないメモリを使用することです(中間リストは不要です)。
[編集]フィールド「date
」を含むオブジェクトのリストを使用して、質問により近い状況でこれを再試行しました。これは日時オブジェクトです。上記のアルゴリズムは、代わりに.date
と比較するように変更され、ソート方法は次のように変更されました。
return sorted(l1 + l2, key=operator.attrgetter('date'))
これにより状況は少し変わります。比較のコストが高いということは、実装の一定時間の速度と比較して、実行する数がより重要になることを意味します。これは、マージによって失われた地面が埋められ、代わりに100,000アイテムでsort()メソッドを超えることを意味します。さらに複雑なオブジェクト(たとえば、大きな文字列またはリスト)に基づいて比較すると、このバランスがさらにシフトする可能性があります。
# items: 1000 10000 100000 1000000[1]
merge : 0.161 2.034 23.370 253.68
sort : 0.111 1.523 25.223 313.20
[1]:注:実際には、1,000,000個のアイテムに対して10回だけリピートしましたが、かなり遅いため、それに応じてスケールアップしました。
これは、2つのソートされたリストの単純なマージです。整数の2つのソートされたリストをマージする以下のサンプルコードをご覧ください。
#!/usr/bin/env python
## merge.py -- Merge two sorted lists -*- Python -*-
## Time-stamp: "2009-01-21 14:02:57 ghoseb"
l1 = [1, 3, 4, 7]
l2 = [0, 2, 5, 6, 8, 9]
def merge_sorted_lists(l1, l2):
"""Merge sort two sorted lists
Arguments:
- `l1`: First sorted list
- `l2`: Second sorted list
"""
sorted_list = []
# Copy both the args to make sure the original lists are not
# modified
l1 = l1[:]
l2 = l2[:]
while (l1 and l2):
if (l1[0] <= l2[0]): # Compare both heads
item = l1.pop(0) # Pop from the head
sorted_list.append(item)
else:
item = l2.pop(0)
sorted_list.append(item)
# Add the remaining of the lists
sorted_list.extend(l1 if l1 else l2)
return sorted_list
if __== '__main__':
print merge_sorted_lists(l1, l2)
これは、日時オブジェクトで正常に機能するはずです。お役に立てれば。
from datetime import datetime
from itertools import chain
from operator import attrgetter
class DT:
def __init__(self, dt):
self.dt = dt
list1 = [DT(datetime(2008, 12, 5, 2)),
DT(datetime(2009, 1, 1, 13)),
DT(datetime(2009, 1, 3, 5))]
list2 = [DT(datetime(2008, 12, 31, 23)),
DT(datetime(2009, 1, 2, 12)),
DT(datetime(2009, 1, 4, 15))]
list3 = sorted(chain(list1, list2), key=attrgetter('dt'))
for item in list3:
print item.dt
出力:
2008-12-05 02:00:00
2008-12-31 23:00:00
2009-01-01 13:00:00
2009-01-02 12:00:00
2009-01-03 05:00:00
2009-01-04 15:00:00
大規模なデータであっても、これは派手なpure-Pythonマージアルゴリズムよりも高速です。 Python 2.6のheapq.merge
はまったく別の話です。
Pythonのソート実装「timsort」は、順序付けられたセクションを含むリスト用に特に最適化されています。さらに、Cで書かれています。
http://bugs.python.org/file4451/timsort.txt
http://en.wikipedia.org/wiki/Timsort
人々が述べたように、それは一定の要因によって比較関数をより多く呼び出すかもしれません(しかし、多くの場合、より短い期間でより多くの時間を呼び出すかもしれません!)。
ただし、これに依存することはありません。 –ダニエル・ナダシ
Python開発者はティムソートを維持するか、少なくともこの場合はO(n)のソートを維持することを約束します。
一般化されたソート(つまり、基数ソートを制限された値ドメインから分離する)
シリアルマシンではO(n log n)未満では実行できません。 –バリーケリー
右、一般的な場合の並べ替えはそれより速くすることはできません。しかしO()は上限であるため、任意の入力でtimsortがO(n log n)であることは、O(n)ソート済み(L1)+ソート済み(L2)。
両方のリストを反復するMerge Sortのマージステップの実装:
def merge_lists(L1, L2):
"""
L1, L2: sorted lists of numbers, one of them could be empty.
returns a merged and sorted list of L1 and L2.
"""
# When one of them is an empty list, returns the other list
if not L1:
return L2
Elif not L2:
return L1
result = []
i = 0
j = 0
for k in range(len(L1) + len(L2)):
if L1[i] <= L2[j]:
result.append(L1[i])
if i < len(L1) - 1:
i += 1
else:
result += L2[j:] # When the last element in L1 is reached,
break # append the rest of L2 to result.
else:
result.append(L2[j])
if j < len(L2) - 1:
j += 1
else:
result += L1[i:] # When the last element in L2 is reached,
break # append the rest of L1 to result.
return result
L1 = [1, 3, 5]
L2 = [2, 4, 6, 8]
merge_lists(L1, L2) # Should return [1, 2, 3, 4, 5, 6, 8]
merge_lists([], L1) # Should return [1, 3, 5]
私はまだアルゴリズムについて学んでいますが、コードが何らかの面で改善される可能性がある場合、私に知らせてください、あなたのフィードバックは感謝します、ありがとう!
def merge_sort(a,b):
pa = 0
pb = 0
result = []
while pa < len(a) and pb < len(b):
if a[pa] <= b[pb]:
result.append(a[pa])
pa += 1
else:
result.append(b[pb])
pb += 1
remained = a[pa:] + b[pb:]
result.extend(remained)
return result
再帰的な実装は以下です。平均パフォーマンスはO(n)です。
def merge_sorted_lists(A, B, sorted_list = None):
if sorted_list == None:
sorted_list = []
slice_index = 0
for element in A:
if element <= B[0]:
sorted_list.append(element)
slice_index += 1
else:
return merge_sorted_lists(B, A[slice_index:], sorted_list)
return sorted_list + B
またはスペースの複雑さが改善されたジェネレーター:
def merge_sorted_lists_as_generator(A, B):
slice_index = 0
for element in A:
if element <= B[0]:
slice_index += 1
yield element
else:
for sorted_element in merge_sorted_lists_as_generator(B, A[slice_index:]):
yield sorted_element
return
for element in B:
yield element
反復で何が起こっているかを学習することとより一貫した方法でそれをしたい場合は、これを試してください
def merge_arrays(a, b):
l= []
while len(a) > 0 and len(b)>0:
if a[0] < b[0]: l.append(a.pop(0))
else:l.append(b.pop(0))
l.extend(a+b)
print( l )
import random
n=int(input("Enter size of table 1")); #size of list 1
m=int(input("Enter size of table 2")); # size of list 2
tb1=[random.randrange(1,101,1) for _ in range(n)] # filling the list with random
tb2=[random.randrange(1,101,1) for _ in range(m)] # numbers between 1 and 100
tb1.sort(); #sort the list 1
tb2.sort(); # sort the list 2
fus=[]; # creat an empty list
print(tb1); # print the list 1
print('------------------------------------');
print(tb2); # print the list 2
print('------------------------------------');
i=0;j=0; # varialbles to cross the list
while(i<n and j<m):
if(tb1[i]<tb2[j]):
fus.append(tb1[i]);
i+=1;
else:
fus.append(tb2[j]);
j+=1;
if(i<n):
fus+=tb1[i:n];
if(j<m):
fus+=tb2[j:m];
print(fus);
# this code is used to merge two sorted lists in one sorted list (FUS) without
#sorting the (FUS)
マージソートのマージステップを使用しました。しかし、私はgeneratorsを使用しました。 時間の複雑さO(n)
def merge(lst1,lst2):
len1=len(lst1)
len2=len(lst2)
i,j=0,0
while(i<len1 and j<len2):
if(lst1[i]<lst2[j]):
yield lst1[i]
i+=1
else:
yield lst2[j]
j+=1
if(i==len1):
while(j<len2):
yield lst2[j]
j+=1
Elif(j==len2):
while(i<len1):
yield lst1[i]
i+=1
l1=[1,3,5,7]
l2=[2,4,6,8,9]
mergelst=(val for val in merge(l1,l2))
print(*mergelst)
さて、単純なアプローチ(2つのリストを大きなリストに結合して並べ替える)はO(N * log(N))の複雑さです。一方、マージを手動で実装する場合(python libsの準備ができているコードについては知りませんが、エキスパートではありません)、複雑さはO(N)になります) 、これは明らかに高速です。この考えは、Barry Kellyの投稿でよく説明されています。
マージソートの「マージ」ステップを使用します。O(n)時間で実行されます。
wikipedia (擬似コード)から:
function merge(left,right)
var list result
while length(left) > 0 and length(right) > 0
if first(left) ≤ first(right)
append first(left) to result
left = rest(left)
else
append first(right) to result
right = rest(right)
end while
while length(left) > 0
append left to result
while length(right) > 0
append right to result
return result
これは、l1とl2を編集せずに線形時間での私のソリューションです:
def merge(l1, l2):
m, m2 = len(l1), len(l2)
newList = []
l, r = 0, 0
while l < m and r < m2:
if l1[l] < l2[r]:
newList.append(l1[l])
l += 1
else:
newList.append(l2[r])
r += 1
return newList + l1[l:] + l2[r:]
このコードは時間の複雑さO(n)を持ち、パラメーターfunc
として定量化関数を指定すると、任意のデータ型のリストをマージできます。新しいマージリストを生成し、引数として渡されたリストのいずれかを変更します。
def merge_sorted_lists(listA,listB,func):
merged = list()
iA = 0
iB = 0
while True:
hasA = iA < len(listA)
hasB = iB < len(listB)
if not hasA and not hasB:
break
valA = None if not hasA else listA[iA]
valB = None if not hasB else listB[iB]
a = None if not hasA else func(valA)
b = None if not hasB else func(valB)
if (not hasB or a<b) and hasA:
merged.append(valA)
iA += 1
Elif hasB:
merged.append(valB)
iB += 1
return merged
この問題に関する私の見解:
a = [2, 5, 7]
b = [1, 3, 6]
[i for p in Zip(a,b) for i in (p if p[0] <= p[1] else (p[1],p[0]))]
# Output: [1, 2, 3, 5, 6, 7]
def compareDate(obj1, obj2):
if obj1.getDate() < obj2.getDate():
return -1
Elif obj1.getDate() > obj2.getDate():
return 1
else:
return 0
list = list1 + list2
list.sort(compareDate)
リストを所定の場所に並べ替えます。 2つのオブジェクトを比較するための独自の関数を定義し、その関数を組み込みのソート関数に渡します。
バブルソートは使用しないでください。パフォーマンスがひどいです。