存档

‘Computer Science’ 分类的存档

Fastbit中的bitmap索引算法

2010年8月19日 Solrex Yang 3 条评论

摘要:bitmap 索引是一种典型的数据库索引方案,本文基于 Fastbit 软件包,使用实际用例对一些常用的 bitmap 索引算法进行了一个较为系统的介绍。

一、Fastbit是什么?

引用 Fastbit 的官方网站上的介绍:Fastbit是一个追随 NoSQL(Not Only SQL) 运动精神的开源的数据处理程序库,它提供了一系列的用压缩的 bitmap 索引支持的查询函数。在这里,我们关注的关键词是“bitmap 索引”。Fastbit 使用的是按列存储方式,其 bitmap 索引也是在按列存储的数据上建立起来的。

二、Fastbit 中的 bitmap 索引算法

Fastbit 的源代码有着非常清晰的结构。在 Fastbit 的源代码中,每个索引算法都用一个 C++ 类来实现,所有的索引算法类都是基类 index 的派生,并且在 fastbit 源代码中保存为以 i 开头的源文件。

下面是 Fastbit 中的索引类的派生关系图,从美观考虑,直接使用 xmind 思维导图而不是 UML 来展现了:

fastbit 索引算法派生关系图

下面我们将对其中部分算法进行简单的介绍。我们将这些索引算法分为几大类:基础算法、扩展算法、多层算法和多成分算法。

三、基础 bitmap 索引算法

基础的 bitmap 索引算法是最简单的 bitmap 索引算法,给出了 bitmap 索引的基本原理。

3.1 relic

relic (定义在 irelic.h 中,实现在 irelic.cpp ) 是最原始的 equality-encoded 算法,这个单词代表“遗迹”的意思。它可谓是最简单直观的 bitmap 索引算法。relic 为需要索引的每个值都建立一个 bitvector,在该 bitvector 中,只有等于该值的列才会被置 1,其它位都被置 0,如下表所示:

数据 索引(bitmap)
a b d e g
a 1 0 0 0 0
g 0 0 0 0 1
d 0 0 1 0 0
e 0 0 0 1 0
b 0 1 0 0 0
d 0 0 1 0 0
g 0 0 0 0 1
e 0 0 0 1 0
3.2 bin

