如何优化这段Python代码以生成所有与给定单词距离为1的单词?

32

分析显示这是我编写的一个小文字游戏中最慢的代码段:

def distance(word1, word2):
    difference = 0
    for i in range(len(word1)):
        if word1[i] != word2[i]:
            difference += 1
    return difference

def getchildren(word, wordlist):
    return [ w for w in wordlist if distance(word, w) == 1 ]

备注:

  • distance() 被调用了超过500万次,其中大部分是从 getchildren 中调用的,它的作用是获取单词列表中与word只差一个字母的所有单词。
  • 单词列表已经预先过滤,只包含与word具有相同字母数的单词,因此可以保证word1word2拥有相同的字符数。
  • 我对Python还比较新(三天前开始学习),所以也欢迎对命名约定或其他代码风格方面的建议。
  • 对于单词列表,请使用“2+2lemma.txt”文件下载12dict单词列表

结果:

谢谢大家的建议,通过不同的建议组合,现在我的程序的运行速度提高了两倍(除了我在提问之前自己进行的优化之外,因此从最初实现以来大概提高了4倍的速度)

我测试了两组输入,分别称为 A 和 B

优化1:遍历 word1、word2 的索引 ...

for i in range(len(word1)):
        if word1[i] != word2[i]:
            difference += 1
    return difference

使用zip(word1, word2)迭代字母对

for x,y in zip (word1, word2):
        if x != y:
            difference += 1
    return difference

优化2: 除了距离方法(我仍然需要用于A*启发式算法),还添加了一个专门的differs-by-one方法。

执行时间从输入A的11.92缩短到9.18,从输入B的79.30缩短到74.59。

def is_neighbors(word1,word2):
    different = False
    for c1,c2 in zip(word1,word2):
        if c1 != c2:
            if different:
                return False
            different = True
    return different

对输入A,执行时间从9.18降至8.83;对输入B,执行时间从74.59降至70.14。

优化3:使用izip替代zip效果显著。

对输入A,执行时间从8.83降至5.02;对输入B,执行时间从70.14降至41.69。

虽然我可能可以用一种更底层的语言编写它,并取得更好的效果,但目前为止我对这个结果感到满意。谢谢大家!

再次编辑:更多结果,使用Mark的方法检查首字母不匹配的情况,将执行时间从5.02降至3.59,将执行时间从41.69降至29.82。

在此基础上,并加入了使用izip替代range,最终代码如下:

def is_neighbors(word1,word2):
    if word1[0] != word2[0]:
        return word1[1:] == word2[1:]
    different = False
    for x,y in izip(word1[1:],word2[1:]):
        if x != y:
            if different:
                return False
            different = True
    return different

将时间从3.59缩短到3.38,将29.82缩短到27.88。

更多结果!

尝试Sumudu的建议,我生成了一个由距离“word”只差1个字母的所有字符串组成的列表,然后检查哪些在单词列表中,而不是使用is_neighbor函数,最终得到了以下代码:

def one_letter_off_strings(word):
    import string
    dif_list = []
    for i in xrange(len(word)):
        dif_list.extend((word[:i] + l + word[i+1:] for l in string.ascii_lowercase if l != word[i]))
    return dif_list

def getchildren(word, wordlist):
    oneoff = one_letter_off_strings(word)
    return ( w for w in oneoff if w in wordlist )

结果最终变慢了(3.38 - > 3.74 和27.88 -> 34.40),但似乎很有前途。起初我以为需要优化的部分是“one_letter_off_strings”,但分析结果表明瓶颈实际上在

( w for w in oneoff if w in wordlist )

我曾想过,如果我交换“oneoff”和“wordlist”,然后以另一种方式进行比较,是否会有任何区别,但是当我开始寻找这两个列表的交集时,我意识到我需要使用字母的交集,于是我做出了替换。

return set(oneoff) & set(wordlist)

Bam!3.74 -> 0.23,34.40 -> 2.25

这真是太神奇了,与我最初的天真实现相比,总速度差异为:23.79 -> 0.23和180.07 -> 2.25,大约比原始实现快80到100倍。

如果有人感兴趣,我写了一篇博客文章描述了这个程序,以及描述了所做的优化,其中包括没有在此处提到的一个(因为它在代码的另一个部分)。

伟大的辩论:

