??xml version="1.0" encoding="utf-8" standalone="yes"?>
原文地址Q?a >http://blog.aulin.no/compiling-sphinx-110beta-on-windows
下面是引导大家如何在windows上编译sphinx 1.10beta
1. 下蝲sphinx源码(http://sphinxsearch.com/downloads/sphinx-1.10-beta.tar.gz)
注:(x)最新版本在Q?a >http://sphinxsearch.com/downloads/archive/ ?br />
2. 因ؓ(f)sphinx使用到MySQL, LibExpat and LibIConv,因此在编译之前需要配|这些库Q?br /> 下蝲MySQL的开发环?a >http://dev.mysql.com/get/Downloads/MySQL-5.1/mysql-5.1.52-win32.msi/from/http://mysql.borsen.dk/Q安装开发组?br /> 下蝲LibExpat(http://garr.dl.sourceforge.net/project/expat/expat_win32/2.0.1/expat-win32bin-2.0.1.exe)
下蝲LibIConv (http://netcologne.dl.sourceforge.net/project/gnuwin32/libiconv/1.9.2-1/libiconv-1.9.2-1.exe)
3. 在shpinx.h中可以配|和U除sphinx需要的lg,如可以移除对PostgreSQL 的支?/p>
4. 在visual studiao 08 中打开 Sphinx08.sln
5. dmysql 的include路径(C:\Program Files (x86)\MySQL\MySQL Server 5.1\include) to all projects (叛_ - Properties - Configuration Properties - C/C++ - General - Additional Include Directories).
6. dmysql的lib路径(C:\Program Files (x86)\MySQL\MySQL Server 5.1\lib\opt) to all projects excluding "libsphinx" (叛_ - Properties - Configuration Properties - Linker - General - Additional Library Directories)
7. 在除?jin)libsphinx的所有工E中Q添加LibExpat的\?C:\Program Files (x86)\Expat 2.0.1\Bin)(叛_ - Properties - Configuration Properties - Linker - General - Additional Library Directories)
8. 在除?jin)libsphinx的所有工E中Q添加LibIConv 的\?C:\Program Files (x86)\GnuWin32\lib)(叛_ - Properties - Configuration Properties - Linker - General - Additional Library Directories)
9. ~译B(ti)uild! (F6)
shuffle法Q我把他叫做z牌法Q它的目标正好与各种的sort法相反Q即把一个有?或者无?的一pd元素打ؕQ以满需求?br>
举个两例子,大家都知道扑克牌Q我们每ơ都需要在摸牌之前把牌z掉Q用来让每个人摸到每张牌的概率尽量相{,增加游戏的随机性和乐趣Q还有音频播攑֙Q有一些h不喜Ƣ顺序播放,而喜Ƣ用随机播?其实随机播放分ؓ(f)两种,random和shuffleQ后文会(x)介绍?Q比如iPod Shuffle的卖点之一是“你永q不知道你将要听到的下一首歌曲是什?#8221;。至,如果要模拟扑克牌游戏Q或者做音频播放器,都要使用shuffle法Q而二者的shuffle法却有一些区别,一个是一ơ性的z牌Q另一个则是每ơ取一首歌。那么怎么实现他们呢?
扑克牌的shuffle法Q?br>
下面Z(jin)方便和容易读懂,我都用扑克牌来作例子Q桌上有n张牌Qƈ且对桌子上的牌进行标P?直到n-1。我们的目的是洗q些牌?br>
一个比较容易想到的Ҏ(gu)是,桌子上有n张扑克牌Q我Wiơ从桌子上等概率随机取一张扑克牌Q作为洗牌后牌堆的第i张扑克牌Q那么这个算法实现v来应该是q样的:(x)
伪代码:(x)
for i <- 0 to n - 1
do d <- Random mod (n - i)
shuffle[i] <- deck[d]
deck[d] <- deck[n - i]
其中Qdeck是洗牌前的序?0~n-1)Qshuffle是洗牌后的序?0~n-1)Q第i??开始数)在剩下的n-i张牌里等概率的取一张牌Q把它放到shuffle里。而deck[d] = deck[n - i]q句辑ֈ的效果是删除取过的牌?br>
q个Ҏ(gu)的时间复杂度是O(n)Q已l可以接受了(jin)Q但q个Ҏ(gu)q(sh)够好Q因为我们需要两个长度ؓ(f)n数组。其实可以很Ҏ(gu)得得C面的Ҏ(gu)Q解决空间的问题Q?br>伪代码:(x)
for i <- 0 to n - 1
do d <- Random mod (n - i)
swap(deck[d], deck[n - i])
q样Q这个算法的道理有些像选择排序?jin),Wi??开始数)定Wn-i个元素的原位|,q且交换两个位置上的元素。它的复杂读仍然是O(n)Q而只需?个额外的I间来储存(sh)换用的(f)时变量?br>q个Ҏ(gu)已经是一个比较好的解x(chng)法了(jin)(自己认ؓ(f))Q如果你q能写出更好的shuffle法Q请告诉我?br>
我相信对z牌q种东西有了(jin)解的人都不会(x)用这L(fng)Ҏ(gu)来洗牌:(x)另外Ҏ(gu)张牌做一个标讎ͼx(chng)否抽q这张牌Q然后第iơ在n张牌里随机抽一个,如果q张牌曾l被抽过Q那么把它放回去Q重复抽取,直到抽到一张没被抽q的牌,这张牌标记为抽取过的牌Q然后在U怸的第i个地方记下这张牌。在计算机里q样实现Q?br>
伪代码:(x)
for i <- 0 to n - 1
do d <- Random mod n
while did[d] = 1
do d = Random mod n
did[d] <- 1
shuffle[i] <- deck[d]
看了(jin)描述Q你一定就?x)觉得这U方法实在是遭透了(jin)Q不仅麻?ch),而且?x)有一个陷阱,那就是在某次取牌的时候,也许?x)运气差永远也取不到没有被取q的那张牌,DE序q行的不定性。然而,在初学者当中,却有不少是用q种Ҏ(gu)实现的shuffle的。个为,在设计算法的时候,简单、越接近生活的模型,pҎ(gu)设计出好的算法,而且法的描qC更接q实际生zR因此,设计法的时候,如果能往qx(chng)生活的方面想Q?L事半功倍的?br>
附上我自己实现的一个类qsort的shuffle法
// element_Size is the size of each element
void swap(void const *element1, void const *element2, size_t element_Size)
{
char *temp = new char,
*elem1, *elem2;
elem1 = (char *)element1;
elem2 = (char *)element2;
for(int i = 0; i < element_Size; i++, elem1++, elem2++){
*temp = *elem1;
*elem1 = *elem2;
*elem2 = *temp;
}
delete temp;
}
// array_Size is the size of array,
// element_Size is the size of each element in array
void shuffle(void const *array, size_t array_Size, size_t element_Size)
{
void *element1, *element2;
srand(time(0));
for(int i = 0; i < array_Size / element_Size; i++){
element1 = (char *)array + i * element_Size;
element2 = (char *)array + rand(i * element_Size,
array_Size - element_Size, element_Size);
swap(element1, element2, element_Size);
}
}
播放器的shuffle法Q?
前面说过播放器的随机播放有两U,一U叫Random,一U叫Shuffle(我自q解的......)Q下面解释这两种Ҏ(gu)的不同?/p>
学过概率的h都该知道有放回的抽取的概c(din)袋中有n个不同的球Q每ơ抽取一个小球,然后攑֛Q每一ơ取的时候概率都是相同的。这正是播放器random法的原理,q种法实现h很简单,一首歌l束以后Q只需要随机选取下一首歌p?jin)?br>但是q样做有一些缺点:(x)1Q有一定的概率使得q箋(hu)选取的两首歌是同一首歌Q我怿q不是所有h都希望在shuffle模式下连l听同一首歌吧,当然也有解决办法Q那是增加层@环判断,如果选上同一首歌Q则重新选,而这样又?x)重y那个很烂的z牌法的覆辙?Q当听完一首歌的时候,觉得q想再听一遍,怎么办?按下“上一?#8221;Q你?x)发现这时听到的歌曲已经不是刚才那一首想听歌曲了(jin)Q因U方法只知道当前的状态,而不知道q去的播攄态。怎么办?一U办法是增加一个队列叫?#8220;刚才播放列表”Q把播放q的歌曲按照序储存在列表里?Q有一定概率在很长的一D|间内Q播攑֙不停的在重复播放两首歌曲A和B或者类似情况,像q样Q?..-A-B-A-B-A-B-...。这U情况也是很讨厌的,可是如何避免呢?我能惛_的办法是增加判断Q看q首歌是不是在列表的最后几wQ如果在׃选这?.....
但是q些概率都小的可怜,对于一个播攑֙的random函数来说Q能够考虑C上的几点Q已l能够做到够random和h性化?jin)。只要能够合理的选择参数Q考虑C些特D情?比如极小的播攑ֈ?Q以?qing)考虑用户的心(j)理,p做出一个比较好的random函数?/p>
下面讲我设计的播攑֙shuffle法Qshuffle法能够很大E度上避免random法的缺P在空间时间上都很节约Q而且能够辑ֈ比较理想的随机化效果。它的大体思\是这L(fng)Q?/p>
我们使用一个隐含的shuffle播放列表(一个@环队?来储存歌曲的序Qƈ用一个指针表C正在播攄歌曲(C"^")Q比如当前的播放列表是这L(fng)Q?/p>
ABCDEFGHIJKLMN
^
即现在有14首歌Q将要播放位|?的歌?正在播放位置14的歌?Q我们认为队列头和尾是相q的Q即N后面的元素是AQ那么这样够成了(jin)一个@环队列?br>在播放之前,我们在前7(7=14*0.5Q这个比例可以随侉K,当然大随机性越大,但能后退的次数越?个位|中Q随机取一个一首歌Q把它和要播放的那个位|的歌曲交换。假设我们选的是EQ则队列变成q样Q?/p>
EBCDAFGHIJKLMN
^
然后播放E。E播放完了(jin)以后(或者选择下一首时)Q重复刚才的动作Q即在BCDAFGH中随机选一个,交换Q比如选到HQ则队列变成Q?br>EHCDAFGBIJKLMN
^
然后播放H。这P一个shuffle法初步完成?jin)?/p>
比如某一时刻播放器的状态是q样Q?br>EHCDAFGBIJKLMN
^
则我们在LMNEHCD中选择一个,比如选择到HQ那么交换ƈ播放Q成为:(x)
ELCDAFGBIJKHMN
^
但是如果用户选择上一首怎么办呢?我们可以再记录一个指针指向最新shuffle选择出来的那首歌?C"*")Q没有选择q前一首的时候,它与播放指针指向同一个位|。当选择前一首的时候,仅移动指针^Q而不Ud*Q比如上一个例子播攄时候按下前一首以后,成ؓ(f)Q?/p>
ELCDAFGBIJKHMN
^*
q时候播攄K正好是刚才播攄那一首,当然q达C(jin)我的目的Q即可以选到刚才播放的曲目,当然如果再一ơ选择上一首,׃(x)变成Q?/p>
ELCDAFGBIJKHMN
^ *
q时候如果按下一首,应该判断^指向的是不是?指向的相同,如果相同Q就按照最早介l的shuffle法q行随机选取Q不相同q单的Ud^Q即成ؓ(f)Q?/p>
ELCDAFGBIJKHMN
^*
伪代码:(x)
function keypress(key)
if key = NEXT
if p1 = p2
do p1 <- p1 + 1
p2 <- p2 + 1
k = Random mod (length / 2)
swap(p1, (p1 + k) mod length)
play(p2)
else
do p2 <- (p2 + 1) mod length
play(p2)
if key = PREV
do p2 <- (p2 + length - 1) mod length
play(p2)
q个播放器的shuffle法比较单实用,而且节约内存开销(q对mp3 walkman之类的东西是十分重要?Q当然也有个缺点,是当^前移多次回到*以后Q再按下一首,则会(x)重新开始shuffleQ但是歌曲数目很多的情况下,q个~点q不是那么重要?br>q个法在刚开始听的时候,q不是很随机Q可是随着听的ơ数的增多,队列?x)越来越乱,辑ֈ一个shuffle的效果?br>当然Q也可以在第一ơ对q个列表播放之前Q用扑克牌的shuffle法(见本文第一部分)q行一ơshuffleQ这P刚开始播攄时候列表就是随机的?br>通过原理我们可以看到Q对于刚听过的那首歌来说Q不l过length / 2ơ,是不?x)再一ơ听到的Q因此很大程度上避免?jin)random法的缺陗这个length / 2的参数可以按照具体情况选择Q可以是常数Q也可以是随机数Q也可以是和长度有关的一个数?nbsp;
拉普拉斯变换QLaplace Transform)Q是工程数学中常用的一U积分变换?br> 它是为简化计而徏立的实变量函数和复变量函数间的一U函数变换。对一个实变量函数作拉普拉斯变换,q在复数域中作各U运,再将q算l果作拉普拉斯反变换来求得实数域中的相应l果Q往往比直接在实数域中求出同样的结果在计算上容易得多。拉普拉斯变换的q种q算步骤对于求解U性微分方E尤为有效,它可把微分方E化为容易求解的代数方程来处理,从而计算化。在l典控制理论中,Ҏ(gu)制系l的分析和综合,都是建立在拉普拉斯变换的基础上的?br> 引入拉普拉斯变换的一个主要优点,是可采用传递函C替微分方E来描述pȝ的特性。这׃ؓ(f)采用直观和简便的图解Ҏ(gu)来确定控制系l的整个Ҏ(gu)(见信hE图、动态结构图Q、分析控制系l的q动q程Q见奈奎斯特E_判据、根轨迹法)(j)Q以?qing)综合控制系l的校正装置Q见控制pȝ校正Ҏ(gu)Q提供了(jin)可能性?br> 拉普拉斯变换在工E学上的应用Q应用拉普拉斯变换解常变量齐ơ微分方E,可以微分方E化Z数方E,佉K题得以解冟뀂在工程学上Q拉普拉斯变换的重大意义在于Q将一个信号从时域上,转换为复频域Qs域)(j)上来表示Q在U性系l,控制自动化上都有q泛的应用?/p>
变成一个链?/span>Q当所有节Ҏ(gu)从小到大的顺序依ơ插入后Q。而红黑树(wi)在每一ơ插入或删除节点之后都会(x)?span lang=EN-US>OQ?span lang=EN-US>log NQ的旉来对?wi)的l构作修改,?span style="COLOR: red">保持?wi)的q。也是_(d)U黑?wi)的查找?gu)与二叉搜索树(wi)完全一P插入和删除节点的的方法前半部分节与二叉搜索树(wi)完全一P而后半部分添加了(jin)一些修Ҏ(gu)(wi)的结构的操作?span lang=EN-US>
q多?jin)一个属性:(x)color?/span>它只能是两种颜色Q红或黑。而红黑树(wi)除了(jin)h二叉搜烦(ch)?wi)的所有性质之外Q还h以下4Ҏ(gu)质Q?span style="COLOR: red">Qؓ(f)什么只要这些性质p解决q个问题Q其实还是一个问题)(j)
根节Ҏ(gu)黑色的?span lang=EN-US>
IҎ(gu)黑色的(U黑?wi)中Q根节点?span lang=EN-US>parent以及(qing)所有叶节点lchild?span lang=EN-US>rchild都不指向NULLQ而是指向一个定义好的空节点Q?span lang=EN-US>
U色节点的父、左子、右子节炚w是黑艌Ӏ?span lang=EN-US>
在Q何一子?wi)中Q每一条从根节点向下走到空节点的\径上包含的黑色节Ҏ(gu)量都相同?span lang=EN-US>
如下囑ְ是一늺黑树(wi)Q?span lang=EN-US>
满二叉搜烦(ch)?wi)的性质Q?span style="COLOR: red">中序遍历q没有改?/span>Q。在U黑?wi)的插入、删除中Q要用到很多Left-Rotate?span lang=EN-US>Right-Rotate操作?span lang=EN-US>
//把一个节点向左下方移一|q让他原来的叛_节点代替它的位置?/span>
void leftRotate(RBTNode* node)
{
RBTNode* right = node->rchild;
node->rchild = right->lchild;
node->rcount = right->lcount;
node->rchild->parent = node;
right->parent = node->parent;
if (right->parent == m_null) {
m_root = right;
}
else if (node == node->parent->lchild) {
node->parent->lchild = right;
}
else {
node->parent->rchild = right;
}
right->lchild = node;
right->lcount += node->lcount + 1;
node->parent = right;
}
//把一个节点向右下方移一|q让他原来的左子节点代替它的位置?/span>
inline void rightRotate(RBTNode* node) {
RBTNode* left = node->lchild;
node->lchild = left->rchild;
node->lcount = left->rcount;
node->lchild->parent = node;
left->parent = node->parent;
if (left->parent == m_null) {
m_root = left;
}
else if (node == node->parent->lchild) {
node->parent->lchild = left;
}
else {
node->parent->rchild = left;
}
left->rchild = node;
left->rcount += node->rcount + 1;
node->parent = left;
}
接下来把z的颜色设?span style="COLOR: red">U色?span style="COLOR: red">Z么?q记得红黑树(wi)的性质吗,从根节点向下到空节点的每一条\径上的黑色节Ҏ(gu)要相同。如果新插入的是黑色节点Q那么它所在的路径上就?span style="COLOR: red">Z(jin)一个黑色的节点?jin)。所以新插入的节点一定要设成U色。但是这样可能又有一个矛盾,如果z的父节点也是U色Q怎么办,前面说过U色节点的子节点必须是黑艌Ӏ因此我们要执行下面一个P代的q程Q称?span lang=EN-US>Insert-FixupQ来修补q棵U黑?wi)?span lang=EN-US>
grandfatherQ指?span lang=EN-US>z->parent->parentQ也是z的爷?span lang=EN-US>(昄׃z->parent为红Ԍgrandfather一定是黑色)Q?span lang=EN-US>uncleQ指?span lang=EN-US>grandfather除了(jin)z->parent之外的另一个子节点Q也是z的父亲的兄弟Q所以叫uncle?span lang=EN-US>
也是U色。这时只要把z->parent?span lang=EN-US>uncle都设成黑Ԍq把grandfather设成U色。这样仍然确保了(jin)每一条\径上的黑色节Ҏ(gu)不变。然后把z指向grandfatherQƈ开始新一轮的q代。如下图Q?span lang=EN-US>
是黑Ԍq且z?span lang=EN-US>z->parent的右子节炏V这时我们只要把z指向z->parentQ然后做一?span lang=EN-US>Left-Rotate(z)。就可以把情况{化成Case 3?span lang=EN-US>
是黑Ԍq且z?span lang=EN-US>z->parent的左子节炏V到?jin)这一步,我们剩最后一步了(jin)。只要把z->parent设成黑色Q把grandfather设成U色Q再做一?span lang=EN-US>Right-Rotate(grandfather)Q整|(wi)׃补完毕了(jin)。可以思考一下,q样一ơ操作之后,实满?jin)所有红黑树(wi)的性质?span lang=EN-US>Case 2?span lang=EN-US>Case 3如下图:(x)
反复q行q代Q直到某一ơP代开始时z->parent为黑色而告l,也就是当遇到Case 3后,做完它而告l?span lang=EN-US>
void insertFixup(RBTNode* insertNode) {
RBTNode* p = insertNode;
while (p->parent->color == RED) {
//z->parent?/span>grandfather的左子节?/span>,下面是三U情?/span>
if (p->parent == p->parent->parent->lchild) {
RBTNode* parentRight = p->parent->parent->rchild;
if (parentRight->color == RED) {
p->parent->color = BLACK;
parentRight->color = BLACK;
p->parent->parent->color = RED;
p = p->parent->parent;
}
else {
if (p == p->parent->rchild) {
p = p->parent;
leftRotate(p);
}
p->parent->color = BLACK;
p->parent->parent->color = RED;
rightRotate(p->parent->parent);
}
}
else {
RBTNode* parentLeft = p->parent->parent->lchild;
if (parentLeft->color == RED) {
p->parent->color = BLACK;
parentLeft->color = BLACK;
p->parent->parent->color = RED;
p = p->parent->parent;
}
else {
if (p == p->parent->lchild) {
p = p->parent;
rightRotate(p);
}
p->parent->color = BLACK;
p->parent->parent->color = RED;
leftRotate(p->parent->parent);
}
}
}
m_root->color = BLACK;
}
U色?/span>Q那q?span style="COLOR: red">不会(x)改变U黑?wi)的性质。如果删除的节点?span style="COLOR: red">黑色?/span>Q那么显然它所在的路径上就一个黑色节点,那么U黑?wi)?span style="COLOR: red">性质p破坏?/span>。这时我们就要执行一个称?span lang=EN-US>Delete-Fixup的过E,来修补这|(wi)。下面我来讲解一下?span lang=EN-US>
然而,如果x是黑色的Q那我们p假想x上背负了(jin)2个单位的黑色。那么红黑树(wi)的性质也同样不破坏Q但是我们要扑ֈ某一个红色的节点Q把x?span lang=EN-US>“蝲”的这1个单位的黑色丢给它,q样才算完成?span lang=EN-US>Delete-Fixup做的是q个工作?span lang=EN-US>
同样是一个@环P代的q程。每一ơP代开始时Q如果指?span lang=EN-US>x指向一个红色节点,那么大功告成Q把它设成黑色即告终。相反如?span lang=EN-US>x黑色Q那么我们就?x)面?span style="COLOR: red">以下4U情?/span>?span lang=EN-US>
是红艌Ӏ这时我们根据红黑树(wi)的性质可以肯定x->parent是黑艌Ӏ?span lang=EN-US>w->lchild是黑艌Ӏ我们把x->parent?span lang=EN-US>w的颜色互换,然后做一?span lang=EN-US>Left-Rotate(x->parent)。做完之?span lang=EN-US>x有?jin)一个新的兄弟:(x)?span lang=EN-US>w->lchildQ前面说q它一定是黑色的。那么我们就在不破坏U黑?wi)性质的前提下Q把Case 1转换成了(jin)Case2?span lang=EN-US>3?span lang=EN-US>4中的一个,也就?span lang=EN-US>w是黑色的情况。思考一下,q样做不?x)改变每条\径上黑色节点的个敎ͼ如下图:(x)
是黑Ԍq且w的两个子节点都是黑色。这时我们只要把w设成U色。然后把xUdx->parentQ开始下一轮P代(注意Q那“蝲”?span lang=EN-US>1单位的黑色始l是跟着指针x走的Q直?span lang=EN-US>x走到?jin)一个红色节点上才能把它“怸”Q。思考一下,q一ơ操作不?x)破坏红黑?wi)的性质。如下图Q图中节?span lang=EN-US>B不一定是U色Q也可能是黑Ԍ(j)Q?span lang=EN-US>
是黑Ԍq且w的两个子节点左红右黑。这时我们把w?span lang=EN-US>w->lchild的颜色互换,然后?span lang=EN-US>Right-Rotate(w)。思考一下,q样做之后不?x)破坏红黑?wi)的性质。这?span lang=EN-US>x的新的兄弟就是原w->lchild?span style="COLOR: red">?span lang=EN-US>Case 3被{化成?span lang=EN-US>Case 4?/span>
是黑Ԍq且w的右子节Ҏ(gu)U色。一但遇?span lang=EN-US>Case 4Q就胜利在望?jin)。我看下面一张图。先?span lang=EN-US>w?span lang=EN-US>x->parent的颜色互换,再做Left-Rotate(x->parent)。这时图中节?span lang=EN-US>EQ也是?span lang=EN-US>w->rchildQ所在的路径p定少?jin)一个黑Ԍ?span lang=EN-US>x所在的路径则多?jin)一个黑艌Ӏ那么我们就?span lang=EN-US>x上多余的1个单位的黑色丢给E可以了(jin)。至此,Delete-Fixup顺利完成了(jin)?span lang=EN-US>
void delFixup(RBTNode* delNode) {
RBTNode* p = delNode;
while (p != m_root && p->color == BLACK) {
if (p == p->parent->lchild) {//左边情况Q以下是四种不同?/span>Case
RBTNode* sibling = p->parent->rchild;
if (sibling->color == RED) {
sibling->color = BLACK;
p->parent->color = RED;
leftRotate(p->parent);
sibling = p->parent->rchild;
}
if (sibling->lchild->color == BLACK
&& sibling->rchild->color == BLACK
) {
sibling->color = RED;
p = p->parent;
}
else {
if (sibling->rchild->color == BLACK) {
sibling->lchild->color = BLACK;
sibling->color = RED;
rightRotate(sibling);
sibling = sibling->parent;
}
sibling->color = sibling->parent->color;
sibling->parent->color = BLACK;
sibling->rchild->color = BLACK;
leftRotate(sibling->parent);
p = m_root;
}
}
else {//双情况
RBTNode* sibling = p->parent->lchild;
if (sibling->color == RED) {
sibling->color = BLACK;
p->parent->color = RED;
rightRotate(p->parent);
sibling = p->parent->lchild;
}
if (sibling->lchild->color == BLACK
&& sibling->rchild->color == BLACK
) {
sibling->color = RED;
p = p->parent;
}
else {
if (sibling->lchild->color == BLACK) {
sibling->rchild->color = BLACK;
sibling->color = RED;
leftRotate(sibling);
sibling = sibling->parent;
}
sibling->color = sibling->parent->color;
sibling->parent->color = BLACK;
sibling->lchild->color = BLACK;
rightRotate(sibling->parent);
p = m_root;
}
}
}
p->color = BLACK;
}
刘未?span lang=EN-US>(pongba)
的罗宫(http://blog.csdn.net/pongba)
前言
猜数?span lang=EN-US>
U球
排序
Z么堆排比快排?span lang=EN-US>
Z么快排其实也不是那么?span lang=EN-US>
基排又ؓ(f)什么那么快?span lang=EN-US>
信息论!信息论?
结
前言
猜数?
在讨Z提到Q这U策略的本质可以概括?span lang=EN-US>“让未知世界无机可?span lang=EN-US>”。它是没?span lang=EN-US>“q?span lang=EN-US>”Q答案的M一个分支都是等概率的。反之,一旦某个分支蕴含的可能性更多,当情况落到那个分支上的时候你郁闷了(jin)。比如猜数字游戏最p糕的策略就是一个一个的猜:(x)?span lang=EN-US>1吗??span lang=EN-US>2吗?... 因ؓ(f)q种猜法最差的情况下需?span lang=EN-US>64ơ才能猜对,下界非常p糕。二分搜索ؓ(f)什么好Q就是因为它每次都将可能性排除一半ƈ且无论如何都能排除一半(它是最p情况下表现最好的Q?
U球
个小球,其中有一个是坏球。有一架天q뀂需要你用最的U次数来定哪个球是坏的ƈ且它到底是轻q是重?
在他的书?span lang=EN-US>Information Theory: Inference and Learning Algorithms》(作者开攑օ费电(sh)子书Q里?span lang=EN-US>4.1节专门讲?jin)这个称球问题,q画?jin)一张不错的图,我就照抄?jin)?x)
排序
Z么堆排比快排?
建立最大堆Q堆的元素大于其两个儿子,两个儿子又分别大于它们各自下属的两个儿子... 以此cLQ?
堆的元素和最后一个元素对调(相当于将堆顶元素Q最大|(j)拿走Q然后将堆底的那个元素补上它的空~)(j)Q然后让那最后一个元素从上往下滑到恰当的位置Q重C堆最大化Q?
重复W?span lang=EN-US>2步?
也提供了(jin)一个修改版的堆排:(x)每次不是堆底的元素拿到上面去,而是直接比较堆顶Q最大)(j)元素的两个儿子,即选出ơ大的元素。由于这两个儿子之间的大关pL很不定的,两者都很大Q说不好哪个更大哪个更小Q所以这ơ比较的两个l果是概率均等的了(jin)。具体参?span lang=EN-US>q里?
Z么快排其实也不是那么?
鸡排Z么又那么快呢Q?
信息论!信息论?
结
q个问题可以有信息论的理释,而信息论则是一个相当大的领域了(jin)?span lang=EN-US>
文中提到的这U看问题的视角除?jin)用于排序、称球,q能够运用到哪些问题?sh)(比如搜?ch)Q?span lang=EN-US>
在讨Zl箋(hu)提到Q?span lang=EN-US>
_(d)(x)
]]>
我个得算法里面极大一部分内容是如何有效地q行搜烦(ch)Q这里的"有效"可以分ؓ(f)Q避免不必要的计(如A*寻\以及(qing)所有的启发式剪枝)(j)Q缓存重复计(如所?shy;的动态规划)(j)。当?dng)知道q些跟具体的设计Z个算法至还有十万八千里Q只能说有了(jin)q个大体的思\Q就可以从这两个角度d视手头的问题Q往往是会(x)有启发意?shy;的Ş?jin)。如何避免不必要的计?也有很多 rules of thumb 可以遵@Q如启发式剪枝里面就要求去设计一个最优(sh)界,而最一般的思\则是使劲瞅瞅问题里面有什么条件是没有利用的,q些条gl合h可以得出什么性质Q也许某个性质p够被利用来减掉一大堆计算Q至于如何从题目条g推出有h(hun)值的性质Q有两个办法Q一是试错(惛_的结论都l写出来Q陶哲轩?Solving Mathematical Problems 里面提到过q个办法。)(j)Q另一个方向则是脑袋里揣着惌实现的目的往反方向归U。如何缓存重复计?单的动态规划问题如fibonacci数列计算Q其重复计算是非常明昄Q计的q程本n指明了(jin)哪些计算是重复的QAn 的计算是重复的Q——当?dng)正如早前邓同学发的一个题?lt;https://groups.google.com/group/pongba/browse_frm/thread/2ca1f2bda0c8...>里面说的Q其实fibonacci数列计算里面的线性变换本w也是有重复计算的——后者便是更隐蔽的重复计了(jin)Q一?non-trivial 的动态规划问题往往涉及(qing)到非帔R蔽的重复计算Q或者更隄是,你遍历组合空间的方式军_?jin)你所能够~存的重复计到底有多少Q也许某个遍历方式之下就没有办法?shy;~存计算。当?dng)法的范畴其实是很大的,法是一个AI-Complete 的问题,所有的 Problem-Solving q程都可以叫做算法。只是有很多实际当中的算法会(x)掉入以上两类而已?nbsp;
W二个问题我举一个例子:(x)不像很多牛h在高?sh)和本科q赛奖牌一堆,我直到大四的时候还?sh)知道什么是动态规划,因ؓ(f)本科四年我一直只对底层技术感兴趣Q最喜欢?比如 Petzold 的《编码的奥秘》和 Richter 的?NET 框架E序设计》(事实上这是我看的W一本英文原版书Q这cM。研一的时候由于方向是自然语言处理Q看的第一?paper ?Rabiner ? A Tutorial on Hidden Markov Models and Selected Applications in Speech
Recognition 。Paper 的内容倒是完全能够理解Q但是理解其实只是第一步,我发现理解了(jin)之后很快忘掉了(jin)Q这p明理解得不够深刻。比如里面的 Viterbi 法Q花?jin)时间去理解Q但是一转头很快又忘掉了(jin)。一q后因ؓ(f)机缘巧合Q对法发生?jin)一D늟暂的兴趣Qƈ学习(fn)?jin)一些基的算法,其是算法的思想Q因为思想是有I?shy;的,但算法是无穷的,其是题目是做不完的。之后一D|_(d)y又需要翻一马可夫模型Q搜出吴军的数学之美以及(qing)那篇 Paper Q发?Viterbi 法其实是最单的一cd态规划,׃对于动态规划的理解深刻?jin)很多,所以对?Viterbi 法Q在脑袋里面C的不再是什?Forward Variable/Backward Variable
之类的技术细节,而是它的本质Q于是便不再Ҏ(gu)忘掉Q而即便忘掉,如庞加莱所_(d)也可以非常迅速的算法的l节自行构徏出来?
其实我相信这L(fng)例子是数不胜数的Q所以我q个只是一?Yet Another Example Q由于对我来说比较特D,所以印象较为深刅R?
q个例子是关?理解"的。有时候算法也?x)非常有用,如有一ơ写E序旉要用?LCS ?Edit-Distance Q这L(fng)Z(x)很少Q但遇到?jin)时如果不知道有多项式复杂度的算法就很(zhn)惨?jin)Q,而做机器学习(fn)和数据挖掘的更是不?jin)一坨坨的算法,如果光是理解别h的做法然后实?shy;出来Q那么对法的思想的把握有助于理解和记忆;如果需要自p计算法,那就需要算法基知识的辅助才行了(jin)。绝大多Ch应该属于前者?
学习(fn)C么程度?我觉得视人群而定。如果做底层开发、应用开发、系l开发,只要知道一个大概就可以?jin),知道l典的数据结构和法没有M困难Q而且反正l典法都有现成的库可用。对于有兴趣做一?research 沾边的事情的人,则需要了(jin)解这些算法背后的一般性思\是什么,否则来一个特定的法你就特定的理解记忆一下,肯定不牢靠,而且费大脑资源。对于搞 real deal ?original research 的那需要广泛的知识U篏?jin),光知道一般性思\都不够?
另一斚wQ我觉得学完?jin)经典算法,深刻理解了(jin)算法背后的一般性思\之后Q如果再q一步去玩题目,做题库。效益却不是很大的,因ؓ(f)刀了(jin)是要用的Q玩题目做题库就是进一步磨刀而不用(不去解决实际问题Q能够生媄(jing)响力的,或生产力的问题)(j)。实际上做了(jin)一些题目之后就完全没必要进一步做题目?jin),因?f)做来做去Q拼的基本也?shy;是谁的知识积累多Q套路多Q,谁的耐心(j)大(肯劲去一道题目)(j)Q实际上谁也不比谁笨Q到最后区别就基本上显露在知识U篏和耐心(j)上了(jin)。所以接着做,刀也不?x)磨?shy;更锋利,更何况大好的时光应该dҎ(gu)意义的事情(如果是ؓ(f)?fun 而做题的Q那么有意义的事情同样也可以?extremely funQ,比如我觉得最吸引Z最Ҏ(gu)的问题就是h工智能问题(x(chng)看,是世界上q今为止所知最为复杂的l构Q这个结构具备了(jin)认识自然?规律"的能力,?shy;备了(jin)认识"自我"的能力,具备?jin)归U_演绎推理的能力,cL的能力,具备?jin)难以置信的启发式搜索能力,具备完美的模式识别能力,而根据进化论的观点,q样的结?shy;居然仅仅是通过变异——筛选得来的Q如果真有上帝,那么利用上帝赋予我们的大脑去破解上帝q个牛逼程序员写的E序——h脑的U密Q还有比q更带劲儿的事情?shy;Q)(j)Q所以我觉得有那么好的基的牛人,不去直面真正 fundamental ?problems Q就可惜?jin),ȝ题目是永q做不完的,一个公理系l的定理也是永远推导不完的,永远可以设计出题目来l你做,但是真正的问题其实只有一个。如果穷举不?jin)世界上所有的问题Q至可以D出那些有、有意义的问?)
--
刘未?pongba)
Blog|C++的罗宫
http://blog.csdn.net/pongba