bin (定义于 ibin.h,实现在 ibin.cpp)是 binned equality-encoded 算法,这里它代表“桶”的意思。它可以视为是 relic 的一种变形,它将值域分为几个不相交的区间,将原本是相等才置一的规则转变为值落在该区间内就置一,如下表所示。当然,relic 也可以视为 bin 的一个特例(将区间定义为 [a, a+ε)。bin 每个区间的范围由程序遵从某些规则设定,这些规则由命令行通过参数传入。

数据 索引(bitmap)
(…,b) [b,e) [e,…)
a 1 0 0
g 0 0 1
d 0 1 0
e 0 0 1
b 0 1 0
d 0 1 0
g 0 0 1
e 0 0 1
3.3 bin->range

range (定义于 ibin.h,实现于 irange.cpp)是 range-encoded 算法,这里它代表“范围”的意思。正如它字面所表达的意思,range 的每个 bitvector 标记着小于某边界值的值,如下表所示。因此,它可以视为是 bin 的一个累积表示,这也是 fastbit 软件包中所做的:首先构造 bin,然后累加转换成 range。值得注意的是,一般最后一列代表着小于无穷大,因此该 bitvector 全为 1,会被略去不写。

数据 索引(bitmap)
(…,b) (…,e) (…,g)
a 1 1 1
g 0 0 0
d 0 1 1
e 0 0 1
b 0 1 1
d 0 1 1
g 0 0 0
e 0 0 1
3.4 bin->mesa

mesa (定义于 ibin.h,实现于 imesa.cpp)是 interval-encoded 算法[1],它与 bin 类似,只不过它的区间之间有重叠部分。与 range 相同,在 fastbit 软件包中,它也是通过 bin 构造起来的。

数据 索引(bitmap)
(…,d) [a,e) [b,g) [d,…)
a 1 1 0 0
g 0 0 0 1
d 0 1 1 1
e 0 0 1 1
b 1 1 1 0
d 0 1 1 1
g 0 0 0 1
e 0 0 1 1

四、扩展 bitmap 索引算法

4.1 direkte

direkte (定义于 idirekte.h,实现于 idirekte.cpp)是丹麦语中的 direct,它与 relic 几乎是一样的,不同点只是它为小于最大值的所有值都建立了一个 bitvector(即使该值并不存在于列中)。

数据 索引(bitmap)
a b c d e f g
a 1 0 0 0 0 0 0
g 0 0 0 0 0 0 1
d 0 0 0 1 0 0 0
e 0 0 0 0 1 0 0
b 0 1 0 0 0 0 0
d 0 0 0 1 0 0 0
g 0 0 0 0 0 0 1
e 0 0 0 0 1 0 0
4.2 relic->slice

slice(定义于 irelic.h,实现于 islice.cpp)实现了 O'Neil'97 [2] 提出的 bit-slice 算法。它的基本思想就是首先将原始数据用二进制进行编码,bitmap 就是所有值的二进制编码表示的集合,bitvector 的个数由最大值的二进制表示决定,如下表所示:

数据 编码 索引(bitmap)
a 0 0 0 0
g 4 1 0 0
d 2 0 1 0
e 3 0 1 1
b 1 0 0 1
d 2 0 1 0
g 4 1 0 0
e 3 0 1 1
4.3 bin->bak

bak (定义于 ibin.h,实现于 idbak.cpp)是丹麦语中的 bin,因此它是 bin 的变形。它使用减精度来表示 bin 区间的中心,即它的每一个区间都是用一个更低精度的数来表示,具体来说就是四舍五入啦。下面是一个对 1-100 的数据列建立 bak 索引的输出,其中第一列表示区间的中心,第二三列代表区间最小最大值,第四列代表该区间内数据的个数:

index (equality encoding on reduced precision values) for data.a contains 19 bitvectors for 100 objects
1   1   1   1
2   2   2   1
3   3   3   1
4   4   4   1
5   5   5   1
6   6   6   1
7   7   7   1
8   8   8   1
9   9   9   1
10  10  14  5
20  15  24  10
30  25  34  10
40  35  44  10
50  45  54  10
60  55  65  11
70  66  74  9
80  75  84  10
90  85  94  10
100 95  100 6
4.4 bin->bak2

bak2 (定义于 ibin.h,实现于 idbak2.cpp)是 bak 的变形,也是以减精度来表示区间。但与 bak 不同的是,它将 bak 的每个区间区分为两个区间:小于减精度数的区间,和大于等于减精度数的区间。虽然注释中这样说,但实现时 bak2 是将 bak 的区间分为了三个:小于、等于和大于。下面是一个对 1-100 的数据列建立 bak2 索引的输出,每列的含义与 bak 中示例相同:

index (equality encoding on reduced precision values) for data.a contains 37 bitvectors for 100 objects
1   1   1   1
2   2   2   1
3   3   3   1
4   4   4   1
5   5   5   1
6   6   6   1
7   7   7   1
8   8   8   1
9   9   9   1
10  10  10  1
10  11  14  4
15  15  19  5
20  20  20  1
20  21  24  4
25  25  29  5
30  30  30  1
30  31  34  4
35  35  39  5
40  40  40  1
40  41  44  4
45  45  49  5
50  50  50  1
50  51  54  4
55  55  59  5
60  60  60  1
60  61  65  5
66  66  69  4
70  70  70  1
70  71  74  4
75  75  79  5
80  80  80  1
80  81  84  4
85  85  89  5
90  90  90  1
90  91  94  4
95  95  99  5
100 100 100 1

除了上面几个算法之外,扩展的算法还有 roster 和 keywords,这两种算法比较复杂,这里就不示例讲解了。

五、多层 bitmap 索引算法

有了几个基础的 bitmap 索引算法,我们就可以考虑将这些算法组合成一个层次的结构,构造出多层的 bitmap 索引算法。下面的几个算法,即是由前面的基础 bitmap 索引算法构造出来的二(多)层 bitmap 索引算法。

5.1 bin->ambit

ambit(定义于 ibin.h,实现于 ixambit.cpp)是 multilevel-range based算法,在这个算法中索引分为多层,每层索引都是基于 range 的索引。具体实现时,fastbit 首先构造 bin,然后对桶进行分组(调用 bin::divideBitmaps),然后构造 ambit。分组粒度可以由命令行传入参数 ncoarse=x 和/或 nrefine=n 指定,否则由一简单算法确定,确定分组个数的算法为(第一个桶不参与分组):

ixambit.cpp:
33     // the default number of coarse bins is determined based on a set
34     // of simplified assumptions about expected sizes of range encoded
35     // bitmaps and word size being 32 bits.
36     const uint32_t defaultJ = static_cast
37         (nbins < 100 ? sqrt((double)nbins) :
38          0.5*(31.0 + sqrt(31.0*(31 + 4.0*nbins))));

下面看一个实际的例子,左侧是对 1-100 的数据列构造的 bin,右侧是基于该 bin 构造的 ambit:

ambit 索引

5.2 bin->pale

pale(定义于 ibin.h,实现于 ixpale.cpp)是 two-level binned equality-range算法,它的索引分为两层,第一层为 binned equality(bin) 索引,第二层为 range 索引。在具体实现时,pale 首先构造 bin,然后对桶进行分组(调用 bin::divideBitmaps),然后构造 pale。与 ambit 相同,分组粒度可以由命令行传入参数 ncoarse=x 和/或 nrefine=n 指定,否则当 bin 桶数大于31时,默认第一层为16个组:

ixpale.cpp:
45     else { // default -- 16 coarse bins
46         if (nbins > 31) {
47         j = 16;
48         }
49         else {
50         j = nbins;
51         }
52     }

下面看一个实际的例子,左侧是对 1-100 的数据列构造的 bin,右侧是基于该 bin 构造的 pale:

pale 索引

5.3 bin->pack

pack(定义于 ibin.h,实现于 ixpack.cpp)是 two-level binned range-equality 算法。它的索引分两层,与 pale 相反,第一层为 range 索引,第二层为 binned equality(bin) 索引。具体实现时,fastbit 首先构造 bin,然后对桶进行分组(调用bin::divideBitmaps),然后构造 pack。分组粒度可以由命令行传入参数 ncoarse=x 和/或 nrefine=n 指定,否则当bin桶数大于63时,默认第一层为31个组:

ixpack.cpp:
44     else { // default -- 31 coarse bins
45         if (nbins > 63) {
46         j = 31;
47         }
48         else {
49         j = nbins;
50         }
51     }

下面看一个实际的例子,左侧是对 1-100 的数据列构造的 bin,右侧是基于该 bin 构造的 pack:

pack 索引

5.4 bin->zone

zone(定义于 ibin.h,实现于 ixzone.cpp)是 two-level binned equality-equality 算法,它的索引分两层,两层均为 binned equality(bin) 索引。它的实现方式也是首先构造 bin,然后对桶进行分组(调用 bin::divideBitmaps),然后构造 zone。其分组粒度可以由命令行传入参数 ncoarse=x 和/或 nrefine=n 指定,否则当bin桶数大于31时,默认第一层为14个组:

ixpack.cpp:
46     else { // default -- 14 coarse bins
47         if (nbins > 31) {
48         j = 14;
49         }
50         else {
51         j = nbins;
52         }
53     }

下面看一个实际的例子,左侧是对 1-100 的数据列构造的 bin,右侧是基于该 bin 构造的 zone:

zone 索引

5.5 bin->fuge

fuge(定义于 ibin.h,实现于 ixfuge.cpp)是 two-level binned interval-equality 算法,fuge 为德语中 interstice 的表述。fuge 的索引分两层,第一层为 interval(mesa) 索引,第二层为 binned equality(bin) 索引,它也是采用首先构造 bin,然后基于 bin 构造 fuge 的方式。其分组粒度由 ncoarse=x 指定,否则默认的分组个数由下面算法确定:

ixfuge.cpp:
887     // default size based on the size of fine level index sf: sf(w-1)/N/sqrt(2)
...
899     if (ncoarse < 5U && offset32.back() >
900     offset32[0]+static_cast(nrows/31)) {
901     ncoarse = sizeof(ibis::bitvector::word_t);
...
913     else {
914         ncoarse = ncmax;
915     }
916     }

下面看一个实际的例子,左侧是对 1-100 的数据列构造的 bin,右侧是基于该 bin 构造的 fuge:

fuge 索引

5.6 relic->bylt

bylt(定义于 irelic.h,实现于 ixrelic.cpp)是 two-level unbinned range-equality 算法,bylt 是丹麦语的 pack(binned 版本算法)。bylt 索引分两层,第一层为 range 索引,第二层为 unbinned equality(relic) 索引。在实现时首先构造 relic,然后对桶进行分组(调用bin::divideBitmaps),然后构造 bylt。分组粒度可以由 ncoarse=x 指定,bylt 保证每组中桶数是大致均匀的,否则由下面算法决定分组的个数:

ixbylt.cpp:
182     // default size based on the size of fine level index sf:
183     // (w-1) * sqrt(sf*(sf-N/(w-1))) / (2N)
184     if (ncoarse < 5U && offset64.back() > offset64[0]+(int32_t)(nrows/31U)) {
185     ncoarse = sizeof(ibis::bitvector::word_t);
     const int wm1 = ncoarse*8-1;
...
199         ncoarse = ncmax;
200     }
201     }