好吧,我和Unknown正在进行一场激烈的辩论,你可以在他的回答的评论中阅读。他声称,如果将其移植到C语言,则使用原始方法(使用is_neighbor而不是使用集合)将更快。我尝试了两个小时编写了一个C模块,并尝试遵循这个这个示例,但并没有取得太大成功,而且在Windows中的过程似乎有点不同?我不知道,但我放弃了。无论如何,这是程序的完整代码,文本文件来自12dict单词表,使用“2+2lemma.txt”文件。对不起,如果代码有点混乱,这只是我随便搞出来的东西。另外,我忘了从单词列表中去除逗号,所以这实际上是一个错误,你可以为了同样的比较而保留它,或者通过将逗号添加到cleanentries字符列表中来修复它。

from itertools import izip
def unique(seq):  
    seen = {} 
    result = [] 
    for item in seq: 
        if item in seen:
            continue 
        seen[item] = 1 
        result.append(item) 
    return result
def cleanentries(li):
    pass
    return unique( [w.strip('[]') for w in li if w != "->"] )
def distance(word1, word2):
    difference = 0
    for x,y in izip (word1, word2):
        if x != y:
            difference += 1
    return difference
def is_neighbors(word1,word2):
    if word1[0] != word2[0]:
        return word1[1:] == word2[1:]
    different = False
    for x,y in izip(word1[1:],word2[1:]):
        if x != y:
            if different:
                return False
            different = True
    return different
def one_letter_off_strings(word):
    import string
    dif_list = []
    for i in xrange(len(word)):
        dif_list.extend((word[:i] + l + word[i+1:] for l in string.ascii_lowercase if l != word[i]))
    return dif_list

def getchildren(word, wordlist):
    oneoff = one_letter_off_strings(word)
    return set(oneoff) & set(wordlist)
def AStar(start, goal, wordlist):
    import Queue
    closedset = []
    openset = [start]
    pqueue = Queue.PriorityQueue(0)
    g_score = {start:0}         #Distance from start along optimal path.
    h_score = {start:distance(start, goal)}
    f_score = {start:h_score[start]}
    pqueue.put((f_score[start], start))
    parent_dict = {}
    while len(openset) > 0:
        x = pqueue.get(False)[1]
        if x == goal:
            return reconstruct_path(parent_dict,goal)
        openset.remove(x)
        closedset.append(x)
        sortedOpen = [(f_score[w], w, g_score[w], h_score[w]) for w in openset]
        sortedOpen.sort()
        for y in getchildren(x, wordlist):
            if y in closedset:
                continue
            temp_g_score = g_score[x] + 1
            temp_is_better = False
            appended = False
            if (not y in openset): 
                openset.append(y)
                appended = True
                h_score[y] = distance(y, goal)
                temp_is_better = True
            elif temp_g_score < g_score[y] :
                temp_is_better = True
            else :
                pass
            if temp_is_better:
                parent_dict[y] = x
                g_score[y] = temp_g_score
                f_score[y] = g_score[y] + h_score[y]
                if appended :
                    pqueue.put((f_score[y], y))
    return None


def reconstruct_path(parent_dict,node):
     if node in parent_dict.keys():
         p = reconstruct_path(parent_dict,parent_dict[node])
         p.append(node)
         return p
     else:
         return []        

wordfile = open("2+2lemma.txt")
wordlist = cleanentries(wordfile.read().split())
wordfile.close()
words = []
while True:
    userentry = raw_input("Hello, enter the 2 words to play with separated by a space:\n ")
    words = [w.lower() for w in userentry.split()]
    if(len(words) == 2 and len(words[0]) == len(words[1])):
        break
print "You selected %s and %s as your words" % (words[0], words[1])
wordlist = [ w for w in wordlist if len(words[0]) == len(w)]
answer = AStar(words[0], words[1], wordlist)
if answer != None:
    print "Minimum number of steps is %s" % (len(answer))
    reply = raw_input("Would you like the answer(y/n)? ")
    if(reply.lower() == "y"):
        answer.insert(0, words[0])
        print "\n".join(answer)
    else:
        print "Good luck!"
else:
    print "Sorry, there's no answer to yours"
reply = raw_input("Press enter to exit")

尽管is_neighbors方法没有被使用,但我还是保留了它。这个方法被建议移植到C语言中。要使用它,只需将getchildren替换为该方法:

def getchildren(word, wordlist):
    return ( w for w in wordlist if is_neighbors(word, w))

至于将其作为C模块运行,我没有深入研究,但这是我得出的结果:

#include "Python.h"

