趣题:连接多个数字串时怎样避免歧义?

标签: 趣题 算法 Brain Storm 协议 | 发表时间:2011-06-27 02:02 | 作者:Matrix67 Codetrick
出处:http://www.matrix67.com/blog

    今天碰上一个非常有意思的问题。有一条通信线路,每次可以发送一个由数字 0 到 9 组成的任意长的数字串。怎样巧妙地利用这条通信线路,构造一种一次能够发送两个数字串的协议?注意到,直接将两个数字串相连是不行的,因为这将会产生歧义。如果对方收到的数字串是 1234 ,他没法知道你发送的是数字串 12 和 34 ,还是数字串 123 和 4 ,抑或是 1 和 234。

    能否把第一个串的位数编码进去,比如把 12 和 34 编码成 21234 ,这样不就知道第一个数字串到哪儿截止了吗?不行,因为你不知道这个位数信息本身到哪儿截止,假如编码结果是 123456789012345 ,你就不知道第一个数字串是 1 位还是 12 位了。换一个思路,能否用几个非常特殊的数字当作分隔符呢?也不行,因为你要发送的数字串里有可能偏偏也包含了这几位数。怎么办呢?


    一种非常容易想到的方法是,把两个数字串都转换为九进制,这样便把数字 9 腾了出来,它就能用作分隔符了。例如,要想发送数字串 12 和 34,只需要把 12 和 34 分别转成九进制,再在他们之间添加一个数字 9,得到数字串 13937,然后发送出去就可以了。即使要发送的数字串中有前导 0 ,问题也不大,我们可以在九进制数前面也加上相同数量的前导 0 。例如,把 012 和 0034 连在一起,也就成了 01390037 ,原始信息中的前导 0 也就很容易识别出来了。

    注意到,把一个十进制数转化成九进制数,数字位数是要增加的。一个值得考虑的问题是,为了实现数字串的连接,采用上述方法需要多花费多少位数字?一个 n 位数的九进制表达,其位数大致可以记作 log(9, 10n) ,也就是 log(9, 10) · n ,大约等于 1.048 n 。因此,把一个 n 位数变成九进制,将会增加 0.048n 位数,似乎是相当节省了。不过,从计算机算法的角度来看,系数再小,它也是 O(n) 的;当数据规模非常大的时候,这个算法的效率并不让人满意。上述算法还有优化的余地吗?

    一个优化方案是,既然首次出现的 9 一定是分隔符,后一个数字串就不必转成九进制了, 12 和 34 只需要编码成 13934 就行了。但是,渐近意义上看,所得数字串的附加长度仍然是 O(n) 。不过,如果和之前“写明第一个串的位数”的思路结合一下,我们就有了一个渐近意义上更优的方案:把第一个串的位数转化为九进制,再添加数字 9 标识出它的右边界即可。如果想要发送数字串 123456789012345 和 12345 ,就可以先数出前一个数有 15 位,再把位数本身转换成九进制 16 ,原始信息便可编码为 16912345678901234512345 。这样一来,附加长度就比原来小得多了,它只有第一个数的位数的 0.048 倍,也就是 0.048 log(n) 了。

    同样是 log(n) 级别的附加长度,我们还有一种更加简单巧妙的方案。将第一个串的位数用“口吃”的方式表达出来,每个数字都重复说一遍,然后加上一对不相同的数字(比如“01”)作为结束标记。例如, 123456789012345 和 12345 就将被编码为 11550112345678901234512345 ,表示第一个数字有 15 位。这样一来,附加的长度就是 2 log(n) + 2 ,同样也是 log(n) 的级别,不过方法简便多了。而且更棒的是,这种方法能够直接扩展到一个更加一般更加困难——两个 01 串的连接!

    大家还能想到什么其他有趣的方案?

 
    Update: 在写上面的文字时,我显然还想得不够多,现在看来让人很不满意。看了大家的回复,我深受启发,决定继续往下说一说。上述方法说穿了就是,用 11 来表示 1 ,用 22 来表示 2 ,一直到用 99 来表示 9 ,然后用 01 来表示分隔。这背后的核心思想其实就是:用两位数字来表示一位数字,从而扩展字符集。因此,我们还可以构造出很多类似的方案来。比方说,用 01 表示 1 ,用 02 表示 2,等等,一直到用 09 表示 9 ,最后用 10 表示分隔符。因此, 123456789012345 和 12345 就可以编码为 01051012345678901234512345 。读取编码后的数字串时,如果读到的是 0?0?0?... 的模式,那么我们都是在读取第一个数字串的位数;什么时候模式被打破了,出现了一个 10 ,第一个数字串的位数也就读完了。

    注意到,扩展字符集的根本动机,就是我们希望能够在字符集中新增加一个分隔符。不过,上面的方案把字符集扩展得太大了,绝大多数新字符其实都没用,实在有些浪费。有办法能够对 {0, 1, 2, ..., 9, 分隔符} 这 11 个字符重新编码,使得编码效率又高,连接起来又不会产生歧义吗?不少网友提到了 Huffman 编码,就是专门解决这个问题的。网友 hhb 提到,扩展字符集还有一个更简单的方法:直接把分隔符用 A 来表示,然后把带有分隔符的数字串看作 11 进制数,再转成 10 进制数即可(有前导 0 的话要做保留)。

    转义符的功能不也是这样吗?由于字符串无法表示出换行的概念,于是用 \n 表示换行;为了不造成歧义,又规定 \\ 表示真正的 \ 。受转义符的启发,我们就有了更好的方案,比方说用 01 表示分隔符,用 00 表示真正的 0 。解码时,每读到一个 0 ,都看看它后面跟的是什么,如果还是一个 0,表明这是一个真正的 0 ;如果是 1 ,则表示分隔符。

    如果看到了新的想法,我再接着补充。

