二分与猜数

二分是解决很多问题的核心思想,在信息学奥赛中被广泛应用。

一个广为人知的问题是:AA11100100 里猜一个整数 xxBB 每次问一个整数 yy,不能问两次相同的 yyAA 会告诉他 xxyy 的大小关系,直到 BB 猜中为至,问 BB 用什么策略猜最好呢?

答案是,BB 每次猜 xx 可能存在区间的中间数,那么这个区间每次都会减小一半,比如最开始我们猜 y=50y=50,那么 xx 要么比 5050 大,要么比 5050 小,要么等于 5050,这下 xx 可能存在的区间大小都变成原来的一半,又因为 26<100<272^6 < 100 < 2^7,所以最多减半 7 次后,xx 可能存在的区间都只有 11 个数,那自然 xx 也就被确定了。

假如问题推广到在 [1,n][1,n] 区间,那么容易发现猜的次数是 O(logn)O(\log n) 次的,这里 O(logn)O(\log n) 代表猜的次数的渐进数量级,如:

nn 个同学点名的复杂度是 O(n)O(n) 的,给 (n+1)(n+1) 个同学点名也是 O(n)O(n) 的,给 2n2n 个同学点名的复杂度的还是 O(n)O(n) 的,因为只要我们多找一个老师同时点,那么每个老师需要点的次数还是不超过 nn 次。而给 n2n^2 个同学点名的复杂度是 O(n)O(n) 的,因为假如你请 kk 个老师点名,kk 为常数,那么当 n=k+1n=k+1 时每个老师点名次数依旧超过了 nn。如果把老师看做计算机单元,点名学生看成计算,那么我们仅仅需要考虑增加计算机单元解决不了的问题。

给出 log\log 函数的定义: 2logx=x2^{\log x} = x

那么怎么证明这个策略最优呢?我们先定义最优的策略为最大猜到答案次数最小的策略,那么每一次猜测相当于我们把问题分成了两支,就可以画成一个树形图:

这是一个 n=10n=10 时画出的最优决策之一下的树形图。

这样的树叫做决策树,每个决策下一步是两个不同的决策,而一个决策也对应着答案,也就是这一次猜到了的结果,每个点都对应着一个答案。

对于一个猜 [1,n][1,n] 里整数的决策树:

  1. 这个决策树一共有多少个节点?

  2. 在图中这样的决策方法中,猜哪些数字会导致猜到 xx 的次数最大?

  3. 构造一个 n=5n=5 时最劣决策方式的树形图(即让猜到 xx 最大可能次数最大的决策)。

  4. 证明决策树至少存在一个需要 O(logn)O(\log n) 次才能猜到的决策。

提示:

1+21+22++2n=2n+111+2^1 +2^2 + \cdots + 2^n = 2^{n+1} - 1

ii 层的节点个数至多是第 (i1)(i-1) 层的两倍。

排序问题:对于 nn 个互不相同的数,把他们从大到小排成一排,请给出尽量快的算法。

二分算法是许多排序算法的核心思想,给出这样一个排序算法:

对于一个长度为 nn 的序列 ana_n,我们每次随机拿出一个数 axa_x,把比 axa_x 小的数放到 axa_x 前面,比 axa_x 大的数放在 axa_x 后面,那么我们只需要再对比他大的数做一次同样的过程,比他小的数做一次同样的过程,最后一定能得到有序的序列,比如对于序列 {4,3,2,1,6,5}\{4,3,2,1,6,5\}

  • 我们选定 33 当中间那个数,序列变成:{2,1,3,4,6,5}\{2,1,3,4,6,5\}

  • 再分别对前一半序列 {2,1}\{2,1\} 和后一半 {4,6,5}\{4,6,5\} 做同样的过程:

  • 前一半序列选定 22 当中间,那么只剩 {1}\{1\} 单个元素一定有序,得到 {1,2}\{1,2\}

  • 后一半序列选定 55 当中间,前后都只剩一个元素一定有序,得到 {4,5,6}\{4,5,6\}

  • 那么原序列就变成了 {1,2,3,4,5,6}\{1,2,3,4,5,6\} 显然有序。