下面看一个实际的例子,左侧是对 1-100 的数据列构造的 relic,右侧是基于该 relic 构造的 bylt:

bylt 索引

5.7 relic->fuzz

fuzz(定义于 irelic.h,实现于 ixfuzz.cpp)是two-level unbinned interval-equality 算法,即 fuge 的 unbinned 版本,名字起源于 fuzzy 聚类/分类。fuzz 索引分两层,第一层为 interval(mesa) 索引,第二层为 unbinned equality(relic) 索引,具体实现时 fastbit 也是采用首先构造 relic,然后构造 fuzz 的方式。其分组粒度可以由 ncoarse=x 指定,否则默认分组个数由下面算法确定:

ixfuzz.cpp:
168     // default size based on the size of fine level index sf: sf(w-1)/N/        sqrt(2)
169     if (ncoarse < 5U && offset64.back() > offset64[0]+nrows/31U) {
170     ncoarse = sizeof(ibis::bitvector::word_t);
...
182     else {
183         ncoarse = ncmax;
184     }
185     }

下面看一个实际的例子,左侧是对 1-100 的数据列构造的 relic,右侧是基于该 relic 构造的 fuzz:

fuzz 索引

5.8 relic->zona

zona(定义于 irelic.h,实现于 ixzona.cpp)是 two-level unbinned equality-equality 算法,zona 是丹麦语的zone(binned 版本算法),其索引分两层,两层均为 unbinned equality(relic) 索引。首先构造 relic,然后对桶进行分组构造zona,分组个数默认为11个。下面看一个实际的例子,左侧是对 1-100 的数据列构造的 relic,右侧是基于该 relic 构造的 zona:

zona 索引

六、多成分 bitmap 索引

多成分(multi-component)bitmap 索引[3]是使用一组基数将数据值分解成多个部分,分别对每个部分进行 bitmap 索引的方案。原理描述如下:给定 n-1 个基数 { bn-1, bn-2, ..., b1},那么一个值 v 可以通过下式分解为 {vn, vn-1, ..., v1}:

数据值的分解

