Python 需要帮助加快排列速度吗

Python 需要帮助加快排列速度吗,python,permutation,Python,Permutation,这是我的工作代码,我正在努力寻找方法,让它更快地找到有效的单词,我在考虑可能为每个单词制作单独的词典列表,yall怎么想 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 op

这是我的工作代码,我正在努力寻找方法,让它更快地找到有效的单词,我在考虑可能为每个单词制作单独的词典列表,yall怎么想

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()

将您的
列表1
更改为一组:

set1 = set(list1)

如果您经常进行测试并且列表很长,那么set1中的str1测试将比列表1中的str1测试快得多。

首先,分析代码。这会告诉你慢的部分在哪里

第二,你可以考虑把单词列表转换成一个集合,它应该有一个更快的“in”运算符来检查单词是否存在。

第三,考虑简化代码以删除不必要的语句,如

def is_valid(str1,list1):
   return str1 in list1

你到底想用这个做什么?看起来你已经有了一些有效单词的字典。为什么您要排列所有可以根据用户输入构建的单词组合

<>你需要考虑一下你的算法。您创建的每一个排列都在迭代字典中的每一个已知单词(列表1)。当你创建所有的单词排列时,你正在创建m!其中m是用户给出的字母数

你基本上有O(n*m!)。这对于像7这样的小数目的东西来说都是荒谬的巨大。通过使用一个集合,而不是一个列表,你可以把这个n项减少到一个常数,这会把你的算法变成O(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有点生疏,但希望您能理解。

尝试将内部循环替换为:

for x in itertools.permutations(letters,length):
    word = ''.join(x)
    if word.upper() in words:
        answers.append(word)
        print(word)

如果你过于热衷于提高速度,而以降低可读性为代价,你可以尝试以下方法

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
。因此,将两个循环组合为一个理解。除此之外,声明

       word=''
        for let in x:
            word+=let
        if is_valid(word.upper(),words):
如果
有效(''.join(x).upper,words)
或甚至
'.join(x).upper在words
中,记住函数调用是昂贵的

我已经做了一个速度和外观的比较,它的运行速度快了50%

现在由你来决定



问题是你的算法基本上是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,即输入的字母数


实现是留给读者的。

如果您计划经常查找单词,您应该根据您的数据构建一个

下面是一个简单的例子。代码应该是非常自解释的,但是请询问是否有不清楚的地方

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)
函数调用即可确定输入的单词是否有效。这是一个巨大的改进,如果单词列表中的单词,需要
O(len(wordlist))

这种方法的缺点是,生成树可能需要一些时间。通过使用序列化
Tree()
对象,您不必每次启动脚本时都构建树

我试图用一个单词列表(总共109582个单词)构建一棵树

通过对其计时,当取消勾选对象文件而不是从头构建dict时,执行时间减少了约50%


如果您只想检查排列,您应该首先更改
Tree()
add\u word()
方法对字母进行排序。
Tree.is\u word()
的输入参数当然也应该排序。

谢谢你的评论,我觉得速度不快,如果你用输入的7个以上的字母运行程序,你会发现它的速度相当慢。作为一个集合,它比列表快几个数量级。@BrandonRutledge:不要依赖“感觉”来优化程序。使用类似“timeit”模块的方法来测试这些假设。()慢的部分是当有效单词为5个字符或更大时,我相信我根据eumiro的评论将其更改为一组,但我感觉速度没有变化,而且我不理解你的第三个陈述。第三个建议只是清理你的代码。列表1中的str1返回布尔值True或False,因此不需要If。。。返回True,否则返回False,因为If中的条件计算为True,所以只需返回它。消除它不会使您的算法更快,但它有助于减少代码大小并提高可读性。它实际上可能会改进executi
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)