概率论告诉我们每次选的数都靠近中间,所以和上面猜数是本质相同的过程,但因为我们每一次决策都要把数重排,需要 O(n)O(n) 的复杂度,随机下期望有 O(logn)O(\log n ) 层,所以在随机下复杂度期望为 O(n)×O(logn)=O(nlogn)O(n) \times O(\log n) = O(n\log n)

  1. 在这种排序算法的启发下,试给出一个求出序列第 kk 大是谁的 O(n)O(n) 算法。

提示: 复杂度为:$O(n+\frac n 2 + \frac n 4 + \frac n 8+ \cdots ) = O(n)$

对于原序列来说,排序结果无非是对原序列标号的重排之一,而原序列共有 $n!=n\times(n-1)\times(n-2)\times\cdots\times 2\times 1$ 种重排,每次比较 aia_iaja_j 得到的信息无非是 aia_iaja_j 前面或者是 aja_jaia_i 的前面两种,那么按照这样可以画出一个类似猜数的决策树,每一种原序列的重排都是决策树的一个终止节点(答案)。

  1. 试证明基于比较的排序复杂度下界为 O(nlogn)O(n\log n)

提示:O(logn!)=O(nlogn)O(\log n! ) = O(n\log n)

位与功能完备

在生活中,加法乘法是最普遍被认识的运算。众所周知,计算机是以二进制的形式存储信息的,正因如此,对于计算机,最基本的运算并非加法或乘法,而是 布尔运算

就像十进制下的一位上可以是 0099,二进制下的一位上只会是 00 或是 11,布尔运算就是对 0011 之间进行的运算,下面是几种常见的布尔运算:

  • not\text {not} 运算,表示取反:
not 1=0\text {not } 1 = 0 not 0=1\text {not } 0 = 1
  • or\texttt {or} 运算,表示取或,a or b=1a \text{ or }b = 1 当且仅当 a,ba,b 中至少有一个是 11,否则为 00
$$1\text{ or } 0 = 0 \text{ or } 1 = 1 \text{ or } 1 = 1 $$0 or 0=00 \text{ or }0=0
  • and\text{and} 运算,表示取与,A and B=1A \text{ and } B = 1 当且仅当 a,ba,b 都为 11,否则为 00

  • xor\text{xor} 运算,表示取异或,A xor B=1A \text{ xor } B = 1 当且仅当 aa 不等于 bb,否则为 00

  1. 对于 or\text{or} 运算,我们能给出这个运算的真值表如下,请画出 and\text{and}xor\text{xor} 的真值表。
$$\begin{array}{|c|c|c|} \hline A & B & A \text{ or } B \\ \hline 0 & 0 & 0 \\ 0 & 1 & 1 \\ 1 & 0 & 1 \\ 1 & 1 & 1 \\ \hline \end{array} $$

我们称一个布尔运算组合是功能完备的,当且仅当这些位运算组合出的新运算就可以构造出全部的布尔运算,如:

$$A \text{ and } B = ((\text{not } A)\text{ or }(\text{not B})) $$

是用了 not, or\text{not, or} 构造了 and\text{and}

  1. 请求出有多少本质不同的二元布尔运算,我们称两个二元布尔运算本质不同,当且仅当存在一对 (A,B)(A,B) 对两个布尔运算的结果是不同的,例:

存在 A=1,B=0A=1,B=0 时,A and BA or BA\text{ and }B \neq A \text{ or } B,所以我们称 and, or\text{and, or} 本质不同。

  1. 已知 (not, and, or)\text{(not, and, or)} 这三种运算的组合可以构造出全部的二元布尔运算,试证明 nand\text{nand} 可以构造出全部的二元布尔运算,其中:
A nand B=not (A and B)\text{A nand B}= \text{not }(A \text{ and }B)
  1. 试证明 nand\text{nand} 是功能完备的(不考虑 0 元的情况)。

  2. 试使用 and, or\text{and, or} 构造出二进制下的整数加法。

开放题

  1. 你为什么学习信息学奥赛?

  2. 你有哪些编程、数学、计算机方面的基础?

  3. 你迄今为止遇到过最大的问题是什么,你采用了哪些手段解决它?

65 comments