Caesar Cipher:为什么我的程序 returns 是错误的答案,而在用作测试的打印值之间却有正确的答案?
Caesar Cipher: why does my program returns the wrong answer, when between the printed values used as tests there is the correct one?
我正在尝试编写一个函数,其目标是解密使用凯撒密码加密的消息,这个函数被命名为 decrypt_message
。函数 returns 给出错误答案,但在打印值中有一个正确答案。
示例:
在下图中,您可以看到打印的值中有 hi how are you
是正确的答案,但函数返回了错误的答案。
您可以在这里找到程序中使用的名为 'words.txt' 的文件及其防病毒扫描:
https://www.dropbox.com/s/jyeos4kdy50yseh/words.txt?dl=0;
这是我的代码(写在Python 3):
import string
def build_shift_dict(shift):
'''
Creates a dictionary that can be used to apply a cipher to a letter.
The dictionary maps every uppercase and lowercase letter to a
character shifted down the alphabet by the input shift. The dictionary
should have 52 keys of all the uppercase letters and all the lowercase
letters only.
shift (integer): the amount by which to shift every letter of the
alphabet. 0 <= shift < 26
Returns: a dictionary mapping a letter (string) to
another letter (string).
'''
d = {}
s_lower = string.ascii_lowercase * 27
s_upper = string.ascii_uppercase * 27
for i in range(27):
d[s_lower[i]] = s_lower[i+shift]
d[s_upper[i]] = s_upper[i + shift]
return d
def apply_shift(shift, message_text):
'''
Applies the Caesar Cipher to message_text with the input shift.
Creates a new string that is message_text shifted down the
alphabet by some number of characters determined by the input shift
shift (integer): the shift with which to encrypt the message.
0 <= shift < 26
Returns: the message text (string) in which every character is shifted
down the alphabet by the input shift
'''
d = build_shift_dict(shift)
r = ""
for e in message_text:
if e in string.punctuation or e in string.whitespace or e in "0123456789":
r += e
else:
r += d[e]
return r
def decrypt_message(message_text):
'''
Decrypt message_text by trying every possible shift value
and find the "best" one. We will define "best" as the shift that
creates the maximum number of real words when we use apply_shift(shift)
on the message text. If shift is the original shift value used to encrypt
the message, then we would expect 26 - shift to be the best shift value
for decrypting it.
Note: if multiple shifts are equally good such that they all create
the maximum number of you may choose any of those shifts (and their
corresponding decrypted messages) to return
Returns: a tuple of the best shift value used to decrypt the message
and the decrypted message text using that shift value
'''
word_list = load_words('words.txt')
best_shift = 0
s = ""
mostValidWords = 0
for i in range(1,27):
validWordsCounter = 0
s = apply_shift(i, message_text)
print(s)
for e in s.split(" "):
if is_word(word_list, e) == True:
validWordsCounter += 1
if validWordsCounter > mostValidWords:
best_shift = i
return (best_shift, apply_shift(26 - best_shift, message_text))
def load_words(file_name):
'''
file_name (string): the name of the file containing
the list of words to load
Returns: a list of valid words. Words are strings of lowercase letters.
Depending on the size of the word list, this function may
take a while to finish.
'''
print('Loading word list from file...')
# inFile: file
in_file = open(file_name, 'r')
# line: string
line = in_file.readline()
# word_list: list of strings
word_list = line.split()
print(' ', len(word_list), 'words loaded.')
in_file.close()
return word_list
def is_word(word_list, word):
'''
Determines if word is a valid word, ignoring
capitalization and punctuation
word_list (list): list of words in the dictionary.
word (string): a possible word.
Returns: True if word is in word_list, False otherwise
Example:
>>> is_word(word_list, 'bat') returns
True
>>> is_word(word_list, 'asdf') returns
False
'''
word = word.lower()
word = word.strip(" !@#$%^&*()-_+={}[]|\:;'<>?,./\"")
return word in word_list
你的错误似乎在这里:
return (best_shift, apply_shift(26 - best_shift, message_text)
在您的演示输出中,正确的解码是在输出 24 上,因此您应该这样做:
return (best_shift, apply_shift(best_shift, message_text)
因为 26 - best_shift
将 return 将 shift = 2
传递给 apply_shift
而不是您在最后正确打印出的 24 - (24, 'lm lsa evi csy')
- 但已经通过在错误的班次编号
您的代码中存在(至少)三个明显的问题:
- 您的单词列表阅读代码只有在所有单词都在一行上时才有效。 (当然,如果您的单词列表确实 确实 由一行长行组成,那也没关系。但是大多数单词列表每行只有一个单词。)
- 除了 0 之外,您永远不会将任何值分配给
mostValidWords
。
- 正如 N Chauhan 所指出的,您正在寻找用于解密输入的最佳移位量 S,然后将输入移位 26 − 返回结果时用S代替S
无论如何,如果您在调试器中单步执行代码(学习使用它!它真的很有用!)或使用更详细的调试打印语句,您自己可能会发现这些问题仅 print(s)
.
特别是,像这样的东西(例如放置在循环的末尾)会告诉您更多关于代码中发生的事情:
print("shift {}: '{}' ({} valid words), best shift {} ({} valid words)"
.format(i, s, validWordsCounter, best_shift, mostValidWords))
无论如何,解决了这些问题后,your code works. You could make it more efficient by converting word_list
into a set(例如只需将 load_words
中的 return word_list
替换为 return set(word_list)
),但对于像您这样的短消息,它是不是绝对必要。
我正在尝试编写一个函数,其目标是解密使用凯撒密码加密的消息,这个函数被命名为 decrypt_message
。函数 returns 给出错误答案,但在打印值中有一个正确答案。
示例:
在下图中,您可以看到打印的值中有 hi how are you
是正确的答案,但函数返回了错误的答案。
您可以在这里找到程序中使用的名为 'words.txt' 的文件及其防病毒扫描:
https://www.dropbox.com/s/jyeos4kdy50yseh/words.txt?dl=0;
这是我的代码(写在Python 3):
import string
def build_shift_dict(shift):
'''
Creates a dictionary that can be used to apply a cipher to a letter.
The dictionary maps every uppercase and lowercase letter to a
character shifted down the alphabet by the input shift. The dictionary
should have 52 keys of all the uppercase letters and all the lowercase
letters only.
shift (integer): the amount by which to shift every letter of the
alphabet. 0 <= shift < 26
Returns: a dictionary mapping a letter (string) to
another letter (string).
'''
d = {}
s_lower = string.ascii_lowercase * 27
s_upper = string.ascii_uppercase * 27
for i in range(27):
d[s_lower[i]] = s_lower[i+shift]
d[s_upper[i]] = s_upper[i + shift]
return d
def apply_shift(shift, message_text):
'''
Applies the Caesar Cipher to message_text with the input shift.
Creates a new string that is message_text shifted down the
alphabet by some number of characters determined by the input shift
shift (integer): the shift with which to encrypt the message.
0 <= shift < 26
Returns: the message text (string) in which every character is shifted
down the alphabet by the input shift
'''
d = build_shift_dict(shift)
r = ""
for e in message_text:
if e in string.punctuation or e in string.whitespace or e in "0123456789":
r += e
else:
r += d[e]
return r
def decrypt_message(message_text):
'''
Decrypt message_text by trying every possible shift value
and find the "best" one. We will define "best" as the shift that
creates the maximum number of real words when we use apply_shift(shift)
on the message text. If shift is the original shift value used to encrypt
the message, then we would expect 26 - shift to be the best shift value
for decrypting it.
Note: if multiple shifts are equally good such that they all create
the maximum number of you may choose any of those shifts (and their
corresponding decrypted messages) to return
Returns: a tuple of the best shift value used to decrypt the message
and the decrypted message text using that shift value
'''
word_list = load_words('words.txt')
best_shift = 0
s = ""
mostValidWords = 0
for i in range(1,27):
validWordsCounter = 0
s = apply_shift(i, message_text)
print(s)
for e in s.split(" "):
if is_word(word_list, e) == True:
validWordsCounter += 1
if validWordsCounter > mostValidWords:
best_shift = i
return (best_shift, apply_shift(26 - best_shift, message_text))
def load_words(file_name):
'''
file_name (string): the name of the file containing
the list of words to load
Returns: a list of valid words. Words are strings of lowercase letters.
Depending on the size of the word list, this function may
take a while to finish.
'''
print('Loading word list from file...')
# inFile: file
in_file = open(file_name, 'r')
# line: string
line = in_file.readline()
# word_list: list of strings
word_list = line.split()
print(' ', len(word_list), 'words loaded.')
in_file.close()
return word_list
def is_word(word_list, word):
'''
Determines if word is a valid word, ignoring
capitalization and punctuation
word_list (list): list of words in the dictionary.
word (string): a possible word.
Returns: True if word is in word_list, False otherwise
Example:
>>> is_word(word_list, 'bat') returns
True
>>> is_word(word_list, 'asdf') returns
False
'''
word = word.lower()
word = word.strip(" !@#$%^&*()-_+={}[]|\:;'<>?,./\"")
return word in word_list
你的错误似乎在这里:
return (best_shift, apply_shift(26 - best_shift, message_text)
在您的演示输出中,正确的解码是在输出 24 上,因此您应该这样做:
return (best_shift, apply_shift(best_shift, message_text)
因为 26 - best_shift
将 return 将 shift = 2
传递给 apply_shift
而不是您在最后正确打印出的 24 - (24, 'lm lsa evi csy')
- 但已经通过在错误的班次编号
您的代码中存在(至少)三个明显的问题:
- 您的单词列表阅读代码只有在所有单词都在一行上时才有效。 (当然,如果您的单词列表确实 确实 由一行长行组成,那也没关系。但是大多数单词列表每行只有一个单词。)
- 除了 0 之外,您永远不会将任何值分配给
mostValidWords
。 - 正如 N Chauhan 所指出的,您正在寻找用于解密输入的最佳移位量 S,然后将输入移位 26 − 返回结果时用S代替S
无论如何,如果您在调试器中单步执行代码(学习使用它!它真的很有用!)或使用更详细的调试打印语句,您自己可能会发现这些问题仅 print(s)
.
特别是,像这样的东西(例如放置在循环的末尾)会告诉您更多关于代码中发生的事情:
print("shift {}: '{}' ({} valid words), best shift {} ({} valid words)"
.format(i, s, validWordsCounter, best_shift, mostValidWords))
无论如何,解决了这些问题后,your code works. You could make it more efficient by converting word_list
into a set(例如只需将 load_words
中的 return word_list
替换为 return set(word_list)
),但对于像您这样的短消息,它是不是绝对必要。