请帮忙解决这个Soundex编码问题

2 投票
2 回答
1741 浏览
提问于 2025-04-15 15:03

美国人口普查局使用一种叫做“soundex”的特殊编码来查找个人信息。这个soundex编码是根据姓氏的发音来进行的,而不是根据拼写。发音相同但拼写不同的姓氏,比如SMITH和SMYTH,会有相同的编码,并且会被归在一起。这个编码系统的设计是为了让你能够找到一个姓氏,即使它可能被记录成了不同的拼写方式。

在这个实验中,你将设计、编写和记录一个程序,当输入一个姓氏时,它会输出对应的soundex编码。用户会被提示输入一个姓氏,程序应该输出相应的编码。

基本的Soundex编码规则

每个姓氏的soundex编码由一个字母和三个数字组成。使用的字母总是姓氏的第一个字母。剩下的字母会根据下面的soundex编码指南分配数字。如果需要的话,最后会加零,以确保总是生成一个四个字符的编码。多余的字母会被忽略。

Soundex编码指南

Soundex为各种辅音分配数字。发音相似的辅音会被分配相同的数字:

数字 辅音

1 B, F, P, V

2 C, G, J, K, Q, S, X, Z

3 D, T

4 L

5 M, N

6 R

Soundex会忽略字母A, E, I, O, U, H, W和Y。

还有3条额外的Soundex编码规则需要遵循。一个好的程序设计会将这些规则实现为一个或多个独立的函数。

规则1. 有双字母的名字

如果姓氏中有双字母,它们应该被视为一个字母。例如:

  • Gutierrez的编码是G362(G,T的编码是3,第一个R的编码是6,第二个R被忽略,Z的编码是2)。

规则2. 旁边有相同Soundex编码数字的字母

如果姓氏中有不同的字母并排在一起,但它们在soundex编码指南中有相同的数字,它们应该被视为一个字母。例子:

  • Pfister的编码是P236(P,F被忽略,因为它和P相同,S的编码是2,T的编码是3,R的编码是6)。

  • Jackson的编码是J250(J,C的编码是2,K被忽略因为和C相同,S被忽略因为和C相同,N的编码是5,最后加0)。

规则3. 辅音分隔符

3.a. 如果一个元音(A, E, I, O, U)分隔了两个有相同soundex编码的辅音,那么元音右边的辅音会被编码。例子:

  • Tymczak的编码是T-522(T,M的编码是5,C的编码是2,Z被忽略(见上面的“旁边”规则),K的编码是2)。因为元音“A”分隔了Z和K,所以K被编码。

3.b. 如果“H”或“W”分隔了两个有相同soundex编码的辅音,那么右边的辅音不会被编码。例子:

*Ashcraft的编码是A261(A,S的编码是2,C被忽略因为和S相同且中间有H,R的编码是6,F的编码是1)。它的编码不是A226。

到目前为止,这是我的代码:

surname = raw_input("Please enter surname:")
outstring = ""

outstring = outstring + surname[0]
for i in range (1, len(surname)):
    nextletter = surname[i]
    if nextletter in ['B','F','P','V']:
        outstring = outstring + '1'

    elif nextletter in ['C','G','J','K','Q','S','X','Z']:
        outstring = outstring + '2'

    elif nextletter in ['D','T']:
        outstring = outstring + '3'

    elif nextletter in ['L']:
        outstring = outstring + '4'

    elif nextletter in ['M','N']:
        outstring = outstring + '5'

    elif nextletter in ['R']:
        outstring = outstring + '6'

print outstring

这段代码基本上完成了要求的功能,但我不太确定如何编码这三条规则。这就是我需要帮助的地方。所以,任何帮助都非常感谢。

2 个回答

0

这里有一些关于Python的小提示。

0) 你可以用for循环来遍历任何序列,而字符串就算是一种序列。所以你可以这样写:

for nextletter in surname[1:]:
    # do stuff

这样写比计算索引然后用索引去取姓氏要简单得多,也更容易理解。

1) 你可以用+=这个操作符来拼接字符串。比如说,不用写

x = x + 'a'

你可以这样写:

x += 'a'

至于你具体问题的帮助,你需要记住前一个字母。如果你的作业有个规则说“两个连续的'z'字符应该编码为99”,你可以加上这样的代码:

def rule_two_z(prevletter, curletter):
    if prevletter.lower() == 'z' and curletter.lower() == 'z':
        return 99
    else:
        return -1


prevletter = surname[0]
for curletter in surname[1:]:
    code = rule_two_z(prevletter, curletter)
    if code < 0:
        # do something else here
    outstring += str(code)
    prevletter = curletter

嗯,你的代码是返回像'3'这样的字符串整数,而我写的代码是返回一个实际的整数,然后在把它加到字符串之前调用str()。这两种方式都可以。

祝你好运!

0

这里有几个小提示:

  • 可以用一个数组来存储每个Soundex代码,并根据它对应字母的ASCII值(或者从中得到的较小的数字范围)来索引,这样做会让代码既高效又易读。这是一种非常常见的技巧:理解、使用并重复利用这个方法哦;-)

  • 在解析输入字符串的时候,你需要记住(或者和)之前处理过的字母进行比较,以便忽略重复的字母,并处理其他规则。(可以把这些规则分别放在不同的函数里,就像文中提到的那样)。可以考虑引入一个函数,专门负责-可能-为当前正在处理的字母添加Soundex代码。这个函数会调用每个“规则”函数,可能会根据某些规则的返回值提前退出。换句话说,替换掉系统性的...

    outstring = outstring + c    # btw could be +=
...with
    outstring += AppendCodeIfNeeded(c)
  • 要注意,这种多函数的结构对于这么简单的逻辑来说有点过头,但为了练习这样做也不是个坏主意。

撰写回答