02.Vector.pdf P45
教材 2.6.5、2.6.6
习题解析[2-20]
版本A为非searchLast版本,即为:
[TODO]: 教材 2.6.5、2.6.6,习题解析[2-20]中有详细归纳分析平均查找长度。
注意,上面的代码是左闭右开区间 $[lo, hi)$,如果向量长度为偶数,$mi = (lo + hi) / 2$ , $mi$ 会是上中位数位置。
怎么看比较次数?
二分查找里的比较次数(查找长度),看上面的代码。
如果要往左递归子向量(e < S[mi]),要比较 $1$ 次。
如果要往右递归子向量(S[mi] < e),要比较 $2$ 次。
(上面 e < S[mi] 为false,然后 S[mi] < e 为true,比较了 $2$ 次)
如果在当前位置命中元素,要比较 $2$ 次。
(e < S[mi] 为false 且 S[mi] < e 为false。)
注意后两种情况都是 $2$ 次。
用图来看:
走左侧绿色的边,需要 $1$ 次。
走右侧绿色的边,需要 $2$ 次。
在一个内部点处安定下来,需要 $2$ 次;若在外部点处安定下来,外部点处不需要查找长度。
以上面 n = 7 的查找树为例:
1 是真实的点,走到 1 需要 2 条向左边,1 次定点,查找长度为 $1 + 1 + 2 = 4$ 。
0 是虚拟的点,代表比 1 小,走到 0 需要 3 条向左边,虚拟的点定点不需要开销,查找长度为 $1 + 1 + 1 = 3$ 。
其它点同理。
往左需要1次,往右需要2次,让二分不是均匀二分,而是左侧重,往左的概率大,能得到更小的比较次数。
若有 $n = fib(k) - 1$ ,取 $mi = fib(k - 1) - 1$ ,前后子向量长度分别为 $fib(k - 1) - 1 、 fib(k - 2) - 1$ 。
计算查找长度的方式与均分的情况相同,只是树是左倾的:
前后部分应该取何种比例方式分割?
可以不太严谨地假设 $f(n) = \alpha(\lambda)log_2{n}$ ,即 $\lambda$ 的选择影响常系数。
如何让 $\alpha(\lambda)$ 最小?
写递推式,落入左右部分的概率分别为 $\lambda$ 和 $1 - \lambda$ ,于是:
$\alpha(\lambda)log_2{n} = \lambda[1 + \alpha(\lambda)log_2{(\lambda n)}] + (1 - \lambda)[2 + \alpha(\lambda)log_2{((1 - \lambda) n)}]$
整理后得到:
$\frac{-ln2}{\alpha(\lambda)} = \frac{\lambda ln\lambda + (1 - \lambda)ln(1 - \lambda)}{2 - \lambda}$
右边求导之后是:
$\frac{2ln\lambda - ln(1 - \lambda)}{(2 - \lambda)^2}$
(不想手动算的话可以用这个网站)
所以,$\lambda$ 取 $\frac{\sqrt{5} - 1}{2}$ 时,$f(n)$ 最小。
而对斐波拉契数列,$fib(n) = \frac{1}{\sqrt{5}}[(\frac{1 + \sqrt{5}}{2})^n - (\frac{1 - \sqrt{5}}{2})^n]$
后项/前项的极限为黄金分割比 $\frac{1 + \sqrt{5}}{2}$ ,而上面 $\frac{\lambda}{1 - \lambda} = \frac{\frac{\sqrt{5} - 1}{2}}{1 - \frac{\sqrt{5} - 1}{2}} = \frac{\sqrt{5} + 1}{2}$ 也是黄金分割比。所以fib查找的分割方式是最优的。
这个方法,有点神奇,写了下递推式,就无中生有可以做了。
即为常见的searchLast/searchFirst的写法。
只有两种分支情况,往左/往右走都只需 $1$ 次比较,相当于以 $+1$ 的开销进入左子向量/右子向量。
二分查找版本A:
$ASL_{succ} = O(1.50 \cdot logn)$
$ASL_{fail} = O(1.50 \cdot logn)$
fib查找:
$ASL_{succ} = O(1.44 \cdot logn)$
$ASL_{fail} = O(1.38 \cdot logn)$
二分查找版本B/C:
$ASL_{succ} = O(1.00 \cdot logn)$
$ASL_{fail} = O(1.00 \cdot logn)$