相关 [趣题 字串 歧义] 推荐:

趣题:连接多个数字串时怎样避免歧义?

- Codetrick - Matrix67: My Blog
    今天碰上一个非常有意思的问题. 有一条通信线路,每次可以发送一个由数字 0 到 9 组成的任意长的数字串. 怎样巧妙地利用这条通信线路,构造一种一次能够发送两个数字串的协议. 注意到,直接将两个数字串相连是不行的,因为这将会产生歧义. 如果对方收到的数字串是 1234 ,他没法知道你发送的是数字串 12 和 34 ,还是数字串 123 和 4 ,抑或是 1 和 234.

一种利用ngram模型来消除歧义的中文分词方法

- - 杨尚川的个人页面
这里的 歧义是指:同样的一句话,可能有两种或者更多的切分方法,这些切分结果,有的正确,有的不正确. 消除歧义的目的就是从切分结果中挑选切分正确的. 假设我们要切分句子:结婚的和尚未结婚的,使用 逆向最大匹配和 正向最大匹配算法的结果如下:. 逆向最大匹配:[结婚, 的, 和, 尚未, 结婚, 的] 正向最大匹配:[结婚, 的, 和尚, 未结, 婚, 的].

趣题:不用相似怎么办?

- flycondor - Matrix67: My Blog
    我老早就写过一个经典的小学几何题. 如果你还没看过这个问题,你一定要去看看. 一个小学奥数老师曾经告诉我,当年带领学生参加这次竞赛时,领队老师们都没有想到这个问题的“小学生解法”,以至于开始质疑这道题是否超纲了. 看到答案后,老师们大为折服——这个问题确实有一个无需任何几何知识的妙解.     今天,同样的事情发生了.

44个精彩的物理趣题

- Henry - Matrix67: My Blog
    这个 Blog 几乎一直在讲数学趣题,却很少提到物理趣题. 其实,我个人觉得,物理也是相当好玩的(我是化学不好才选的文科). 隐约记得初中搞物理竞赛时,曾见过大量让人大呼过瘾的好题. 前几天看到了一个绝好的网站,里面有相当多的物理题目,让我激动了好一阵子. 我搜集整理了里面的一些好题,加上了我自己的一些补充,在这里和大家分享.

趣题:不动点与线性代数

- sdyy1990 - Matrix67: My Blog
    假设 X 、 Y 是两个有限集合,f:X→Y 和 g:Y→X 是两个函数. 求证:复合函数 g∘f 和 f∘g 拥有相同数量的不动点(也就是说 g(f(x)) = x 和 f(g(y)) = y 的解的个数相同).     下面先提供一个“正常”的解法. 观察函数 g∘f 的不动点,可以看出它有以下两个性质:首先,如果某个 x 是 g∘f 的不动点,即 x = g(f(x)) ,那么 f(x) = f(g(f(x))),这就说明 f(x) 是 f∘g 的一个不动点;另外,如果 x1 和 x2 是 X 中两个不同的不动点,则函数 f 不可能把它们映射到 Y 中的同一个元素,否则 g 没办法把它分别还原成 x1 和 x2.

趣题:随机折断的木棒

- Wang - FeedzShare
来自: Matrix67: My Blog - FeedzShare  . 发布时间:2011年02月06日,  已有 3 人推荐. 随机在中间选取一点,把这根木棒折断. 那么,短的那一截木棒平均有多长. 随机在中间选取一点,把这根木棒折断. 那么,长的那一截木棒平均有多长. 随机在中间选取一点,把这根木棒折断.

趣题:不用相似怎么办?

- 法法 - Matrix67: My Blog
    我老早就写过一个经典的小学几何题. 如果你还没看过这个问题,你一定要去看看. 一个小学奥数老师曾经告诉我,当年带领学生参加这次竞赛时,领队老师们都没有想到这个问题的“小学生解法”,以至于开始质疑这道题是否超纲了. 看到答案后,老师们大为折服——这个问题确实有一个无需任何几何知识的妙解.     今天,同样的事情发生了.

趣题:舞台里的狮子

- Dajusha - Matrix67: My Blog
    有一个半径为 10 米的圆形舞台,初始时舞台上的某个地方有一头狮子. 这头狮子在舞台上以折线段的方式跑了 30 千米. 求证:在整个过程中,这头狮子至少转了 2998 个弧度.     有时候,换一个角度思考,问题就会迎刃而解.     现在,让我们站在狮子的角度,用狮子的眼光来看周围的世界.

趣题:公司应该雇用多少员工?

- 山石 - FeedzShare
来自: Matrix67: My Blog - FeedzShare  . 发布时间:2011年06月13日,  已有 3 人推荐.     某大公司有这么一个规定:只要有一个员工过生日,当天所有员工全部放假一天. 但在其余时候,所有员工都没有假期,必须正常上班. 这个公司需要雇用多少员工,才能让公司一年内所有员工的总工作时间期望值最大.

趣题:老鼠与毒药问题的推广

- zz - Matrix67: My Blog
    今天的趣题来源于 IBM Ponder This 三月份的谜题.     大家应该都听说过这个老题目:有 1000 个一模一样的瓶子,其中有 999 瓶是普通的水,有一瓶是毒药. 任何喝下毒药的生物都会在一星期之后死亡. 现在,你只有 10 只小白鼠和一星期的时间,如何检验出哪个瓶子里有毒药.