rozwiązanie Colin Pitrat jest bardzo dobra, ale chyba można go do góry podczas korzystania sortedcontainers.SortedSet
Mam wygenerowany plik z 1.000.000 liczb losowych (używając numpy.random.randint) i sprawdził, czy jest co najmniej jeden numer (w środku pliku), który spełnia twój warunek.
Porównajmy oba podejścia:
import filecmp
from sortedcontainers import SortedSet
from timeit import Timer
def load2list():
with open('data', 'r') as f:
return [int(line) for line in f]
def save_result(i, fn):
with open(fn, 'a') as f:
print(i, file=f)
def test_Colin_Pitrat():
j = 0
for i in range(0, len(l)):
while l[j] < 2*l[i]:
j += 1
if j == len(l):
return -1
if l[j] == 2*l[i]:
save_result(l[i], 'Colin_Pitrat.out')
return l[i]
def test_SortedSet():
for i in sorted_set:
if i<<1 in sorted_set:
save_result(i, 'SortedSet.out')
return i
return -1
if __name__=='__main__':
timeit_number = 10000
l = load2list()
sorted_set = SortedSet(l)
print('len(l):\t\t%d' % (len(l)))
print('len(sorted_set):\t%d' % (len(sorted_set)))
print('Timeit results with %d executions:' %timeit_number)
print('Colin_Pitrat:\t', Timer(test_Colin_Pitrat).timeit(timeit_number))
print('SortedSet:\t', Timer(test_SortedSet).timeit(timeit_number))
print("filecmp.cmp('Colin_Pitrat.out', 'SortedSet.out'):\t%s" % (filecmp.cmp('Colin_Pitrat.out', 'SortedSet.out')))
wyjściowa:
len(l): 1000001
len(sorted_set): 999504
Timeit results with 10000 executions:
Colin_Pitrat: 35.94529931032006
SortedSet: 2.548847197918647
filecmp.cmp('Colin_Pitrat.out', 'SortedSet.out'): True
PS jak widać SortedSet jest bardzo szybki.
UPDATE: (teraz jestem testowania w domu, gdzie mój PC jest znacznie wolniej, więc zmniejszy liczbę egzekucji do 1.000)
Zgodnie z propozycją Colin Pitrat jestem generowania teraz dane (około 100 000 numerów) dla najgorszego scenariusza - gdy nie można znaleźć żadnego dopasowania. Teraz porównuję trzy funkcje: test_Colin_Pitrat, test_Colin_Pitrat2 (dostrojona wersja), test_SortedSet ...Skrypt generator
danych:
import numpy as np
l = np.random.randint(10**7, 10**9, 200000)
l = l[ l % 2 > 0 ]
np.savetxt('data', np.sort(l), fmt='%d')
Kod:
import filecmp
from sortedcontainers import SortedSet
from timeit import Timer
def load2list():
with open('data', 'r') as f:
return [int(line) for line in f]
def save_result(i, fn):
with open(fn, 'a') as f:
print(i, file=f)
def test_Colin_Pitrat():
j = 0
for i in range(0, len(l)):
while l[j] < 2*l[i]:
j += 1
if j == len(l):
return -1
if l[j] == 2*l[i]:
return l[i]
def test_Colin_Pitrat2():
j = 0
s = len(l)
for i in range(0, s):
l_i = l[i]
l_i2 = l_i<<1
while l[j] < l_i2:
j += 1
if j == s:
return -1
if l[j] == l_i2:
return l[j]
def test_SortedSet():
for i in sorted_set:
if i<<1 in sorted_set:
return i
return -1
if __name__=='__main__':
timeit_number = 1000
l = load2list()
sorted_set = SortedSet(l)
print('len(l):\t\t%d' % (len(l)))
print('len(sorted_set):\t%d' % (len(sorted_set)))
print('Timeit results for %d executions:' %timeit_number)
print('Colin_Pitrat:\t', Timer(test_Colin_Pitrat).timeit(timeit_number))
print('Colin_Pitrat2:\t', Timer(test_Colin_Pitrat2).timeit(timeit_number))
print('SortedSet:\t', Timer(test_SortedSet).timeit(timeit_number))
wyjściowa:
len(l): 99909
len(sorted_set): 99899
Timeit results for 1000 executions:
Colin_Pitrat: 153.04753258882357
Colin_Pitrat2: 103.68264272815443
SortedSet: 99.59669211136577
Wniosek: Colin_Pitrat2 wynosi 33% szybciej w porównaniu do Colin_Pitrat i prawie tak szybko jak SortedSet.
algorytm Ty wynosi O (N * logN) złożoność. Powinno być dość szybko z elementami 100k. Jaki jest dokładny czas, który uważasz za wolny i jaki jest odpowiedni czas dla ciebie? Zakładam, że problem polega na tym, że używasz konstrukcji 'number in list', która nie korzysta z faktu, że lista jest posortowana (i dlatego ma złożoność O (N^2). Zamiast tego użyj wyszukiwania binarnego –
To jest wyraźnie do zrobienia w O (N). –
@ColinPitrat Trudno jest udowodnić, że jest * stabilny * O (N) we wszystkich przypadkach, ale wydaje mi się rozsądny i prawdopodobnie szybszy, niż O (N * logN) tak czy inaczej –