static PyObject *
py_is_neighbor(PyObject *self, Pyobject *args)
{
    int length;
    const char *word1, *word2;
    if (!PyArg_ParseTuple(args, "ss", &word1, &word2, &length))
        return NULL;

    int i;
    int different = 0;
    for (i =0; i < length; i++)
    {
        if (*(word1 + i) != *(word2 + i))
        {
            if (different)
            {
                return Py_BuildValue("i", different);
            }
            different = 1;
        }
    }
    return Py_BuildValue("i", different);
}

PyMethodDef methods[] = {
    {"isneighbor", py_is_neighbor, METH_VARARGS, "Returns whether words are neighbors"},
    {NULL, NULL, 0, NULL}
};

PyMODINIT_FUNC
initIsNeighbor(void)
{
    Py_InitModule("isneighbor", methods);
}

我使用以下命令进行了性能分析:

python -m cProfile "Wordgame.py"

记录的时间是 AStar 方法调用的总时间。快速输入集为 "verse poets",长输入集为 "poets verse"。不同机器的计时结果会有所不同,因此如果有人尝试这样做,请提供程序的结果比较,包括使用 C 模块的情况。


如果word2是word1的前缀,应该如何计算距离? - Markus Jarderot
能否请您再次运行我的最新函数并计时?我非常想知道它在您的实际数据上与之前相比如何。 - Mark Ransom
请看 http://norvig.com/spell-correct.html 上的 edits1(word) 函数。 - jfs
必须使用Python 2.x吗(3.x中不存在izip)?在3.x中更具可重用性;此外,优化和trie库的实现将有所不同。 - smci
12个回答

24
如果您的单词列表很长,那么从“word”生成所有可能的1个不同字母,然后检查哪些在列表中,可能更有效。我不懂Python,但应该有适合单词列表的数据结构,可以进行对数时间查找。建议这样做是因为如果您的单词长度合理(~10个字母),那么您只需要查找250个潜在单词,如果您的单词列表大于几百个单词,这可能更快。

5
哇……我想我得接受这个……你让它的速度提高了1000%……稍微修改一下,将列表转换为集合并执行交集操作而不是检查列表中的元素,但总体想法帮助我实现了目标。很高兴我试了一下,一开始我持怀疑态度,因为我认为生成列表会花费很长时间,担心如何优化那部分。结果创建起来真的很快,而检查每个是否在列表中则需要很长时间,把它改成使用集合后,它就飞快了。 - Davy8
优化一直是首先选择正确算法,然后进行微调的过程。恭喜你有独到的思考方式。 - Mark Ransom
有趣的发现,但我认为这更多是Python特定的优化(即集合比数组访问更快)。考虑在25L(L代表字母)中搜索的成本,而不是匹配L/2(假设您会找到一半单词的2个或更多差异)。在像C这样接近底层的语言中,我相信您会发现集合搜索不太优化,直到log(25)> L/2-log(L)。我绘制了函数图形,并且对于长度大于17个字母的L,这是正确的。http://imgur.com/27AUC.png - Unknown
我在SO上消失了一段时间,所以直到现在才看到这个。正如我在答案中指出的那样,我不懂Python,但你应该使用支持对数时间查找的单词列表,这显然是设置的(就像在C++中)。我不同意unknown的分析,因为他没有考虑单词列表中单词的数量(W)。使用我的方法,最坏情况是25L * log(W),但检查单词列表中的每个单词并没有利用集合结构,并且需要W * L/2(使用他的近似值)。因此,如果单词列表足够长,我的方法会更快(正如我在开头所提到的)。 - Sumudu Fernando
刚刚意识到我的需求的最坏情况需要另一个L因素,这是由于在集合查找期间发生的字符串比较。Davy8提到单词数约为4500个,每个单词的长度约为5个,所以这仍然可以接受。未知:如果我用C++做这件事,我仍然会用std::set按照我的方式去做。 - Sumudu Fernando
请提供您正在使用的字典的URL,以便我们可以构建Trie并进行复制。 - smci

10
你的函数distance计算了总距离,但实际上你只关心距离=1。在大多数情况下,你会在几个字符内知道它是否>1,所以你可以提前返回并节省很多时间。
除此之外,可能有更好的算法,但我想不出来。 编辑: 另一个想法。
你可以分成两种情况,取决于第一个字符是否匹配。如果不匹配,则剩余部分必须完全匹配,你可以一次性测试。否则,按照你之前的做法进行。你甚至可以递归进行,但我认为这不会更快。
def DifferentByOne(word1, word2):
    if word1[0] != word2[0]:
        return word1[1:] == word2[1:]
    same = True
    for i in range(1, len(word1)):
        if word1[i] != word2[i]:
            if same:
                same = False
            else:
                return False
    return not same