这和数的表示法类似,如果令 bi 都是 10,那么 vi 就是十进制表示法中第 i 位的值(大于等于0,小于10)。更准确的表述可以参考[3]。下面我们来看 fastbit 中的几个实现。

6.1 relic->fade

fade(定义于 irelic.h,实现于 ifade.cpp)是 multicomponent range-encoded 算法,即在每个部分中,是使用的 range 索引。下面来看一个 range-encoded 的例子:

fade 索引

在(b)图中,选择的基数是 9,那么索引就变成了一个单成分的 range 索引算法;在(c)图中,选择的基数是 <3, 3> 这样一个双成分编码,对分解出来的每个成分(大于等于0,小于3)生成 range 索引,就得出了 (c) 图中的结果。

6.2 relic->fade->sapid

sapid(定义于 irelic.h,实现于 isapid.cpp)是 multicomponent equality-encoded 算法,即在每个部分中是使用的 equality(relic) 索引。下面来看一个 equality-encoded 的例子:

sapid 索引

在(b)图中,选择的基数是 <3, 4> 这样一个双成分编码,对分解出来的每个成分生成 relic 索引,就得到了 (b) 图中的索引结果。

除了这两个索引算法之外,还有 sbiad(multicomponent interval-encoded),egale(multicomponent equality code on bins), entre(multicomponent interval code on bins), moins(multicomponent range code on bins)这几个索引算法。从括号中我们可以大致猜出这些索引的实现方式,但是由于我们现在没有一个很好的示例展现方式,用实际用例来展现这些索引算法的效果将会留给以后的文章进行。

七、总结

这篇文章基于 fastbit 软件包,加以实际的用例对常用的 bitmap 索引算法进行了一个较为系统的介绍。不过生成 bitmap 索引仅仅是第一步,bitmap 索引在存储时会有很大的开销,在不损害(较少损害)查询效率的情况下,对 bitmap 索引进行有效的压缩是一个非常有挑战性的课题。除了 bitmap 索引的生成和存储之外,在不同类型的 bitmap 索引上实现高效的各种类型的查询,也是一个值得进一步探讨的问题。我们很高兴地看到 fastbit 软件包实现了很多这些相关领域的算法,为我们提供了非常宝贵的资料。

参考文献

[1] C-Y. Chan and Y. E. Ioannidis, An efficient bitmap encoding scheme for selection queries, in Proceedings of the ACM international conference on Management of data (SIGMOD), 1999.
[2] P. O’Neil and DalIan Quass, Improved Query Performance with Variant Indexes, in Proceedings of the ACM international conference on Management of data (SIGMOD), 1997.
[3] C-Y. Chan and Y. E. Ioannidis, Bitmap Index Design and Evaluation, in Proceedings of the ACM international conference on Management of data (SIGMOD), 1998.

使用 screen 命令的一些小技巧

2010年7月22日 Solrex Yang 5 条评论

由于工作环境的问题,最近越来越感觉到 screen 命令的可贵,下面总结一点使用 screen 命令的小技巧。

最常用的参数组合:

screen -ls // 列出已有的 screen
screen -D -R // 进入指定的 screen 名,如果没有,则以该名称创建 screen

由于很常用,我把这两个命令取了个 alias:

alias sl='screen -ls'
alias sr='screen -D -R'

除了命令之外,还有快捷键 Ctrl+ac 创建 screen;Ctrl+aa 在两个 screen 之间相互切换;Ctrl+ad 从 screen 中 detach;Ctrl+a数字,跳转到数字指代的 screen。

在 screen 最下方显示状态栏,状态栏包括已经打开的 screen 标签列表,当前的 screen 和时间。其中在 screen 标签处显示该 screen 所处的目录名。显示 screen 所处的目录名这一点实现起来要困难一些,首先得修改 .bashrc,加入 screen term 对应的信息

case $TERM in
    screen*)
        # This is the escape sequence ESC k \w ESC
        # Use current dir as the title
        SCREENTITLE='\[\ek\W\e\\\]'
        PS1="${SCREENTITLE}${PS1}"
        ;;
    *)
        ;;
esac

然后 . 或者 source 一下,再修改 screen 的配置文件,添加状态栏,在 .screenrc 中添加:

caption always '%{=b cw}%-w%{=rb db}%>%n %t%{-}%+w%{-b}%< %{= kG}%-=%D %c%{-}'
shelltitle '$ |bash'

最终效果为:

GNU Screen 多标签状态栏

删除 MBR 引发的诡异问题

2010年6月14日 Solrex Yang 7 条评论

我要跟女友交换一下笔记本电脑,她不常用 Linux,而我的 Ubuntu 分区占了好几十 G 空间,因此我想还是删了再给她吧。

我的电脑有两个系统:Windows Server 2008 和 Ubuntu 10.04。按照惯常的思维,删除 Ubuntu 只需要先格式化 MBR,然后删除 Ubuntu 分区即可。因为手头没有 DOS 启动盘,我想到一键恢复的硬盘版是带 DOS 工具的,就在 Windows 下装了个一键恢复硬盘版,然后进 DOS 命令行 “fdisk /mbr”。

可谁知道做完这些之后,MBR 是清掉了,但系统无法启动了,提示消息是这样的:

