答案 0 :(得分:24)
如果您的单词列表很长,从'word'生成所有可能的1个字母差异可能更有效,那么检查列表中的哪些?我不知道任何Python,但应该有一个合适的wordlist数据结构,允许进行日志时间查找。
我建议这样做,因为如果你的单词是合理的长度(~10个字母),那么你只会寻找250个潜在单词,如果你的单词列表大于几百个单词,这可能会更快。
答案 1 :(得分:10)
当你真正关心距离= 1时,你的函数distance
正在计算总距离。大多数情况下,你会知道它在几个字符内是> 1,所以你可以提前返回并节省大量时间。
除此之外,可能有更好的算法,但我无法想到它。
编辑:另一个想法。
根据第一个字符是否匹配,您可以制作2个案例。如果不匹配,则单词的其余部分必须完全匹配,您可以一次性测试。否则,与你正在做的事情类似。你甚至可以递归地做,但我认为这不会更快。
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:我已经删除了检查以查看字符串是否长度相同,因为您说它是多余的。在我自己的代码和is_neighbors函数Ryan's tests上运行provided by MizardX,我得到以下结果:
编辑3:(可能会进入社区维基地区,但是......)
使用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
答案 2 :(得分: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) )
答案 3 :(得分:4)
人们主要通过尝试编写更快的功能来解决这个问题,但可能还有另一种方式......
“距离”被称为超过500万次
这是为什么?也许更好的优化方法是尝试减少调用distance
的次数,而不是缩短distance's
执行时间的毫秒数。没有看到完整的脚本就无法分辨,但通常不需要优化特定的功能。
如果这是不可能的,也许你可以把它写成C模块?
答案 4 :(得分:3)
使用相同的参数调用distance函数的频率是多少?一个简单的实现优化就是使用memoization。
您可能还可以创建某种字典,其中包含字母和单词列表,它们之间存在差异,并在其中查找值。该数据结构既可以通过pickle存储和加载,也可以在启动时从头开始生成。
如果你使用的词很长,那么短路评估只会给你增益,因为你使用的汉明距离算法基本上是O(n),其中n是字长。
我做了一些关于时间的实验,以寻找可能具有说明性的替代方法。
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
答案 5 :(得分:2)
答案 6 :(得分:1)
对于这样一个具有如此大的性能影响的简单函数,我可能会创建一个C库并使用ctypes来调用它。 reddit的创始人之一声称他们使用这种技术使网站的速度提高了2倍。
你也可以在这个功能上使用psyco,但要注意它会占用大量内存。
答案 7 :(得分: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 )
主要的问题是列表理解会在内存中构建自己并占用相当多的空间,而生成器会动态创建列表,因此不需要存储整个事物。
另外,根据未知的答案,这可能是一种更加“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的情况下,它只返回与最短单词一样多的字符。 (这可能会成为一种优化......)
答案 8 :(得分:0)
试试这个:
def distance(word1, word2):
return sum([not c1 == c2 for c1, c2 in zip(word1,word2)])
另外,你有游戏链接吗?我喜欢被文字游戏摧毁
答案 9 :(得分:0)
首先发生在我身上:
from operator import ne
def distance(word1, word2):
return sum(map(ne, word1, word2))
它有比人们发布的其他函数更快的机会,因为它没有解释循环,只是调用Python原语。而且它足够短,你可以合理地将它内联到调用者。
对于更高级别的问题,我将研究在度量空间中为相似性搜索开发的数据结构,例如: this paper或this book,我从未读过这些文章(他们在寻找我读过但不记得的论文时出现过)。
答案 10 :(得分: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])
相同的模式将遵循所提供的代码......
答案 11 :(得分:0)
其他人只关注明确的距离计算,而没有做任何关于构建距离-1候选者的事情。 您可以使用名为 Trie 的众所周知的数据结构来改进,以将隐式距离计算与生成所有距离的任务合并-1邻居单词。 Trie是一个链表,其中每个节点代表一个字母,而“下一个”节点代表一个字母。 field是一个包含多达26个条目的dict,指向下一个节点。
这里是伪代码:迭代地按照你给出的单词走Trie;在每个节点处将所有distance-0和distance-1邻居添加到结果中;保持距离的计数器并减少距离。你不需要递归,只需要一个需要额外distance_so_far整数参数的查找函数。
通过为长度为3,长度为4,长度为5等单词构建单独的Tries,可以获得O(N)空间增加的额外速度的微小权衡。