这是我的工作代码,我正在设法让它更快地找到有效的单词,我在考虑为每个单词制作单独的字典列表,你会怎么想?
import random
import itertools
file_name='words.txt'
def load_words():
try:
f=open(file_name,'r')
str1=f.read()
f.close()
except:
print('Problem opening the file',file_name)
list1=[]
list1=str1.split()
return(list1)
def is_valid(str1,list1):
valid=False
if str1 in list1:
valid=True
return valid
def generate(words,letters):
answers=[]
for length in range(2,len(letters)+1):
for x in itertools.permutations(letters,length):
word=''
for let in x:
word+=let
if is_valid(word.upper(),words):
answers.append(word)
print(word)
print(answers)
def main():
words=load_words()
letters = input('Enter your letters')
answers = generate(words,letters)
main()
答案 0 :(得分:5)
将list1
更改为套装:
set1 = set(list1)
如果您经常进行测试并且列表很长,那么测试str1 in set1
将比str1 in list1
多快。
答案 1 :(得分:5)
首先,对代码进行概要分析。这将告诉你缓慢的部分在哪里。
其次,您可以考虑将单词列表转换为一个集合,该集合应该具有更快的' in'运算符用于检查单词是否存在。
第三,考虑简化代码以删除不必要的陈述,例如
def is_valid(str1,list1):
return str1 in list1
答案 2 :(得分:1)
你到底想要完成什么?看起来你已经有了一些你正在读的有效单词的字典。为什么要排列可以根据用户提供的输入构建的所有单词组合?
您需要考虑的算法。您创建的每个排列都会迭代字典中的每个已知单词(list1)。当您创建正在创建m的单词的所有排列时!用户给出的m个字母的单词。
你基本上有O(n * m!)。对于像7这样的少数事情来说,这是非常巨大的。通过使用一个集合,而不是列表,你可以取n个术语并将其减少到一个常数,将你的算法改为O(m!),这仍然太大了。 如果我不得不猜测这个算法在做什么,我会说你想知道你可以用给出的字母创建多少已知单词。你没有这么说,但我们假设这就是你的意思。
更快的算法是迭代字典中的每个单词,看看你是否可以通过从输入中选取字母来制作单词。所以你只能在O(n * m)上走过字典。这消除了置换输入的需要。这是算法:
user_input = input("Give me some words")
for word in list1:
current = user_input
found = True
for letter in word:
if letter in current:
current.remove( letter )
else
found = False
break;
if found:
answers.add( word )
print( answers )
抱歉,我的python有点生疏,但希望你能理解。
答案 3 :(得分:1)
尝试用:
替换内部循环for x in itertools.permutations(letters,length):
word = ''.join(x)
if word.upper() in words:
answers.append(word)
print(word)
答案 4 :(得分:1)
如果您过于热衷于提高速度而不考虑其可读性,则可以尝试以下
def is_valid(str1,list1):
return str1 in list1
words=["BAD","CAB","BEC"]
def generate2(words,letters):
answers=[]
[[answers.append(''.join(x).upper()) for x in itertools.permutations(letters,length) if ''.join(x).upper() in words] for length in range(2,len(letters)+1)]
#print(answers)
return answers
List comprehension is faster than loops。因此将两个循环组合成一个单一的理解。除了声明
word=''
for let in x:
word+=let
if is_valid(word.upper(),words):
可以组合到is_valid(''.join(x).upper,words)
甚至''.join(x).upper in words
,请记住函数调用很昂贵。
我已经在速度和外观上进行了比较,列表理解速度提高了50%。
现在由你来决定
>>> stmt1="""
def is_valid(str1,list1):
valid=False
if str1 in list1:
valid=True
return valid
words=["BAD","CAB","BEC"]
def generate1(words,letters):
answers=[]
for length in range(2,len(letters)+1):
for x in itertools.permutations(letters,length):
word=''
for let in x:
word+=let
if is_valid(word.upper(),words):
answers.append(word)
#print(word)
#print(answers)
return answers
generate1(words,['A','B','C','D','E'])
"""
>>>
>>> stmt2="""
def is_valid(str1,list1):
return str1 in list1
words=["BAD","CAB","BEC"]
def generate2(words,letters):
answers=[]
[[answers.append(''.join(x).upper()) for x in itertools.permutations(letters,length) if ''.join(x).upper() in words] for length in range(2,len(letters)+1)]
#print(answers)
return answers
generate2(words,['A','B','C','D','E'])
"""
>>>
>>> t1=timeit.Timer(stmt=stmt1)
>>> t2=timeit.Timer(stmt=stmt2)
>>> t1.repeat(number=1000)
>>> t2=timeit.Timer(stmt=stmt2)
>>> t1.repeat(number=1000)
[0.47923321640178074, 0.4353549521401874, 0.4362746333173959]
>>> t2.repeat(number=1000)
[0.2536238984591819, 0.2470974750062851, 0.24726312027155473]
答案 5 :(得分:1)
问题在于你的算法基本上是O(n * m!),其中n是单词列表大小,m是字母数。将单词列表更改为集合应该使搜索记录时间和将性能提高到O(log(n)* m!),这是其他人在此建议的。
然而,真正的性能提升来自完全消除排列搜索字母的情况。首先按字母顺序对单词列表中每个单词中的字母进行排序;它应该是O(n * p log(p))时间,其中p是平均字长。然后在O(n * log(n))时间内按字母顺序对整个列表进行排序。同时跟踪原始单词,以便您可以从排序单词列表中的字符串转到O(1)中的原始单词。接下来按字母顺序对插补字母进行排序,并在排序后的单词列表中搜索它们。
上述算法中最慢的操作是对按字母顺序排序的字符串列表进行排序,即O(n Log(n))。搜索这样的列表是Log(n)并导致整个算法在O(n Log(n))时间内执行。它应该线性缩放到m,即输入的字母数。
实施由读者完成。
答案 6 :(得分:0)
如果您打算经常查找单词,则应从数据中构建tree。
简单示例如下。代码应该是不言自明的,但请问是否有不明确的事情。
import pickle
class Tree:
def __init__(self):
self.letters = dict()
def add_words(self, words):
for word in words:
self.add_word(word)
def add_word(self, word):
chars = list(word.lower())
l = chars.pop(0)
try:
self.letters[l].add_word(chars)
except KeyError:
self.letters[l] = Letter(l)
self.letters[l].add_word(chars)
def is_word(self, word):
chars = list(word.lower())
l = chars.pop(0)
try:
return self.letters[l].is_word(chars)
except KeyError:
return False
class Letter:
def __init__(self, letter):
self.letter = letter
self.sub_letters = dict()
self.is_a_word = False
def add_word(self, word):
if len(word) == 0:
self.is_a_word = True
return
l = word.pop(0)
try:
self.sub_letters[l].add_word(word)
except KeyError:
self.sub_letters[l] = Letter(l)
self.sub_letters[l].add_word(word)
def is_word(self, word):
if len(word) == 0:
return self.is_a_word
l = word.pop(0)
try:
return self.sub_letters[l].is_word(word)
except KeyError:
return False
def get_dict(obj_file, dict_file):
try:
with open(obj_file, 'rb') as my_dict:
return pickle.load(my_dict)
except IOError:
my_tree = Tree()
with open(dict_file, 'rb') as in_file:
for word in in_file:
my_tree.add_word(word.strip())
with open(obj_file, 'wb') as outfile:
pickle.dump(my_tree, outfile, pickle.HIGHEST_PROTOCOL)
return my_tree
obj_file = 'mydict.pk'
dict_file = 'wordlist.txt'
my_tree = get_dict(obj_file, dict_file)
(有很多不同类型的树,这只是一个非常简单的例子)
构建树时,只需要len(word)
个函数调用来确定输入的单词是否有效。这是if word in wordlist
的巨大改进,需要O(len(wordlist))
。
这种方法的缺点是生成树可能需要一些时间。通过使用pickle序列化Tree()
对象,您不必在每次启动脚本时都构建树。
我尝试使用SIL International中的单词列表(总共109582个单词)构建一个树。
使用timeit对其进行定时,取消对象文件的执行时间减少了约50%,而不是从头开始构建dict。
如果您只想检查排列,则应更改add_word()
Tree()
方法,以便首先对字母进行排序。当然也应该对Tree.is_word()
的输入参数进行排序。