顶功及其优势
声笔码
我们在这里用《声笔码》为例来让大家对顶功先产生一个直观的了解。声笔码是历史上的一个顶功输入方案,它直接或者间接地激发了其它顶功输入法的产生,因此以其为案例开始有其历史意义。
声笔码对汉字的编码规则如下:
- 第一码为汉字拼音的首字母(如果是 aoe 则用 v 表示),使用了 bpmfdtnlgkhjqxzcsrywv 共 21 个键;
- 第二、三和四码为汉字的第一、第二和第三个笔画,使用了 aeiou 共 5 个键。
可见,其全码为 4 码。例如,「码」的全码为 meui
,由声母 m、和前三笔「横、撇、竖」(安排在 e, u, i
键上)构成。除全码外,还有一、二、三级简码,例如 m
对应「没」,me
对应「面」,meu
对应「码」等等。最重要的是,声笔码规定:字的实际编码就是字的原始编码,不管是全码还是一、二、三级简码都不需要加空格上屏1。
举例来说,假设用户想以单字方式连续输入「没码面」三个字,那么用户只需要输入 mmeume
即可,中间不需要加任何空格。这显然与五笔字型这样的前缀码很不相同,因为这里允许「m 没」是「me 面」的前缀。为什么在这样的情况下,输入平台还能做出正确的切分呢?这和它具体的编码规则有关系!
在声笔码中,第一码一定是 bpmfdtnlgkhjqxzcsrywv
中的一个,而二三四码一定是 aeiou
中的一个。这样,输入平台每看到一个辅音字母,就知道一定是一个新的字的输入开始了;而看到一个元音字母,就知道一定是在向前面的字追加编码。因此,尽管它不是前缀码,但用户输入的一串按键仍然能被准确切分!
我们来汇总一下声笔码和五笔字型的区别:
- 五笔字型
- 第一至四码都使用 a-y 25 键
- 原始编码的一二三码需要空格上屏、四码自动上屏
- 实际编码如
g_
一、gg_
五、ggg_
玨、gggg
王……等等
- 声笔码
- 第一码使用 bpmfdtnlgkhjqxzcsrywv 21 键,第二、三、四码使用 aeiou 5 键
- 实际编码如
m
没、me
面、meu
码、meui
码……等等
并且:
- 五笔字型中,每一个字实际编码都不会是另一个字的实际编码的前缀,例如
g_
不是gg_
的前缀;在实际编码输入完成之后,输入平台就立即知道了用户打的是哪个字。 - 声笔码中,有些字的实际编码是另一个字的实际编码的前缀,例如
m
是me
的前缀;当用户输入完m
之后,输入平台还不知道用户要打的是「没」还是「面」,但是当用户继续输入下一个字的编码的时候,输入平台却能推断出用户上一个字已经输入完成。之所以能这样做,是由于「bpmfdtnlgkhjqxzcsrywv 21 键」和「aeiou 5 键」形成了互斥关系,导致不存在mm
这样的编码。
顶功的定义
基于这个观察,我们就可以给出「顶功输入方案」的正式定义:
我们说一个方案是顶功输入方案,如果其中存在两个不同的实际编码,较短码是较长码的前缀;
由于不再能通过前缀来切分,为了使输入平台做出正确的切分,必须存在特殊的结构来保证下一个编码总是和较长码的剩余部分形 成互斥关系。
也就是说,如果一个码不是前缀码,那它就是顶功。顶功是前缀码的反义词。
顶功方案的格式
读者可能会觉得上面关于声笔码的描述过于冗赘。是的,我们现在就要引入一个更好的工具来描述顶功方案:顶功格式。
假设 26 个拉丁字母构成的结合用 U 表示;「bpmfdtnlgkhjqxzcsrywv 21 个键」用 A 表示,称为大集合;「aeiou 5 个键」用 B 表示,称为小集合。那么,声笔码的所有编码必然属于以下四个之一:
- A
- AB
- ABB
- ABBB
而顶功的原理也可以简洁地表达为:这些编码的首码 A 与较长码的剩余部分中的 B 互斥。这里的每个式子称为一个「正则表达式」,而这四个式子合在一起所构成的集合(A、AB、ABB、ABBB)称为声笔码这个方案的「格式」。
顶功极大降低了设置高效的简码的难度
在上面的例子中,声笔码确实比五笔字型在简码的利用效率上高了很多:
- 声笔码:一键字 21 个,二键字 105 个(21 × 5),三键字 525 个(21 × 5 × 5),其余为四键字
- 五笔字型:一键字 0 个,二键字 25 个,三键字 625 个(25 × 25),其余为四键字
但是看了上一篇文章的分析,你可能会问:前缀码理论上效率也并不更差,不能让前缀码的码长和这个顶功方案一样短吗?
是的,理论上可以构造出来一个和声笔码码长相同的前缀码方案,这个方案的格式是:
- A
- BA
- BBA
- BBBA
这和声笔码的格式(A、AB、ABB、ABBB)只是顺序调换了一下而已。但是,这个方案的代价是什么呢?代价就是,你在输入的时候,开始输入之前就得知道这个字是用 A 还是 BA 还是 BBA 还是 BBBA 编码的,只能尝试或者死记住。
如果用格式来描写五笔字型的编码,设 B = a ~ y 25 个键,A = 空格,那么格式是:
- BA(一级简码,如 g_)
- BBA(二级简码)
- BBBA(三级简码)
- BBBB
是不是和上面很像?但是,五笔字型的用户不需要死记到底是哪一级简码,因为他们在输入 B, BB, BBB 的时候分别已经显示出 BA, BBA, BBBA 的候选了,如果是想要的字就补空格(A)上屏,如果不是就继续打。这里的关键在于空格(A)是不携带信息的,所以用户不用被迫提前做出选择。
与此相对,顶功的解决方案就自然得多了:A、AB、ABB、ABBB,因为一个编码可以是另外一个编码的前缀,所以用户只需要看候选确认就可以自然地熟悉简码,而且不影响末码携带信息。
综上所述,顶功的真正优势在于「允许一个编码是另一个编码的前缀」和「简码是全码的前缀」这两者是天然吻合的。在前缀码需要付出大量学习成本来达到理论最优码长(即接近 Huffman 编码 的码长)时,顶功只需要使用统一的编码规则结合确认候选的动作就可以轻易地接近最优码长。
前缀码,真的不行?
上面的讨论,只是在理论上预言了前缀码在提高简码效率的时候,不如顶功来得「简单」。也就是说,顶功主要是易学性上的优势。
在实际的方案设计中,有可能会设计出来这样的前缀码:在末码携带信息的情况下,只需要记忆很少量的特殊简码,剩下的都打全码或者加空格,以平衡码长和学习难度。这样的前缀码也是实用的,而且可能具备一些特殊的好处。我们会在方案示例中单独开辟「前缀码」一节来讨论前缀码的设计。
附注:定长方案最长码上的选重并不构成顶功结构
有人可能会疑惑:五笔中,「王」和「琵琶」的原始编码都是 gggg
,前者可以被下一个字的编码顶上屏幕,实际编码为 gggg
;后者需要选重,实际编码为 gggg2
。gggg
是 gggg2
的前缀,字母键和数字键互斥,这是不是也符合上面的定义呢?
确实,这种情况表面上是符合上面的定义,不过 2
只是一个选重键,其中不包含任何有效的编码信息;并且,这种选重并不是作为五笔字型的主要输入方式出现的。所以,实际上顶功是要求这个较长码的剩余部分携带 编码信息。
Footnotes
-
实际上,声笔码是单字和词组混合输入,导致其一码字并不能顶。这里为了教学上的方便,把它视为一个一码顶单字输入方案。 ↩