У меня длинный список сгенерированных имен и файл из 5000 слов, содержащий допустимые имена. Я хочу найти имена в моем списке, которые также появляются в файле. Как мне это сделать?

Я попытался использовать цикл, но это занимает слишком много времени для того, что мне нужно, потому что мой файл имен слишком длинный для поиска по всему файлу для каждого сгенерированного имени. Когда n имеет длину 12 цифр, в моем сгенерированном списке 531441 имя.

Вот некоторый код:

from time import process_time
from itertools import product
start = process_time()
n = "5747867437"
phone = {2: ["A", "B", "C"], 3: ["D", "E", "F"], 4: {"G", "H", "I"}, 5: ["J", "K", "L"], 6: ["M", "N", "O"], 7: ["P", "R", "S"], 8: ["T", "U", "V"], 9: ["W", "X", "Y"]}
li = set(open("dict.txt", "r").read().strip().split("\n"))
num = []
names = []
for x in n:
    num.append(phone[int(x)])
for y in product(*num):
    names.append(''.join(y))
available = []
ad = False
for z in names:
    if z in li:
        available.append(z)
acceptable.sort()
print(acceptable)
if acceptable:
    for a in acceptable:
        print(a + "\n")
else:
    print("NONE\n")
print(process_time() - start)

Файл "accept_names.txt" - это файл с допустимыми именами в нем. Прямо сейчас это занимает 3 секунды. Есть ли способ сделать это быстрее?

Заранее спасибо!

-1
Hiwafreak 9 Июл 2019 в 19:40

3 ответа

Лучший ответ

Как предложено выше, используйте пересечение между множествами. Что-то вроде:

set_names = set(names)
set_li = set(li)
acceptable = set_names.intersection(set_li)

# if you want to sort it, convert it into a list first
print(list(acceptable).sort()
1
dallonsi 9 Июл 2019 в 16:56

Использовать наборы https://docs.python.org/ 3 / библиотека / stdtypes.html # набор - типа посаженные - frozenset

Найти в списке O (n) найти в наборе O (1)

# converting list to set
names = set(names)
for z in li:
    if z in names:
        acceptable.append(z)
acceptable.sort()
print(acceptable)
1
frankegoesdown 9 Июл 2019 в 16:56

Как и предполагалось - используйте наборы. Обрежьте то, что не нужно из вашего кода. MRE того, как может выглядеть ваш код:

from itertools import product

def writeAcceptFile(filename):
    with open(filename,"w") as f:
        f.write("JIM\nJON\nTIM\nIKE")

def getNamesFromFile(filename):
    with open(filename) as f:
        return set(name.strip() for name in f.readlines())


fn = "acceptable_names.txt" 
writeAcceptFile(fn)
accept = getNamesFromFile(fn)

phone = {2: ["A", "B", "C"], 3: ["D", "E", "F"], 4: {"G", "H", "I"}, 
         5: ["J", "K", "L"], 6: ["M", "N", "O"], 7: ["P", "R", "S"], 
         8: ["T", "U", "V"], 9: ["W", "X", "Y"]}

n = 566

ok = [k for k in ( ''.join(l) 
                  for l in product(*(phone[int(x)] 
                                     for x in str(n)))) 
      if k in accept]

print(ok) # ['JON']

Вместо «глупого» oneliner вы можете сделать это, используя списки и циклы:

# or by foot:
names = []
num = []
for x in str(n):
    num.append(phone[int(x)])
for y in product(*num):
    n = ''.join(y)
    # only add name if in accepted list
    if n in accept:
        names.append(''.join(y))

print(names)  # ['JON']

Причиной использования наборов является то, что они очень быстрые (то есть постоянное время, невзирая на то, сколько в них вещей) для содержит проверок.

Ваш код зацикливается для каждого из ваших сгенерированных слов (531441) по всему списку (5k) разрешенных слов - что делает его медленным.

1
Patrick Artner 9 Июл 2019 в 17:13