[英]Find the indicies of common tuples in two list of tuples without loop in Python
[英]Find all common N-sized tuples in list of tuples
我必須創建一個執行以下操作的應用程序(我必須只解析一次數據並將它們存儲在數據庫中):
我得到K元組(K超過1000000),每個元組都是
(UUID, (tuple of N integers))
讓我們假設每個k元組的N等於20,並且每個20個大小的元組都被排序。 我已將以下兩種形式(2個不同的表)中的所有數據保存在數據庫中,以便我可以更輕松地處理它們:
目標是從元組列表中找到所有10個大小的元組,這樣每個元組都存在於多個20個大小的元組中。**
例如,如果給出以下兩個20個大小的元組:
(1, (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,161,17,18,19,20))
(2, (1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39))
常見的元組是:(1,3,5,7,9,11,13,15,17,19)
這是一個10大小的元組,結果如下:
(1, 2, (1,3,5,7,9,11,13,15,17,19))
為了實現這一點,我目前正在做的是(在Python 3中):
但這個程序很慢 。 即使使用多處理來充分利用我的8個CPU內核,每個計算不同的數字,也需要永遠。 我的程序現在運行了2天,只有20%。
您對如何優化流程有任何想法嗎? NumPy數組會幫助執行速度嗎? 在SQL中有什么方法可以計算我想要的每一行,甚至一次一行?
提前致謝。
看起來您可以將元組放入矩陣的行中,並將行號從行號轉換為UUID。 然后將所有元組存儲在一個numpy數組中是可行的,因為元組的元素很小。 numpy具有能夠計算這種數組的行之間的交叉點的代碼。 此代碼生成組合以首先作為元組進行處理,然后進行比較。
from itertools import combinations
import numpy as np
from time import time
minInt=1
maxInt=100
tupleSize=20
intersectionSize=10
K=100
rows=np.zeros((K,tupleSize),dtype=np.int8)
print ('rows uses', rows.nbytes, 'bytes')
for i,c in enumerate(combinations(range(minInt,maxInt),tupleSize)):
if i>=K:
break
for j,_ in enumerate(c):
rows[i,j]=_
t_begin=time()
for i in range(K-1):
for j in range(i+1,K):
intrsect=np.intersect1d(rows[i],rows[j],True)
if intrsect.shape[0]==intersectionSize:
print (i,j,intrsect)
t_finish=time()
print ('K=', K, t_finish-t_begin, 'seconds')
以下是我家中舊的雙核P4 clunker的一些樣本測量結果。
行使用200字節K = 10 0.0009770393371582031秒
行使用1000個字節K = 50 0.0410161018371582秒
行使用2000字節K = 100 0.15625秒
行使用10000字節K = 500 3.610351085662842秒
行使用20000字節K = 1000 14.931640863418579秒
行使用100000字節K = 5000 379.5498049259186秒
如果您在計算機上運行代碼,則可以進行推斷。 我不知道它是否會使你的計算成為可能。
也許我會得到一堆反對票!
比爾,我認為這會產生更隨意的組合。 您的組合版本系統地通過選擇步驟。 對於小K,交叉點大小接近tupleSize
。
choices = np.arange(minInt, maxInt)
for i in range(K):
rows[i,:] = np.random.choice(choices, tupleSize, replace=False)
使用sets
比np.intersect1d
快約4倍。
sets = [set(row) for row in rows]
dd = collections.defaultdict(int)
for i in range(K-1):
for j in range(i+1,K):
intrsect=sets[i].intersection(sets[j])
dd[len(intrsect)] += 1
我切換到收集交叉點大小,因為它更有趣,對迭代策略不太敏感。
K = 5000時:
K= 5000 221.06068444252014 seconds
{0: 77209, 1: 514568, 2: 1524564, 3: 2653485, 4: 3044429, 5: 2436717, 6: 1408293, 7: 596370, 8: 188707, 9: 44262, 10: 7783, 11: 1012, 12: 93, 13: 8}
較短的時間僅適用於sets
創建步驟; 那很快。
K= 5000 0.058181047439575195 46.79403018951416 seconds
{0: 77209, 1: 514568, 2: 1524564, 3: 2653485, 4: 3044429, 5: 2436717, 6: 1408293, 7: 596370, 8: 188707, 9: 44262, 10: 7783, 11: 1012, 12: 93, 13: 8}
對於更大的K.
K= 10000 818.3419544696808 seconds
{0: 309241, 1: 2058883, 2: 6096016, 3: 10625523, 4: 12184030, 5: 9749827, 6: 5620209, 7: 2386389, 8: 752233, 9: 176918, 10: 31168, 11: 4136, 12: 407, 13: 18, 14: 2}
K= 10000 0.09764814376831055 151.11484718322754 seconds
似乎沒有太多的希望:忘記combinations()
部分,你需要檢查每對K
元組的交集。 有choose(K, 2) = K*(K-1)/2
對,所以有一百萬元組有近5000億對。
您可以玩的一個低級技巧是將元組表示為整數而不是集合,其中當且僅當i
在元組中時,位2**i
在整數中設置。 既然你在評論中說元組不包含重復項,並且每個元組元素都在range(1, 100)
,那么100位整數就足夠了(它可以切成99位,但不值得麻煩)。
重點是整數的按位“和”比設置的交叉點快得多。 在我的盒子上,大約快7倍。 這里有一些代碼來說明這個概念,以及一些草率的計時結果(在同時執行大量其他工作的機器上運行):
def build(K, values, N):
from random import sample
sets = []
ints = []
for i in range(K):
t = sample(values, N)
sets.append(set(t))
ints.append(sum(1 << i for i in t))
return sets, ints
def run(K, values, N):
from time import time
as_sets, as_ints = build(K, values, N)
for meth, collection in [("sets", as_sets),
("ints", as_ints)]:
s = time()
for i in range(K-1):
base = collection[i]
for j in range(i+1, K):
x = base & collection[j]
t = time()
print("K", K, meth, t-s)
for K in 5000, 10000, 20000:
run(K, range(1, 100), 20)
並輸出:
K 5000 sets 7.322501182556152
K 5000 ints 1.0357279777526855
K 10000 sets 30.60071086883545
K 10000 ints 4.150524377822876
K 20000 sets 128.24610686302185
K 20000 ints 15.933331727981567
請注意,正如預期的那樣,任何一種方法的運行時間都是K的二次方(因此,加倍K需要大約4倍;而將K大10倍會使運行時間增加約10 ** 2 = 100)。
雖然使用整數的交點要快得多,但確定結果的基數較慢。 有很多方法可以做到這一點,但“最好的”取決於預期的分布和你對編碼疼痛的容忍度;-)這里是主要方法的概述。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.