Windows 未能将启动原因可能是最近更改了硬件或软件
文件:\Windows\system32\winload.exe
状态:0xc000000e
信息:无法加载所选项,因为应用程序丢失或损坏。
...

然后我就傻眼了,从来没有遇到过这种情况呀!搜索了一番之后,才明白了这是什么意思。

Windows Vista 之后的系统,不再使用 boot.ini 保存启动菜单,而是使用一种叫做 BCD(Boot Configuration Data)机制来管理启动菜单,其默认的配置文件是活动分区(一般是 C:\)的 \Boot\BCD。简单的来说,可以将 \Boot\BCD 文件看成是 GRUB 的 menu.lst(grub.conf)文件,里面储存着系统装载程序的路径和参数等。

在我这里,出现上面问题的原因是 BCD 每项记录中的 device 选项被“一键恢复硬盘版”改成了 unknown,这样启动程序不知道到哪里去找系统的装载程序,自然也就无法启动了。使用 bcdedit /store C:\Boot\BCD 可以查看系统的 BCD 每项记录。(较为诡异的是,在没有删除 MBR 之前,我是如何进入到启动项里的?)

我用的解决方法是把所有默认启动项中的 unknown 改成了 boot。还得依靠工具,使用 WinPE U 盘(DOS 启动盘未尝试)启动,进入 C:\Windows\System32\,执行 bcdedit 命令:

bcdedit /store C:\Boot\BCD /set {default} osdevice boot
bcdedit /store C:\Boot\BCD /set {default} device boot
bcdedit /store C:\Boot\BCD /set {default} detecthal 1

然后就可以启动进入 Windows 了。

分类: Linux 标签: , , , ,

南京大学学位论文 LaTeX 模板

2010年5月9日 Solrex Yang 9 条评论

由于女朋友要写毕业论文,一些前人写的模板我不熟悉,并且摘要格式好像都不符合南京大学研究生院要求,所以我就在中科院学位论文模板的基础上给她改了一个南大研究生学位论文 LaTeX 模板。主要工作是将封面和摘要格式都改成符合南大研究生院给的样张格式,实话说,花了不少工夫。

既然模板都已经写好,后来我干脆又完善了一下,添加了博士毕业论文需要的国家图书馆论文封面。这样大概可以作为一个完整的,包含硕士和博士毕业论文格式的南大研究生学位论文 LaTeX 模板包,我将这个 LaTeX 模板释出在下面这个地址:
http://share.solrex.org/njuthesis/,或者Google Code页:http://njuthesis.googlecode.com/
下面是一个 flash 的预览:
http://share.solrex.org/njuthesis/template-preview.swf

有需要的同学可以去下载,最起码可以作为一个修改的基础,希望这些工作能够对别人有所助益。我会尽量地维护这个模板,所以如果有哪位校友觉得有不完善的地方,可以和我联系,我会修正相应的缺点。

支持多浏览器的网站变灰方法

2010年4月21日 Solrex Yang 2 条评论

这篇文章中给出了针对 IE 浏览器的使网站变灰的方法,具体做法是在 CSS 文件的开头添加这样一行:

html { filter:progid:DXImageTransform.Microsoft.BasicImage(grayscale=1); }

但是很遗憾这种方法并不能支持 Firefox 和 Chrome。这篇博客介绍一种支持各种浏览器的网站变灰方法,试验其支持 Firefox、Chrome 和 IE,据说可以支持 Opera(未测试),方法比较简单,就直接介绍步骤了。

1. 到这里下载 grayscale.js 文件到你网站的根目录(或者也可以不下载,直接引入该 js,未测试)。

2. 在网站的 footer 或者 header 等全局的文件中插入以下代码(注意,$() 和 .load handler需要 jquery.js 的支持,不使用 jquery 的同学可以自行搜索解决标签查找和 window onload 事件处理问题,例如这里这里):

<script type="text/javascript" src="/grayscale.js"></script>
<script type="text/javascript">
$(window).load(function () {
  grayscale( $('body') );
});
</script>

该方法的缺点是:

1. 页面加载完后才对整个页面进行变灰操作;
2. 在非 IE 浏览器中不支持来自其它域名的网站图片的变灰;
3. 造成非 IE 浏览器在加载完页面后进行大量 js 计算,该计算负担可以通过仅变灰 img, a 等标签而不是 body 来优化。

一些论文相关 LaTeX 技巧

2010年4月14日 Solrex Yang 5 条评论

最近在写毕业论文,记录一下使用 LaTeX 排版时的一些笔记:

1. 正文英文使用 Times * 字体:

\usepackage{times}

2. 自定义列表样式

\usepackage{enumitem}
% 例:缩略语列表,缩略语大写,全称左侧缩进对齐
\begin{description}[font=\textbf, labelindent=2em, leftmargin=6em, style=sameline]

\item[CA] Central Authority.

\end{description}

% 例:列表标签使用 *) + 元素中段落首行缩进
\begin{enumerate}[label=\alph*)]
\setlength{\parindent}{2em}

\item 测试标签

测试段落

\end{enumerate}

3. 想使用列表但不希望列表中段落整体有缩进