编辑 2:我已经删除了检查字符串长度是否相同的代码,因为你说这是多余的。在我的代码和MizardX提供的is_neighbors函数上运行Ryan的测试,我得到以下结果:

  • 原始distance():3.7秒
  • 我的DifferentByOne():1.1秒
  • MizardX的is_neighbors():3.7秒

编辑 3:(可能会进入社区Wiki领域,但是...)

尝试使用izip而不是zip进行最终定义的is_neighbors():2.9秒。

这是我的最新版本,仍然计时为1.1秒:

def DifferentByOne(word1, word2):
    if word1[0] != word2[0]:
        return word1[1:] == word2[1:]
    different = False
    for i in range(1, len(word1)):
        if word1[i] != word2[i]:
            if different:
                return False
            different = True
    return different

如果 word1[0] != word2[0]: return word1[1:] == word2[1:] 这可能会使它变慢,因为对不可变字符串进行切片会在内存中创建一个新副本。 - Unknown
这似乎比原始代码慢,基准测试非常基础。使用timeit模块,distance(“abc”,“abd”)需要4.03秒,DifferenceByOne(“abc”,“abd”)需要5.02秒。 - dbr
哇,我没想到这段代码:if word1[0] != word2[0]: return word1[1:] == word2[1:]会有这么大的影响,但看起来确实如此。干得好,先生。5.02 -> 3.59 和 41.69 -> 29.82。 - Davy8
这是一种经典的优化技术 - 剥离出常见情况并给予更快的处理。平均而言,您预计优化后的情况会发生25/26次。 - Mark Ransom
哦,我看到我把错误的代码粘贴到了我的答案中。实际上,我测试了一个使用izip而不是range的版本。 - Mark Ransom
显示剩余3条评论

6
from itertools import izip

def is_neighbors(word1,word2):
    different = False
    for c1,c2 in izip(word1,word2):
        if c1 != c2:
            if different:
                return False
            different = True
    return different

也许可以将izip代码内联:
def is_neighbors(word1,word2):
    different = False
    next1 = iter(word1).next
    next2 = iter(word2).next
    try:
        while 1:
            if next1() != next2():
                if different:
                    return False
                different = True
    except StopIteration:
        pass
    return different

并且还有一个重写的 getchildren

def iterchildren(word, wordlist):
    return ( w for w in wordlist if is_neighbors(word, w) )
  • izip(a,b) 返回一个迭代器,该迭代器包含从ab中获取的值对。
  • zip(a,b) 返回一个列表,其中包含从ab中获取的值对。

4

人们主要通过尝试编写更快的函数来解决这个问题,但也许还有另一种方法...

"distance"被调用了500万次以上

为什么会这样?也许更好的优化方式是尝试减少对distance的调用次数,而不是削减distance的执行时间。没有看到完整的脚本很难判断,但通常情况下优化特定函数是不必要的。

如果不可能实现上述优化,也许您可以将其编写为C模块?


很好的观点。数据肯定可以以一种方式组织,从而消除许多检查。 - Dan Olson
我考虑过这个问题,但实际上90-95%的调用都是来自于上面展示的getchildren函数,所以除非有一种方法可以让该调用更少地距离,否则我不确定。 - Davy8
哈哈,我猜我最终果然把调用距离的部分全部删除了。 - Davy8

3

距离函数使用相同参数的频率有多高?一个简单实现优化的方法是使用memoization

您可能还可以创建一些字母的frozensets和仅相差一个单词的单词列表的字典,并在其中查找值。这个数据结构可以通过pickle存储和加载,或者在启动时从头生成。

如果你使用的单词非常长,那么短路评估只会给你带来收益,因为你使用的汉明距离算法基本上是O(n),其中n是单词长度。

我用timeit进行了一些实验,探索了一些替代方法,这可能会有所启发。

Timeit结果

您的解决方案

d = """\
def distance(word1, word2):
    difference = 0
    for i in range(len(word1)):
        if word1[i] != word2[i]:
            difference += 1
    return difference
"""
t1 = timeit.Timer('distance("hello", "belko")', d)
print t1.timeit() # prints 6.502113536776391

一行代码

