注意:本答案中所有函数的返回值都是23248,因为我喜欢从0开始计数。如果您更喜欢从1开始计数,并且想要得到23249作为答案,则只需在函数中添加+1即可。
第一种方法:编写自己的increment_word函数:
您可以编写一个计算下一个单词的函数来迭代单词。例如,bikd之后的下一个单词应该是bike,cdzz之后的下一个单词应该是ceaa。
由于Python中的字符串是不可变的,因此为了方便起见,我们将使用字符列表而不是字符串。
def increment_word(w):
i = len(w) - 1
while (w[i] == 'z'):
w[i] = 'a'
i = i - 1
w[i] = chr(ord(w[i]) + 1)
请注意,只有在调用至少包含一个非“z”字母的单词时,才保证该函数能正常工作。用户需要自行避免在
'zzz'
后请求下一个单词。
现在我们可以解决你的问题:
def find_word(w):
candidate = ['a' for _ in w]
w = list(w)
count_attempts = 0
while candidate != w:
increment_word(candidate)
count_attempts += 1
return count_attempts
第二种方法:使用 itertools.product
通常情况下,在Python中需要迭代复杂的数据结构时,已经有人编写了你需要的循环结构,并且它在 itertools
包中。如果没有,那么可能在 itertools
recipes 或者 more_itertools
中。
在这种情况下,你可以使用 itertools.product
:
from string import ascii_lowercase
from itertools import product
def find_word(w):
for count_attempts, candidate in enumerate(product(*[ascii_lowercase]*len(w))):
if all(x == y for x,y in zip(w, candidate)):
return count_attempts
请注意,我们使用的是
string.ascii_lowercase
而不是自己打出整个字母表。有人很好心地教 Python 字母表。我们没有必要过度热情,重新编写字母表(有遗漏导致一切都崩溃的风险)。
第三个想法:使用递归而不是迭代
任何复杂的循环都可以使用递归来模拟。请注意,Python 对于递归来说是一种相当糟糕的语言——特别是,在 Python 中,递归函数 tend to be pretty slow,而且如果递归太深,程序可能会崩溃,因为 Python 不会优化尾调用。但是,如果您需要使用其他语言,您应该考虑这个选项。
def find_word(word):
return find_word_aux(word, 'a'*len(word), 0)
def find_word_aux(word, candidate, count_attempts):
if candidate == word:
return count_attempts
else:
i,c = max((i,c) for i,c in enumerate(candidate) if c != 'z')
return find_word_aux(word, candidate[:i] + chr(ord(c)+1) + 'a'*(len(word)-i-1), count_attempts + 1)
请注意,这最终与
increment_word
版本非常相似。不幸的是,在我的机器上使用默认python参数,它仅适用于
aaa
和
bmg
之间的单词。对于
bmh
之后的任何单词,它都会崩溃并显示异常
RecursionError: maximum recursion depth exceeded in comparison
。如果您将此代码翻译为优化尾调用的语言(如OCaml、Haskell或C),则它将适用于任何单词。
第四个想法:使用组合数学来立即解决问题
不必一个一个地迭代单词,您可以尝试使用乘法计数批次单词。例如,很容易看出有:
26 * 26 * 26 = 26^3 = 17576
个单词在aaaa
和azzz
之间;
8 * 26 * 26 = 5408
个单词在baaa
和bhzz
之间;
10 * 26 = 260
个单词在biaa
和bijz
之间;
5
个单词在bika
和bike
之间。
总计:在aaaa
和bike
之间共有23249个单词。
这给我们提供了一个Python程序:
def find_word(w):
count_attempts = 0
for i,c in enumerate(w):
n = ord(c) - ord('a')
count_attempts += n * 26**(len(w) - i - 1)
return count_attempts
请注意,此处的
for
循环是针对
w
的字符而不是所有可能的单词进行的;因此我们只迭代4次而不是23249次。 这个函数比其他版本要快得多。