% 自定义 minisection 命令,小标题,无编号
\newcommand\minisection[1]{\vspace{2ex}{\heiti #1}\vspace{1ex}}
% 例
\minisection{1)测试小标题}

4. vim 中使用 gqap 命令对中文文本格式化

下载 vim 插件:http://info.sayya.org/~edt1023/vim/format.vim

5. 按行方向合并单元格

\usepackage{multirow}

6. 拼凑中文生僻字

\hbox{\scalebox{0.4}[1]{王}\scalebox{0.6}[1]{莹}}

7. 为插图加框

\fbox{\includegraphics[width=0.9\textwidth]{figname}}

8. 设置 pdf 属性,设置参考文献链接和图片链接颜色为黑色

\hypersetup{linkcolor=black, %
            citecolor=black, %
             pdftitle={Title}, %
            pdfauthor={Name}, %
           pdfsubject={Subject}, %
          pdfkeywords={Key words}}

9. 对某页分栏排版

\usepackage{multicol}
% 分两栏
\begin{multicols}{2}

不可用于浮动环境。

\end{multicols}

10. 中文 LaTeX 编译 Makefile(注意缩进应替换为制表符)

ARTICLE=filename
ARTICLE_SRC=$(ARTICLE).tex $(ARTICLE).bib CASthesis.cls CASthesis.cfg
IMAGES=

all: article

article: $(ARTICLE).pdf

$(ARTICLE).pdf: $(ARTICLE_SRC) $(IMAGES)
    latex $(ARTICLE).tex
    bibtex $(ARTICLE)
    latex $(ARTICLE).tex
    gbk2uni $(ARTICLE)
    latex $(ARTICLE).tex
    dvipdfmx $(ARTICLE).dvi

clean:
    rm -f *.aux *.toc *.lon *.lor *.lof *.ilg *.idx *.ind *.out *.log *.exa
    rm -f *.nav *.snm *.bbl *.blg *.spl *.lot *.bak *~

distclean: clean
    rm -f *.pdf *.dvi *.ps

11. IEEE 投稿 LaTeX 编译 Makefile,主要处理嵌入字体和 pdf 版本问题(注意缩进应替换为制表符)

ARTICLE=filename
ARTICLE_SRC=$(ARTICLE).tex

#DVIFLAGS=-t [letter/a4]
DVIFLAGS=
# To avoid fonts and pdfinfo problems with the pdf file.
#PS2PDFFLAGS=-sPAPERSIZE=letter -dCompatibilityLevel=1.4 \
-dPDFSETTINGS=/prepress -dMaxSubsetPct=100 -dSubsetFonts=true \
-dEmbedAllFonts=true
PS2PDFFLAGS=-dCompatibilityLevel=1.4 -dPDFSETTINGS=/prepress \
-dMaxSubsetPct=100 -dSubsetFonts=true -dEmbedAllFonts=true

all: article

article: $(ARTICLE).pdf

$(ARTICLE).pdf: $(ARTICLE_SRC) $(IMAGES)
    latex $(ARTICLE).tex
    latex $(ARTICLE).tex
    dvips $(DVIFLAGS) $(ARTICLE).dvi
    ps2pdf $(PS2PDFFLAGS) $(ARTICLE).ps

test: $(ARTICLE).pdf
    @echo "****************************************************************************"
    @echo "** IMPORTANT: PDF version should be 1.4!!!                                **"
    @echo "****************************************************************************"
    @pdfinfo $<
    @echo "****************************************************************************"
    @echo "** IMPORTANT: All Type 1 and Type 1C fonts should be embeded!!!           **"
    @echo "****************************************************************************"
    @pdffonts $<

clean:
    rm -f *.aux *.toc *.lon *.lor *.lof *.ilg *.idx *.ind *.out *.log *.exa
    rm -f *.nav *.snm *.bbl *.blg

distclean: clean
    rm -f *.pdf *.dvi *~ *.ps

分类: IT 标签: , , ,

我的京东换货经历

2010年4月4日 Solrex Yang 3 条评论

京东上也买过不少东西,这两天第一次体验了京东的售后客服,如实记录一下经历:

2010 年 4 月 2 日 22:09,在京东下了买电熨斗的单;

2010 年 4 月 3 日 08:39,收到订单已到达自提点的手机短信通知;

2010 年 4 月 3 日 10:55,在石景山自提点付款提货(必须先付款才能检查物品),拿到货物检查发现有问题,电熨斗有明显使用过的痕迹。当场提出换货,被告知自提点只负责提货,不负责售后,需要自己回去与客服联系;

2010 年 4 月 3 日 11:44,由于网上订单仍然显示未完成,无法提交返修申请,于是拨打京东客服 400 电话询问了一下情况,客服小姐的回答仍是等订单显示完成后在网上提交返修单;

2010 年 4 月 3 日 15:56,网上订单显示已完成;

2010 年 4 月 3 日 16:26,提交返修单,返修类型:换货,问题描述:电熨斗被使用过: 1. 电熨斗水箱内有残留水珠; 2. 电熨斗尾部下方支撑脚有磨损和污迹; 3. 电熨斗中部塑料转盘有破损; 4. 电熨斗插头周围有污迹;

2010 年 4 月 3 日 16:54,收到返修已生成换货新单的手机短信通知;

2010 年 4 月 4 日 09:46,接到快递电话,更换了新品。

说实话,事情顺利地挺出乎我意料的。因为从论坛上还有其它网站看到很多对京东售后的抱怨,本来有做好长期抗战的心理准备,没想到那么容易就把问题解决了,这件事情的处理我还是很满意的。当然,这只是个案,我只如实记录个人遭遇,不参与对京东客服整体质量的讨论。

使用无线自组网共享互联网接入

2010年4月1日 Solrex Yang 5 条评论

在这个互联网已经渗透入每个角落的时代,GUCAS 的网络接入收费显得非常不合时宜:(二年级以上同学)每个月 25 元套餐,其中包括5G国内流量、2G 国际流量,国内流量超出部分按 10元/G 收费,国际超出部分按 1元/M 收费。幸好有了 IPv6 BT(IPv6 流量不计入收费流量),不然 GUCAS 的网络就是一个悲剧。

对于高年级同学来说稍微好一点的是,某些实验室会有免费的网络,还可稍解流量窘迫。但如果不下 BT、不看视频、不视频聊天的话,5G 每月也是用不完的,而且 25 元的价钱也并不便宜,所以很多高年级同学都是宿舍共用一个上网帐号。

共用上网帐号有几种方式:1> 使用路由器,这是比较简洁的方式,但是这样就无法使用 IPv6——目前的路由器一般不支持 IPv6 路由功能,因此需要使用特别的配置才能同时支持 IPv6;2> 使用代理,一台电脑作为主机,为另一台电脑开一个代理,这种方式可以使用 IPv6,但缺点是主机分配的 IP 可能变化,需要手动更新代理地址,而且部分软件并不能完美支持代理。

我这里尝试了另外一种方式,使用无线自组网共享互联网接入,只适用于两台都带无线网卡的电脑共享上网。简单点儿来说,就是让两台电脑无线网卡相连,其中一台(主机)将有线的互联网接入通过无线链路共享给另外一台(从机)。这种方案同样适用于那些家里有两台笔记本却只有一个网口的家庭,省了买路由器的钱。

注:下文使用操作系统平台为 Windows XP SP3。

首先,需要将主机的有线连接即“本地连接”设为可共享。具体方法是,在主机上右击“本地连接”,选择“属性”,进入“高级”选项卡,“选中 Internet 连接共享”中的两个复选框,即“允许其他网络用户通过此计算机的 Internet 连接来连接”和“允许其他网络用户控制或禁用共享的 Internet 连接”。

其次,将两台电脑进行无线自组网。无线自组网的建立方式根据网卡管理软件的不同可能有不同的方法,简单的来说就是建立一个两台笔记本之间的一个点到点的(不需要接入点的)无线连接。一个典型的配置是:

主机:
Ethernet adapter 无线网络连接:

        Connection-specific DNS Suffix  . :
        IP Address. . . . . . . . . . . . : 192.168.0.1
        Subnet Mask . . . . . . . . . . . : 255.255.255.0
        Default Gateway . . . . . . . . . :
从机:
Ethernet adapter 无线网络连接:

        Connection-specific DNS Suffix  . : mshome.net
        IP Address. . . . . . . . . . . . : 192.168.0.27
        Subnet Mask . . . . . . . . . . . : 255.255.255.0
        Default Gateway . . . . . . . . . : 192.168.0.1

需要注意的一点是,两台电脑需要在同一个 Windows 工作组中,这里两台电脑的工作组都是 MSHOME。

如果对无线自组网不是很了解,以上两步可以通过软件来进行设定。联想有一款叫做“闪联任意通”的软件,配置起来相对简单好用,用它的“共享网络”功能即可做到以上两步。但是闪联任意通有一些BUG,会造成服务占用 CPU 过高。一旦配置成功之后,记住建立的点到点网络 SSID(一般以 PAN_ 开头),以后每次两台电脑都连接至该网络即可。不再需要闪联任意通的运行。

最后,将从机有线接口的 IPv4 功能禁用,将从机无线接口的 IPv6 功能禁用。具体方法是,在从机上右击“本地连接”,选择“属性”,在“常规”选项卡中的“此连接使用下列项目”选择框中的“Internet 协议 (TCP/IP)”前的复选框去掉,确定退出;在从机上右击“无线网络连接”(也可能是其它名称),在类似于上面的位置找到“Microsoft TCP/IP 版本 6”,去掉前面的复选框。

经过上面三步之后,就可以保证从机的 IPv4 的流量会走无线接口,使用主机共享的 IPv4 互联网接入;而 IPv6 的流量会走有线接口,使用本地交换机提供的 IPv6 互联网接入。第二步建立的点到点网络会保存到无线网络配置中,两台电脑开机只要打开无线,一般就会自动连接到该点到点网络,无需再进行额外的手动修改。(即便是使用代理的同学,由于无线的 IP 不会变化,这样做也能带来不用修改代理地址的好处。)

综上,这种方式可以完美地实现共享 IPv4 接入而又不影响 IPv6 连接。

注册 Google Voice 的曲折经历

2010年3月27日 Solrex Yang 16 条评论

昨天 iron-feet 同学给我讲了不少 Google Voice 的好处,搞得我也心痒痒的(技术男的通病),想去注册一个。但后来发现只有收到邀请才能注册,Google 官方的邀请可能要等很长时间,于是我就在 Twitter 上发推求邀请,非常感谢好心的 @liyong3 同学(blog),马上就给我发了邀请。

前面是好运,下面就是悲剧的开始。在注册之前我也知道注册 Google Voice 的麻烦之处:不允许中国网络访问,要有出国代理或VPN;激活时不允许绑定非美国号码,要申请到一个虚拟的美国号码,并转发电话到自己的聊天软件。我就是在激活上出了问题。

在目前网上流传的几个可以申请美国号码的服务里:ipkall 注册时无论如何都会出现密码错误;Gizmo 已经被 Google 收购,目前不提供注册;Freedigits 早就不提供注册了。所以从我的体验来看,能用的就只剩下:http://www.virtualphoneline.comhttp://www.groovytel.com 了。其实这两家网站的页面风格一模一样,很可能是一个公司的产品。

http://www.virtualphoneline.com 之所以流行跟谷奥那篇介绍注册 Google Voice 的文章有关,但是 virtualphoneline 注册的免费虚拟号码只能试用 24 天,不过能以更多的形式(10 种)转发来电;至少从声明上来看,http://www.groovytel.com 要好一些,免费号码能试用 3 年,但是转发形式少了(6种)些。不过还好他们都支持转发到 Gtalk。

我一开始就注册了这两个服务的号码,也设置了转发到 Gtalk。但在 Google Voice 里尝试打了几十次激活电话,也没收到一个来电。于是我就对网上流传的各种方案进行尝试,包括网上没有的方案。整整尝试了一下午,才收到了那么几次 virtualphoneline+nonoh 的电话,但是很悲剧,nonoh 的拨号盘不能配合 virtualphoneline 输入认证码。于是我只好无奈放弃了。

到了晚上 11 点多我看 Google Reader 的时候,心仍有些不甘。看了一个视频,发现别人 Gtalk 有个联系人叫做 service@gtalk2voip.com,我顺手也加上了。然后将 groovytel 改回转发到 Gtalk,看文章的时候一会儿过去打一下,一会儿过去打一下,没想到还真给我打通了。按照网上的方法,先输一个数字,回车,再输一个数字,回车,就通过了验证。

总结一下下午激活失败的可能原因:

1. 没有加 service@gtalk2voip.com 机器人为联系人。其实我不确定这个有没有用,反正加个机器人也不麻烦。

2. 可能在通话的高峰期打电话。从我看 groovytel 和 virtualphoneline 的通话记录,发现很多通话没应答只持续 5 秒钟。我不知道是不是意味着如果 5 秒钟没有接通 Gtalk 他们就放弃了连接。按说北京的下午在美国是凌晨呀,不应该是高峰的。

反正不管怎样,结果表明还是多尝试好,要有耐心,反正 Google Voice 貌似也没有限制可以尝试多少次,多次尝试打不通就换个时间打。我在 Twitter 上也碰到和我一样没能激活的好友,也许大家可以借鉴一下这里的经验。

分类: IT 标签: , , ,

有关 SVN、Cygwin 和 Notepad++

2010年3月25日 Solrex Yang 3 条评论

1. svn 的访问控制

很久以前我就自己配置过 svn 服务器,但总是不能访问成功。到最后还是使用文件系统(即用 file:/// 而不是 svn:// 或其它)访问 svn 仓库,因为自己建立的文件系统不需要认证。

今天我又尝试琢磨了一下我 svn 仓库的设置,才发现之前没配置成功的原因:svn 对用户的权限默认是关闭的。因此当我设置了用户名密码,svn ls 时得到的提示信息仍然是:

svn: Authorization failed

时,我就糊涂了,我的用户名密码没错呀,为啥还是Authorization failed?我还以为是密码设置有问题,没想到除了用户名以外,还得给用户配置访问列表(ACL),否则就什么都访问不了。说简单点儿就是 svn 用户访问控制是一个白名单机制,而我当成了黑名单机制。

知道了错误原因,就很简单了。到与 svnserve.conf 同目录下的 authz 为对应用户添加可以访问的项目就可以了。

2. cygwin 的启动速度

最近发现 cygwin 的启动速度大大变慢,一个终端起来至少要 30 秒。而且不仅仅是启动,所有程序的运行速度都变慢了,比如文件名补全竟然需要好几秒!我忍了很久,就差卸了重装它了,只是想到好不容易配好的各种环境,给忍耐了下来。

今天琢磨了一下 cygwin 的启动过程,发现可以在 bash 命令后加 -x 参数打印所有执行的命令。于是把启动 log 打印出来,查找到引起运行变慢的罪魁祸首:bash_completion。我之前装了一个名叫 bash_completion 的包所谓命令补全的增强包,好家伙,在 /etc/bash_completion.d/ 下面添加了 144 个文件。在启动的时候要一个个 source 这些脚本,怪不得慢呢!

卸载掉这个 bash_completion 包后,cygwin 的运行速度回到了原来的水平,敲命令的时候总算不用憋屈地等补全了。

3. Notepad++ 的中文搜索

使用 2.6.8 版本时,又发现无法搜索中文的 bug。我非常搞不懂 Notepad++ 的作者怎么维护软件质量的,这 bug 在我的印象里就反覆出现两次了。这样的bug都不写一个回归测试用例来检查,实在是有点儿不可思议。无奈之下只好退回到 2.6.7 版本了。

分类: IT 标签: , , , ,