d = """\
from itertools import izip
def hamdist(s1, s2):
    return sum(ch1 != ch2 for ch1, ch2 in izip(s1,s2))
"""
t2 = timeit.Timer('hamdist("hello", "belko")', d)
print t2.timeit() # prints 10.985101179

快速求值

d = """\
def distance_is_one(word1, word2):
    diff = 0
    for i in xrange(len(word1)):
        if word1[i] != word2[i]:
            diff += 1
        if diff > 1:
            return False
    return diff == 1
"""
t3 = timeit.Timer('hamdist("hello", "belko")', d)
print t2.timeit() # prints 6.63337

1
在一个关于优化的问题中,你是唯一一个(到目前为止)提供任何基准测试的人.. :/ - dbr
我可以研究一下记忆化的东西。这比我今晚想尝试的要复杂,也许明天再试吧,但还是给它加上一个+1,因为它似乎可以帮助到我。 - Davy8
1
正确拼写是“memoization”,在“memoize”中没有“r”。 - S.Lott

2

你可以通过当差值为2或更多时终止循环来开始。

另外,你还可以修改

for i in range(len(word1)):

to

for i in xrange(len(word1)):

因为xrange仅在需要时生成序列,而不是一次性生成所有数字的范围。
您还可以尝试比较单词长度,这将更快。还要注意,如果word1大于word2,则您的代码将无法工作。
在那之后,算法上没有太多其他事情可做,也就是说,通过将该部分移植到C中,您可能会发现更快的速度。
尝试解释Sumudu算法与逐个字符验证差异之间的分析。
当您有一个长度为L的单词时,您将生成“仅相差一个”单词的数量将为25L。我们知道从现代计算机上实现的集合中,搜索速度约为log(n)base 2,其中n是要搜索的元素数。
看到你测试的5百万个单词中的大多数都不在集合中,大多数时间,你将遍历整个集合,这意味着它真正变成了log(25L)而不是只有log(25L)/2。(这是假设最佳情况下,将字符串逐个比较等同于逐个比较字符)
现在我们来看一下确定“仅相差一个”的时间复杂度。如果我们假设您必须检查整个单词,则每个单词的操作数变为L。我们知道大多数单词很快就相差2个。并且知道大多数前缀只占单词的一小部分,我们可以逻辑地假设您将在L/2或一半的单词处大多数时间中断(这是一个保守的估计)。
因此,现在我们绘制两个搜索的时间复杂度,L/2和log(25L),并记住即使考虑到字符串匹配与字符匹配的速度相同(高度支持集合)。您有方程式log(25 * L)> L/2,可以简化为log(25)> L/2-log(L)。从图表中可以看出,使用字符匹配算法应该更快,直到达到非常大的L为止。
此外,我不知道您是否在优化中计算了差异2或更多次数,但从Mark的答案中可以看出,我已经在差异2或更多次数上中断,并且实际上,如果第一个字母不同,则在第一个字母之后就会中断,尽管所有这些优化都改用集合来解决问题。我对尝试您的想法很感兴趣。
我是第一个建议在差异为2或更多时中断的人。问题是,Mark的字符串切片想法(如果word1 [0]!= word2 [0]:返回word1 [1:] == word2 [1:])只是将我们正在做的事情放入了C中。你认为word1 [1:] == word2 [1:]是如何计算的?就是我们现在所做的方式。
引用: 我读了你的解释几遍,但并没有完全理解,请问你能再深入解释一下吗?此外,我对C不是很熟悉,过去几年一直在使用高级语言(最接近的是6年前在高中学习C ++) 关于生成C代码,我有点忙。我相信你能够做到,因为你之前写过C。你也可以尝试使用C#,它可能具有类似的性能特征。
更多解释: 这里是给Davy8的更深入的解释。
def getchildren(word, wordlist):
    oneoff = one_letter_off_strings(word)
    return set(oneoff) & set(wordlist)

您的 one_letter_off_strings 函数将创建一个包含 25L 个字符串的集合(其中 L 是字母数)。
从单词列表中创建一个集合将创建一个包含 D 个字符串的集合(其中 D 是您的字典长度)。通过从中创建交集,您必须遍历每个oneoff并查看它是否存在于wordlist中。
此操作的时间复杂度如上所述。这个操作比将您想要的wordwordlist中的每个单词进行比较要低效。Sumudu 的方法是 C 中的优化,而不是算法。 更多解释2 只有4500个单词(因为单词列表在传递给算法之前已经被预过滤为5个字母的单词),与125个错位一个字母的单词相交。似乎你说的是交集是log(smaller),或者换句话说是log(125,2)。与再次假设你所说的相比,在L/2个字母中比较一个单词,我会将其四舍五入为2,即使对于一个5个字母的单词来说,它更可能是3。这个比较做了4500次,所以是9000。log(125,2)约为6.9,而log(4500,2)约为12。如果我误解了你的数字,请告诉我。
要创建一个包含125个错位一个字母的单词与4500个字典的交集,您需要进行125 * 4500次比较。这不是log(125,2)。最好情况下是 125 * log(4500, 2),假设字典已经排序。没有集合的魔法快捷方式。 您在此处也正在进行字符串逐个比较,而不是字符逐个比较。

我想你说得有道理:你可以这样做: for i,c in enumerate(word1): c != word2[i]这看起来可能有点笨重,但我建议你使用"for i in xrange(len(word1))",因为xrange会按需生成数字,而不是占用内存的列表。 - Unknown
我读了你的解释几遍,但是我还是没太明白,你能不能再详细地解释一下呢?另外,我对C语言不是很熟悉,过去几年一直在使用高级语言(最接近的是6年前在高中学习C++),所以如果你能提供一个代码示例让我试一下并告诉你结果就好了。话虽如此,我不知道Python中集合的实现方式,但它们似乎被超级优化了,如果一个不太复杂的C模块能做得更好,我会感到惊讶的。但是必须进行性能分析。 - Davy8
@Davy8 我现在得走了,但我保证你用C实现我的方法会比使用集合更快。 - Unknown
所有5个字母单词中的5个字符,仍然可以得到7585。它应该比逐个字符更好,因为列表是排序过的,所以甚至应该更快。如果我漏掉了什么,请告诉我。 - Davy8
@Unknown 我正在计算字符比较次数,根据定义,你无法在计算2个字符之前找到2个字符的差异。7585是125log(4500,2) 5(char/word)的数字,这是一个高估(再次使用你的数字),与需要比较至少2个字符的4500个单词相比,其产生9000个字符比较。尽管如此,在我刚刚测试了马克给出的链接后,似乎搜索字符串集对于O(1)是成立的。搜索10000次集合的第一个元素和大小为9000的列表每次耗时20毫秒。搜索第4500个元素耗时18毫秒,而不是1.8秒。 - Davy8
显示剩余18条评论

1
对于这样一个简单的函数,它具有如此大的性能影响,我可能会制作一个C库,并使用ctypes调用它。 Reddit的创始人之一声称他们使用这种技术使网站快了2倍。
您还可以在此函数上使用psyco,但请注意它可能会占用大量内存。

0

对于这段片段:

for x,y in zip (word1, word2):
    if x != y:
        difference += 1
return difference

我会用这个:
return sum(1 for i in xrange(len(word1)) if word1[i] == word2[i])

相同的模式将在提供的代码周围重复出现...


0

我不知道这是否会显著影响您的速度,但您可以尝试将列表推导式转换为生成器表达式。它仍然是可迭代的,因此在使用上应该没有太大区别:

def getchildren(word, wordlist):
    return [ w for w in wordlist if distance(word, w) == 1 ]

def getchildren(word, wordlist):
    return ( w for w in wordlist if distance(word, w) == 1 )

主要问题在于列表推导式会在内存中构建自身并占用相当多的空间,而生成器会动态创建列表,因此无需存储整个列表。

此外,继续unknown的回答,这可能是编写distance()函数更符合"Pythonic"风格的方式:

def distance(word1, word2):
    difference = 0
    for x,y in zip (word1, word2):
        if x == y:
            difference += 1
    return difference

但是当len(word1) != len(word2)时,zip函数的意图很容易让人感到困惑。在这种情况下,它只会返回最短单词中的字符数。(这可能会成为一种优化...)


0

试试这个:

def distance(word1, word2):
  return sum([not c1 == c2 for c1, c2 in zip(word1,word2)])

另外,你有游戏链接吗?我喜欢被文字游戏摧毁


还没有,但我可以在某个地方发布它。代码可能全部放在一页纸上,所以它相当简单。 - Davy8
看起来这实际上略微慢一些,但它更短。 - Davy8
哈哈 - 我真是帮了大忙了。再试一次,用 izip 代替 zip,使用元组语法而不是列表,希望会有所改善。 - user44484

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接