据说这个游戏可以很快的增加你的内存

今天的主角名叫 N-back,起源似乎是某种心理测试,然后碰巧有人发现它碰巧和大多数智力游戏只能提高你在那一个游戏上的表现不同,N-back 的作用可以转移

关于 N-back,更多的细节可以看这里:N-back FAQ(作者貌似是个大牛),文中提到几点挺有道理的,比如说不管内存(Working Memory)和 IQ 是否有直接的关系,但至少对程序员来说,快速的记住几个变量及其类型、几个函数及其原型什么的可以很大的提高工作效率——也就是说内存这东西还是挺重要的。这个,是当然的了

下面提一下 N-back 怎么玩:

首先,N-back 中的 N 是一个变量,当 N==1 时,你需要判断上一个图形和当前的图形是不是一样的;当 N==2 时,你就需要判断上上个图形和当前的图形是不是一样的,依此类推。

当然,上一句提到的图形也可以不仅仅是图形,还可以是字母、数字、声音等各种刺激;而一种称作 Dual N-back 的变种则是同时施加两种刺激,你需要分别判断它们各自是否和之前匹配。

下面是我乱写的 Single N-back:

(BTW 我显然不会去考虑 IE 用户的感受,读者您自重吧)

其实我觉得 Dual N-back 更好玩一些啦,不过想不到有什么好的操作方式,所以没加上来(用 A,L 操作也实在太囧了吧!),iPhone 上的 IQ boost 的 2-back (Dual) 我已经可以玩到全对了,哈哈,炫一下。

哦对了,我的法线贴图做好了

看看这光影的变化,多漂亮啊:

其实那些凹凸并不在模型上,而只是通过一张图保存了平面的法线方向。

利用法线贴图上的法线信息计算光照的时候需要注意的是得要把它的坐标系(横着是 red, 竖着是 green, 立在平面上的是 blue)转换到空间上对应平面的坐标系中 —— 或者反过来,把光线和视线转换到法线平面的坐标系中(我就是这么做的),然后再那啥。

提一下,从网上搜到的信息来看这个的计算貌似挺复杂的,但是考虑线性变换其实也就是线性方程而已,从法线贴图的坐标系转换到空间坐标系,当前正在渲染的点所在的空间三角形的贴图的 u 坐标的正方向为 a,法向量为 n,不考虑位移(为什么可以不考虑位移?自己想),我们可以列出这么几个式子:

z = cross(a, n);

(1,0,0) -> (a.x, a.y, a.z)
(0,1,0) -> (n.x, n.y, n.z)
(0,0,1) -> (z.x, z.y, z.z)

 

有没有恍然大悟的感觉?

 

没有?再提示,设从法线贴图的坐标空间转换到空间三角形的坐标空间的矩阵为 M, 那么有

1,0,0            a.x, a.y, a.z
0,1,0  *  M ==  n.x, n.y, n.z
0,0,1  z.x, z.y, z.z

好了,点到为止。

 

俺用的漫反射贴图是这样的:

漫反射贴图

法线贴图:

法线贴图

高光贴图:(虽然用了而且计算了,不过光源放的位置似乎不太恰当以至于完全看不到高光的效果,囧~)

高光贴图

可执行文件:

binary

 

源码……还是不给,因为写得丑了……

 

PS. 想要运行着玩玩看的话你得把上面三个贴图和这个程序保存在一起,如果你有更靠谱的贴图当然更好(分别保存为 tex.png, tex_NRM.png, tex_SPEC.png)。

PPS. 在学 DX,所以上面用到的线性变换也是假设向量是行向量,所以对向量的变换就是用行向量左乘矩阵,不过换个方式用矩阵左乘列向量区别也不大。

PPPS. 扫了几本 3D 数学方面的书,发现还是这本比较靠谱: http://book.douban.com/subject/1400419/

PPPPS. 非常感谢老胡当年让我感受到了线性代数的好玩之处,不然现在在公司里可要遭鄙视了。

PPPPPS. 我写的 3dmath.h,如果你有兴趣看的话——含有一个向量类一个矩阵类,若干无趣的函数,唯一比较有意思的东西是透视和投影。

PPPPPPS. 好几次看见自己拍的照片,突然想到,哇,这阴影好逼真啊。

无营养两则

1. 最近在补 3D 知识,上周花很大的精力试着手动逐像素渲染 3D 图形,挺好玩的:

老大的要求是高光贴图和法线贴图也是要的,我也做出来了,只不过几个小时之前突然意识到一个非常重要的坐标转换没有做,所以那个法线贴图的结果其实很不靠谱,代码先不放了,省得丢人,等把法线贴图做对了再说吧。

这是阉割版的程序 —— 如果你有兴趣围观的话:rending.7z

哦对了,这里用到的数学运算俺也都自己实现了一遍,等有时间了会整理一下的,毕竟这个其实挺好玩。

 

 

2. 觉得 mataball 这东西挺有意思,结果就整出了个奇丑的:

任何试图画出有两个眼睛一个嘴巴的面部的尝试都会得到一个青蛙

程序和代码在这儿:metaball.7z

圈圈们默认是会动的,如果你只是想画青蛙,把 render 函数的第二个参数设为 0 就好了。

BTW Fast inverse square root 真是威武,有多威武你自己试试就知道了。

 

自己编译了一下 SDL

其实我只是想在 SDL 中使用 png 图像,但又不愿意加上 SDL_image.dll, libpngXX.dll, zlibXXX.dll 这大一堆东西,想起不知道什么时候见到的一句让我深以为然的话:糙快猛乃素王道,于是俺就把 SDL, zlib, libpng, SDL_image 分别编译了一遍,然后把它们的 .o 文件都放在一起(除了 SDL_win32_main.o),再然后 gcc --shared -o SDL--.dll *.o -lgdi32 -lwinmm 就成了。(怎么样,够暴力吧!)

SDL_image 有问题,需要改:

72a73,74
> #include <pngstruct.h>
> #include <pnginfo.h>
350c352
<       if ( setjmp(png_ptr->jmpbuf) ) {
---
>       if ( setjmp(png_ptr->png_jmpbuf) ) {

以后自己做点什么游戏原型,或者小游戏,或者寻路算法之类的东西时,可能就会用得上它。

 

你也需要吗?点这里: 动态链接库 静态链接库

 

PS. 这几个玩意儿的授权真乱,一个 GNU Lesser GPL,一个 GNU Library GPL,另外两个是自己的协议;简单的读了一下,不出意外的话和闭源程序动态链接起来应该都是不会有啥问题的,不过另一方面,混合起来之后应该怎么发布还真不知道,所以,我没有发布任何东西,上面的链接纯粹是你的幻觉。

用 hough 变换检测抛物线

告诉你某张图中很可能有某个东西长得很像开口向上的抛物线,想把它检测出来,嘿嘿,看上去很难吧?

其实呢,这种事情很适合用 hough 变换 来做:

假设抛物线方程为 y = a ( x - b )2 + c

先把图像边缘检测出来,对于图像边缘上的每一个点 (x,y), 可以有很多条抛物线穿过它,即有很多个 a, b, c 可以使上面的方程成立,于是, 你很暴力的把这些 a, b, c 都记下来! ,对于每个 x,y 点都记下一次,然后看看哪些 a, b, c 的组合被命中的次数比较多就好了。

当然,上面的那个过程有不少地方是可以优化的,比如说对于图片,b 和 c 肯定是整数值,而且 b 很可能在比较偏中心的位置,而 a 的取值范围可以预先设定,比如说如果你想检测下巴,保守的说 a 肯定就是小于 1 的(不然那下巴也太尖了吧!),如此这般等等——而 OpenCV 中用 hough 变换检查圆的代码中显然还有些其他的不那么容易理解的优化,反正我也用不着把它做得很高效,就没有研究了。

下面上图:

(图片均来源于 pixdaus.com ,如果碰巧它侵犯了您的版权,请与我联系)

 

再上代码——

 

(你确定要看吗??!!)

 

 

(警告!魔数出没!)

 

 

 

(警告!没有注释!)

 

 

 

 

(警告!诡异的变量名出没!)

 

 

 

 

 

(警告!你的大脑可能因为看到这种乱七八糟的代码受到永久性的损伤!!)

 

 

 

 

 

 

(你确定依然要看吗??!!!!)

 

 

 

 

 

 

 

// y = a/150*(x-b)^2+c;
struct Parabola {
    int a;
    int b;
    int c;
    Parabola():a(0),b(0),c(0){}
    Parabola(int a, int b, int c):a(a),b(b),c(c){}
    Parabola(Parabola const& p):a(p.a),b(p.b),c(p.c){}
};
struct Record {
    Parabola para;
    int      count;
    Record():para(),count(0){}
    Record(int a, int b, int c, int cnt):para(a,b,c),count(cnt){}
};
struct RecordCmp {
    inline bool operator()(Record const& a, Record const& b) {
        return a.count>b.count;
    }
};
std::vector<Parabola> detectParabolas( cv::Mat const& img,
                                       int minA=-1,
                                       int maxA=-1,
                                       int minB=-1,
                                       int maxB=-1,
                                       int minC=-1,
                                       int maxC=-1 )
{
    cv::Mat edges;
    // cv::imshow("debug", img);
    // cv::waitKey(3000);
    cv::Canny( img, edges, 50, 150 );
    cv::Mat_<uint8_t> img_(edges);
    // cv::imshow("debug", edges);
    // cv::waitKey(3000);
    int ***acc = 0;

    if(minA < 0)
        minA = 1;
    if(minB < 0)
        minB = img.cols/4;
    if(minC < 0)
        minC = img.rows/4;
    if(maxA < 0)
        maxA = 60;
    if(maxB < 0)
        maxB = img.cols/2+img.cols/4;
    if(maxC < 0)
        maxC = img.rows;

    acc = new int**[maxA+1];
    for(int i=0; i<maxA+1; ++i) {
        acc[i] = new int*[maxB+1];
        for(int j=0; j<maxB+1; ++j) {
            acc[i][j] = new int[maxC+1];
            for(int k=minC; k<maxC+1; ++k) {
                acc[i][j][k]=0;
            }
        }
    }

    printf("calculating ...\n");
    int percent=0;
    for(int y=0; y<img.rows; ++y) {
        for(int x=0; x<img.cols; ++x) {
            if(img_(y,x)) {
                for(int a=minA; a<=maxA; ++a){
                    for(int b=minB; b<maxB; ++b) {
                        int c=y+a*(x-b)*(x-b)/150;
                        if(c>=minC && c<=maxC) {
                            ++acc[a][b][c];
                        }
                    }
                }
            }
        }
        int p=int(round(double(y)/img.rows*100.));
        if(p!=percent) {
            percent = p;
            printf("> %02d%%\n", percent);
        }
    }
    printf("done!\n");

    sizedpq<Record, RecordCmp> pq(3);
    for(int i=minA; i<=maxA; ++i) {
        for(int j=minB; j<=maxB; ++j) {
            for(int k=minC; k<=maxC; ++k) {
                pq.insert(Record(-i,j,k, acc[i][j][k]));
            }
        }
    }

    for(int i=0; i<=maxA; ++i) {
        for(int j=0; j<=maxB; ++j) {
            delete[] acc[i][j];
        }
        delete[] acc[i];
    }
    delete[] acc;

    std::vector<Parabola> res;
    for(int i=0; i<3; ++i) {
        res.push_back(pq[i].para);
    }
    return res;
}

上面那段代码最诡异而需要说明的地方是 a ,我希望检测的是开口向上的抛物线,于是在计算机图像的坐标系统下 a 的取值应该是负数,但作为数组下标时它当然得要是正数,所以这里多绕了几个弯,下次我自己看到肯定也会忘记的,不过还好这算法相当简单,下次需要用到我肯定更愿意重写。

sizedpq 类是俺以前做马赛克拼接图像时折腾出来的一个固定大小的优先队列类,其实设计得并不好,不过接口挺好用,所以.. 就拿来用了。

 

PS. 速度与图像边缘数量关系很密切,不过通常情况下处理一张上面展示的那种大小的图像也就需要 3 秒钟左右吧,其实还可以接受。

PPS. 人果然还是逼出来的啊,今早老宋很客气的问我节日快乐,弄得我很不好意思的在两个小时之内把这个做好了……

PPPS. 上周末和室友去西湖边逛了一圈,在柳浪闻莺景区里,我刚拍完一张照,发现旁边飘过了一个长发白衣飘逸出尘的美女背影,我说哇美女,他说其实不是美女我已经观察很久了...... 唉,男人。

PPPPS. 谁能教我玩 DotA 啊!!!

还是流水帐加意识流

1   来到网易实习

这些天我一直在后悔一件事情,就是还没看保密协议上面都写有些什么就把它给签了………而且到目前为止我还不知道上面写了些什么。

嗯,透露一下下肯定不是商业机密的事情吧,网易的伙食还不错。

2   晒晒我的 vimrc

分到电脑的那一天花了一个上午装各种软件,其中最耗精力的要数 VIM 了,自己的 vimrc 没有随身带着,又考虑到里面塞满了各种从网上看到的看上去很好玩但实际上一万年用不到一次的设置,所以干脆自己重新写了一份,里面全是每天都会用到的东西,不长:

set nocompatible
source $VIMRUNTIME/mswin.vim
behave mswin

if has("gui_running")
  colo Ifs
  set gfn=consolas:h11
  set go-=m
  set go-=T
  set cursorline
else
  colo blue
  set nocursorline
endif

syntax on
filetype on
filetype indent on
filetype plugin on

map  <space> :
map  <C-j> <C-w><down>
map  <C-k> <C-w><up>
map  <C-h> <C-w><left>
map  <C-l> <C-w><right>
imap <C-j> <down>
imap <C-k> <up>
imap <C-h> <left>
imap <C-l> <right>

let mapleader=";"
map <leader>w :w!<cr>
map <leader>q :close<cr>
map <leader>[ <C-o>
map <leader>] <C-]>
map <leader><space> <C-w>

map <leader>j :bn<cr>
map <leader>k :bp<cr>
map <leader>bd :bd<cr>

map <leader>tn :tabnew<cr>
map <leader>l  :tabn<cr>
map <leader>h  :tabp<cr>
map <C-t>      :tabnew<cr>
map <C-tab>    :tabn<cr>
map <C-S-tab>  :tabp<cr>

map <silent> <leader><cr> :noh<cr>

map <leader>tl :TlistToggle<cr>
map <leader>gf :NERDTree<cr>

set history=50
set autoread
set ts=4
set expandtab
set smarttab
set shiftwidth=4
set autoindent
set smartindent
set cindent
set scrolloff=5
set wildchar=<Tab>
set wildmenu
set cmdheight=1
set ruler
set nu
set lz
set hid
set whichwrap+=<,>,h,l
set ignorecase
set incsearch
set magic
set showmatch
set hlsearch
set laststatus=2
set statusline=\ %P\ %F%m%r%h\ %y\ %w\ \ CWD:\ %{getcwd()}%h\ \ Line:\ %l/%L:%c
set showmatch
set showmode

其中 <space><leader> 的设置照目前看来貌似很非主流,但是 <space> 不映射成 : 实在是可惜了,毕竟向下滚动有 j 就够了,而且 j 很好用,把 <space> 映射成 : 可以在敲命令的时候感觉十分痛快;另外 ; 就在小拇指上面,用来敲快捷键再方便不过,默认的用于重复 f, t, F, T 的操作也是基本上用不到的,所以,嗯……

这个设置没考虑到 Linux 下的情况,不过除了 gfn 之外其他应该没什么需要改动的吧。另外 Ifs 主题是我在 lettuce 主题的基础上改的,自己喜欢而已。

插件装得不多,因为发现插件多了启动会慢:

  • bufexplorer
  • c
  • mark
  • matchit
  • NERD_tree
  • supertab
  • taglist

c.vim 要修改一下,它有个 <leader>lcs 的映射,除了让我的 <leader>l 响应变慢之外啥用途都没有,罪过很大。

——然后用起来就很舒服了。

3   Qt,各种吐槽

最新版本的 Qt 光是安装文件竟然就有 1.5 GB !!! 它完全疯了!!!

有段时间特别喜欢 Qt,因为它的每个类都显得非常漂亮,比如说字符串类里面会提供宽字符和窄字符的转换,再比如说 QImage 类光是构造函数就有这么多种:

  • QImage ()
  • QImage ( const QSize & size, Format format )
  • QImage ( int width, int height, Format format )
  • QImage ( uchar * data, int width, int height, Format format )
  • QImage ( const uchar * data, int width, int height, Format format )
  • QImage ( uchar * data, int width, int height, int bytesPerLine, Format format )
  • QImage ( const uchar * data, int width, int height, int bytesPerLine, Format format )
  • QImage ( const char * const[] xpm )
  • QImage ( const QString & fileName, const char * format = 0 )
  • QImage ( const char * fileName, const char * format = 0 )
  • QImage ( const QImage & image )

不但功能上一应俱全,而且甚至不需要任何文档,一眼看上去就能知道每个函数是怎么用的。

让人不爽的还是 C++ 的二进制不兼容问题,上次做的一个小实验清楚的说明了想让不同的 C++ 编译器生成的二进制程序共用一个 C++ 类实在是很难的事情;而更早之前的经验表明即使是同一个编译器,不同的版本之间也不见得可以兼容,坑爹啊,也就是说你根本提供不了一个统一的运行时环境,写了个好玩的程序想发给别人装 B,只有每次都把一堆几十 MB 的 dll 和自己几 kb 的程序放在一起才行...这不是比 .Net 还过分吗...

还有,很大程度上托无所不能的 template 的福,C++ 编译的那速度... 上次编译 Qt 4.6 花了 4 个小时左右,后来我专门统计了一下,不算文件头注释和空行,Qt 4.6 一共有 2362083 行代码[1],按 4 个小时来算的话,那么编译+链接速度平均就是每秒钟 164 行了,还有什么语言能比它更慢一点的!!

怎么好像在对 C++ 吐槽……

……都怪 Qt 太大了, FLTK 这种 kb 级别和谁都能静态链接在一起的图形库,用 C++ 我当然没意见,否则您丫每隔一两月更新一次,每次都扔给我好几 G 的东西,还让我以前的程序面临死翘翘的危险,太过分了。

4   乱七八糟

今天在 cnBeta 上看到一句话 “别人一开源,我们就自主研发了。” 笑死了

搞ACM的你伤不起[2]” - 一看就知道是大牛手笔

[1] 包括自动生成的 moc 文件夹下的
[2] 墙外 其实不在墙外... 作者竟然是同事的同学,世界真小

一个想法:could

曾经在 Notepad++ 的官网上看到过一个关于 Linux 为何不和谐的笑料:

bash-3.2$ peace
bash: peace: command not found
bash-3.2$ harmony
bash: harmony: command not found
bash-3.2$ kill
kill: you need to specify whom to kill
base-3.2$ _


 

前几天不知为何又想到了这个笑话,突然有了个让他变得和谐一点的想法,为何不让命令行指令看上去人性化一点呢,比如说,在命令行输入 could you calculate 1+41 for me?

输出 Of course! The answer is 42 这样。

于是,俺就动手码了几行代码,写了个名为 could 的 python 脚本,which 可以把命令行参数分给不同的程序去做,在上面这个例子中,他所做的就是把 calculate 1+41 交给 calculate.py 处理。

虽然没啥技术含量,但是,等把它相关的功能实现得差不多了,应该会比较有趣吧——尤其对于那些不知道命令行是什么玩意儿的生物。

 

已经完成的代码在这儿,目前只能算是个 demo。

走过路过的你如果觉得有点意思,不妨留个爪印,咱可以一起整点好玩的。

悲剧啊,只能混个纪念品玩玩

我的程序在这儿了: http://code.google.com/p/harmless/

很想把他叫做 Mostly Harmless,可是不能太狂啊,它明明就是 TOTALLY harmless。

另外眼下 svn 连接不上 google code, 不出意外的话原因应该就是众所周知的那个吧,我最近也懒得折腾了,所以代码先就在压缩包里看吧。

核心代码在这里: http://code.google.com/p/harmless/source/browse/trunk/main.cpp

Windows / Linux 可执行程序以及源码压缩包在这里: http://code.google.com/p/harmless/downloads/list

 

用到的技术有 alpha-beta, iterative deepening, transposition table, null move heuristic, bit board, threat space search (没写完而且不准的版本), principal variation ,然而这些算法中没有几个是容易调试的,再加上核心的评价函数依然是瞎掰的(试过用进化算法让它进化出一个比较靠谱的参数,然而结果是它进化到了一种自娱自乐的境界,和自己这种风格的AI竞赛倒还好,可实际棋力依旧不行),再加上整体框架上还有好些不成熟的地方,比如说我干嘛要记录下某个空位在四个方位的子力呢,记录下最好的两个方位就OK了啊——反正,改来改去,最后一次练习赛之后的版本棋力竟然越变越差,真伤心。

泥人五子棋里面似乎有好几个可以借鉴的想法,开局库可以多折腾几条记录进去,proof number search + threat space search + 开局库效果应该就会还可以了吧;另外可惜 db-search 接触得太晚了,它的思想也很吸引人。

寒假重写。

 

 

最后,附上两张几天前在厕所玩水的照片:

 

还有好多张,有时间了再传到网易去。

为了证明我还没死…这是我写的五子棋

完成度不到 40%,棋力相当差,最核心的在找不到必杀时的局面评价函数完全是瞎掰的……虽说如此当我小放水时它偶尔还是能赢的:

white-won

想在五子棋方面找自信的童鞋可以试试: gomoku.7z

 

竞赛毕竟是竞赛,技术细节源代码神马的就不透露鸟……何况还没写完呢……

BTW 为了保证逆向我的程序的成本比从头开始写一个五子棋程序要高,俺把 exe 文件加密鸟,如果杀毒软件抱怨的话(也不太可能抱怨啦,只是怕有这种可能性)——就请你做一个艰难的选择吧。

 

对某个五子棋AI竞赛很有兴趣!

具体的说是北大的这个: http://www.botzone.org/RATE/news/zhengbasai.htm

想写个厉害的五子棋AI是俺上大学之前就有的欲望,到现在也许可以试试手了……

至于 gomocup.... 还没有那胆子……

聊聊 memcpy

嗯 .. 提到 memcpy 你是不是就条件反射的想到了这样的东西呢:

void * memcpy(void * dst, const void * src, size_t size)
{
    size_t i=0;
    unsigned char * _dst = dst;
    const unsigned char * _src = src;
    for(; i<size; ++i) {
        _dst[i] = _src[i];
    }
    return dst;
}

其实吧,memcpy 里面还是很有学问的 ... 首先从一个新闻说起吧

最近某个由 memcpy 引发的血案似乎引起了不少人关注,事情大概是 glibc 使用了一个号称经过优化的 memcpy 函数,这个函数对源地址和目标地址 重叠 的情况没有处理(虽然实际上 memcpy 函数也不要求处理这种情况),于是 Adobe Flash Player 闹情绪了,原因在于那个 tricky 的 memcpy 函数作风比较诡异,在某些机器上它是把数据 从后往前 拷贝的,对此 Linus 的评价是:

That said - why the heck would you ever do memcpy() backwards? Things like automatic prefetchers usually work better for the "natural" patterns, so a backwards memcpy is generally a bad thing to do unless you have some active reason for it, like doing a memmove()

不过我其实更觉得这完全是 Adobe 的问题,因为如果从后往前拷贝它就会出问题,那么遇到下面这种情况,从前往后拷贝也可以照样整垮它:

A: bbbbbaaaaa
B:      aaaaabbbbb

memcpy(B,A,10);

Anyway,事情到现在已经基本上算是平息了: glibc 相信问题不在于 memcpy 而在于 Adobe 没有正确使用它,问题不再讨论;Linus 则给出了一个暂行方案,他的那个方案已经非常优秀了以至于想想很有可能破坏掉读者的挑战欲望,所以还是留着过一会儿再说吧……

现在我们回到主题上,memcpy,怎么样让它更快些呢。

首先, dst[i] = src[i] 这一句实际上的效率就相当于 *(dst+i) = *(src+i) , 这里的加法显得有点太过分了。稍微修改一下下:

void * memcpy(void * dst, const void * src, size_t size)
{
    unsigned char * _dst = dst;
    const unsigned char * _src = src, * _src_end;
    for(_src_end = _src+size; _src<_src_end; ++_src, ++_dst) {
            * _dst = * _src;
    }
    return dst;
}

Andorid 上面的 libc 似乎就是类似这样的实现了。

但是,其实还不够,拷贝 size 个字节,比较运算就执行了 size 次,可不可以少一点?

答案:Yes!

方案一:Duff's Device

Duff's Device 是个非常 tricky 的东西,以至于你第一眼见到很可能怀疑“这货真能通过编译?”

void * memcpy(void * dst, const void * src, size_t size)
{
    register n=(size+7)/8;
    register unsigned char * _dst = dst;
    register const unsigned char * _src = src;
    switch(size%8){
        case 0:     do{     *_dst++ = *_src++;
        case 7:             *_dst++ = *_src++;
        case 6:             *_dst++ = *_src++;
        case 5:             *_dst++ = *_src++;
        case 4:             *_dst++ = *_src++;
        case 3:             *_dst++ = *_src++;
        case 2:             *_dst++ = *_src++;
        case 1:             *_dst++ = *_src++;
        } while(--n>0);
    }
}

这样,被 switch 一次之后便陷入了 do while 循环;这里 case 0 - case 7 如果不够还可以再加, 此段代码相当于把上面的小于比较省掉了 7/8 。

方案二:不要一个字节一个字节操作,而要一个字一个字的处理:

通常一个机器字有好几个字节长 —— 比如 32 位机器上是 4 个字节,64 位机器上是 8 字节。

将每 n 个字节作为一个整体“超级字符”进行操作,在这种场合算是很常见的应用了:

void * memcpy(void * dst, const void * src, size_t size)
{
    void * orig = dst;
    asm volatile("rep ; movsq"
        :"=D" (dst), "=S" (src)
        :"0" (dst), "1" (src), "c" (size >> 3)
        :"memory");
    asm volatile("rep ; movsb"
        :"=D" (dst), "=S" (src)
        :"0" (dst), "1" (src), "c" (size & 7)
        :"memory");
    return orig;
}

解释一下,这里汇编代码的原理也就是先把 size/8 个字(size>>3)拷贝过去(movsq), 再把后 size%8 个字节(size&7)拷贝过去(movsb)

这个就是 Linus 给出的临时方案了。

另外这里一个字是八个字节,那是因为这里出现症状的机器是 64 位的。

方案三(其实不算方案三):结合以上二者

以字为单位,用 Duff's Device 操作…… 这个想想就觉得挺牛的,Newlib 似乎就是这么玩的了:

/* Nonzero if either X or Y is not aligned on a "long" boundary.  */
#define UNALIGNED(X, Y) \
  (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1)))

/* How many bytes are copied each iteration of the 4X unrolled loop.  */
#define BIGBLOCKSIZE    (sizeof (long) << 2)

/* How many bytes are copied each iteration of the word copy loop.  */
#define LITTLEBLOCKSIZE (sizeof (long))

/* Threshhold for punting to the byte copier.  */
#define TOO_SMALL(LEN)  ((LEN) < BIGBLOCKSIZE)

_PTR
_DEFUN (memcpy, (dst0, src0, len0),
        _PTR dst0 _AND
        _CONST _PTR src0 _AND
        size_t len0)
{
#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__)
  char *dst = (char *) dst0;
  char *src = (char *) src0;

  _PTR save = dst0;

  while (len0--)
    {
      *dst++ = *src++;
    }

  return save;
#else
  char *dst = dst0;
  _CONST char *src = src0;
  long *aligned_dst;
  _CONST long *aligned_src;

  /* If the size is small, or either SRC or DST is unaligned,
     then punt into the byte copy loop.  This should be rare.  */
  if (!TOO_SMALL(len0) && !UNALIGNED (src, dst))
    {
      aligned_dst = (long*)dst;
      aligned_src = (long*)src;

      /* Copy 4X long words at a time if possible.  */
      while (len0 >= BIGBLOCKSIZE)
        {
          *aligned_dst++ = *aligned_src++;
          *aligned_dst++ = *aligned_src++;
          *aligned_dst++ = *aligned_src++;
          *aligned_dst++ = *aligned_src++;
          len0 -= BIGBLOCKSIZE;
        }

      /* Copy one long word at a time if possible.  */
      while (len0 >= LITTLEBLOCKSIZE)
        {
          *aligned_dst++ = *aligned_src++;
          len0 -= LITTLEBLOCKSIZE;
        }

       /* Pick up any residual with a byte copier.  */
      dst = (char*)aligned_dst;
      src = (char*)aligned_src;
    }

  while (len0--)
    *dst++ = *src++;

  return dst0;
#endif /* not PREFER_SIZE_OVER_SPEED */
}

方案四,有请硬件牛

翻出 glibc 2.9 的代码,它的代码看上去倒是不长:

void *
memcpy (dstpp, srcpp, len)
     void *dstpp;
     const void *srcpp;
     size_t len;
{
  unsigned long int dstp = (long int) dstpp;
  unsigned long int srcp = (long int) srcpp;

  /* Copy from the beginning to the end.  */

  /* If there not too few bytes to copy, use word copy.  */
  if (len >= OP_T_THRES)
    {
      /* Copy just a few bytes to make DSTP aligned.  */
      len -= (-dstp) % OPSIZ;
      BYTE_COPY_FWD (dstp, srcp, (-dstp) % OPSIZ);

      /* Copy whole pages from SRCP to DSTP by virtual address manipulation,
     as much as possible.  */

      PAGE_COPY_FWD_MAYBE (dstp, srcp, len, len);

      /* Copy from SRCP to DSTP taking advantage of the known alignment of
     DSTP.  Number of bytes remaining is put in the third argument,
     i.e. in LEN.  This number may vary from machine to machine.  */

      WORD_COPY_FWD (dstp, srcp, len, len);

      /* Fall out and copy the tail.  */
    }

  /* There are just a few bytes to copy.  Use byte memory operations.  */
  BYTE_COPY_FWD (dstp, srcp, len);

  return dstpp;
}

从注释可以看出,现在它在玩的都是些硬件相关的 trick 了 (更别提 Linux Kernel 中的那些对应不同处理器的汇编实现),于是我发现我似乎不太适合再继续挖下去了。




文章最后,我想以高中语文考试作文体结尾,非常感谢所有这些 —— hacker 也好 geek 也好 —— 做了这么多有意思又有意义的工作, 让我们有 Linux 这么优秀的操作系统可以用;正是由于这样的细节,这样的竞争气氛,这一点一滴的积累,才有了( 我自己的测试中 )比 Win7 快上近 1/3 的速度,让人没有理由再质疑开源的力量。

我打算让这个博客看上去相对主流一点了

——于是它就成了现在的样子。

 

Hint: 快捷键有 j, k, g, G, z, r, v 以及两个彩蛋

j, k, g, G, z 都不知道是什么的话你可就太过于主流了。

v: view 

r: reply

 

另外很不解 RC4 犯什么错了,实现 RC4 加密的 JS 文件会被 Avast! 报毒(JS:Packed-C[Trj]),弄得我很烦,如果说用 JS 加密就是罪的话为啥 AES 没问题呢:
http://g00d.blogbus.com/logs/56991621.html

关于 iPhone WebApp

总的说,写起来还是挺方便的,就拿上篇的记忆游戏来说,加上

<meta name="viewport" content="width=320,user-scalable=false,device-width=320,minimum-scale=1,maximum-scale=1" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />

这样的标记,然后处理一下 rotate 事件,保存到主屏幕,就已经是个像模像样的 app 了——除了必须得要联网。

 

通过这样那样的途径,我碰巧又发现了其实 HTML 5 具备了离线使用的潜质,于是很迫切的希望让我的程序获得这样的超能力。

好消息是:经过这样那样的努力,我实现了这个愿望:(看!灰行模式!)

webApp

webApp'

坏消息是:由于需要在 .htaccess 或者 apache 的配置文件中添加 text/cache-manifest 的 MIME 类型,而 110mb 不让我这么做;app engine 则似乎根本不让 host HTML 文件,于是目前能使用这个酷功能的有且仅有我自己一人;anyway,你还是可以使用 iSaveWeb 实现同样的效果,Please enjoy: 

http://ifyu.110mb.com/memory-game-iPhone.html

 

最后记几个笔记:

  • 在局域网中无法通过 ip 地址访问 apache 服务器是怎么回事呢,原来除了把防火墙好好调教一番之外还需要在 httpd.conf 中加上 Listen 192.168.XXX.XXX:PORT (这么设计有嘛好处?)。
  • 用于 host 简单的 Web 页面,AnalogX SimpleServer 是个好东西,比 python -> import SimpleHTTPServer -> SimpleHTTPServer.test() 还要方便。

小游戏:你的记忆力怎么样?

看到游荡在我这个博客的幽魂们喜欢游戏,我就再放出一个小作品吧……

 

这次是 HTML+JavaScript 写的, iPhone/iPod Touch 之类的设备玩起来应该都没问题;它真的有点难度哟,我自己最多只能玩到 10 56 74 184 分,欢迎走过路过的生物来鄙视。

玩法:点击 Start Game,会有几个格子显示出蓝色,两秒钟之后颜色消失,然后你需要把它们指出来;答对一轮会增加一个蓝色格子,答错了游戏结束。

另外,写得有点仓促,有 bug 的话欢迎反馈

Loading ...

Press ⇑ Above Button To Start This Game

Update Oct/24th/2010 :

 

最长公共子串 [后缀树应用之二]

1. 我干了什么

我写了个小程序,用 1.276 秒找出了《银河系漫游指南》和《宇宙尽头的餐馆》的最长公共子串:

the history of every major galactic civilization tends to pass through three distinct and recognizable phases, those of survival, inquiry and sophistication, otherwise known as the how, why and where phases.

用 1.266 秒找出了《宇宙尽头的餐馆》和《再见,谢谢所有的鱼》的最长公共子串:

in the uncharted backwaters of the unfashionable end of the western spiral arm of the galaxy

用 8.671 秒找出了《魔戒》和《1984》的最长公共子串:

. it was inevitable that they should make

用 6.728 秒找出了《飘》和《基本无害》的最长公共子串:

more than anything else in the world.

...

我发现这是件很好玩的事情~

2. 怎么做的

Again,有了 后缀树 ,这样的工作太简单了:

/*
 * Author  : If
 * Date    : Oct/16th/2010
 * License : Boost License v1

Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license  (the "Software")  to use,  reproduce,  display,  distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:

The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED,  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/

#include "suffixtree.h"
#include <algorithm>
#include <iterator>
#include <iostream>
#include <fstream>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <ctime>
using namespace std;

static void ticToc_(bool s)
{
    static clock_t start_ = clock();
    if(s) {
        start_ = clock();
    } else {
        clock_t end_=clock();
        std::cerr
            << (end_ - start_) << " ticks"
            << "    (" << (end_ - start_) * (1000.)/CLOCKS_PER_SEC <<"ms)"
            << std::endl;
    }
}

inline void tic()
{
    ticToc_(true);
}

inline void toc()
{
    ticToc_(false);
}

struct Info
{
    vector<int> belongTo;
};

typedef Node<char, Info>       NodeT;
typedef SuffixTree<char, Info> STree;

// 初始化节点的从属属性。
void initInfo(NodeT& leaf)
{
    NodeT* node = leaf.parent;
    int    nth  = leaf.belongTo;
    while ( node->parent!=node && (
            node->info.belongTo.empty() ||
            node->info.belongTo.back()!=nth )) {
        node->info.belongTo.push_back(nth);
        node=node->parent;
    }
}

void printPath(NodeT const* node)
{
    if(node->parent!=node)
        printPath(node->parent);
    copy(node->str, node->str+node->len,
         ostream_iterator<char>(cout));
}

// 将连续的空白字符转换成单个空格;将大写全转换为小写。
void filter(char * text, int & len)
{
    char*p=text,*q=text;
    for(;q<text+len;++p,++q){
        if(*q>='A'&&*q<='Z') {
            *p=*q-'A'+'a';
        } else if(isspace(*q)) {
            *p=' ';
            while(isspace(*q))
                ++q;
            --q;
        } else {
            *p=*q;
        }
    }
    len = p-text;
}

struct LetsRock
{
    NodeT const*& result;
    int   longestNow;
    int   nStrings;
    LetsRock(int n, NodeT const*& root):
        result(root),
        longestNow(0),
        nStrings(n)
    { }
    void operator()(NodeT const& node) {
        if (node.depth + node.len > longestNow &&
            node.info.belongTo.size()==nStrings) {
            longestNow = node.depth + node.len;
            result     = &node;
        }
    }
};

int main(int c,char**v)
{
    STree tree;
    tic();
    for(int i=1;i<c;++i) {
        cerr<<"reading "<<v[i]<<" ...\n";
        FILE*  file = fopen(v[i],"r");
        string content;
        char   buf[8192];
        int    bytesRead=0;
        int    bytesTotal=0;
        while((bytesRead=fread(buf,1,8192,file))==8192) {
            filter(buf,bytesRead);
            content.append(buf,bytesRead);
            bytesTotal += bytesRead;
        }
        if(bytesRead) {
            filter(buf,bytesRead);
            content.append(buf,bytesRead);
            bytesTotal += bytesRead;
        }
        fclose(file);
        cerr<<"done. ( " << bytesTotal <<" bytes )\n";
        cerr<<"adding to suffix tree ...\n";
        tree.addString(content);
        cerr<<"done.\n";
    }
    NodeT const* ans = tree.root();
    tree.eachLeaf(initInfo);
    LetsRock fun(c-1,ans);
    tree.dfs(fun);
    printPath(ans);
    cout<<endl;
    toc();
    return 0;
}

代码压缩工具原型 [后缀树应用之一]

嗯,对我来说是代码压缩工具原型;对大多数地球生物来说这其实是最长不重叠重复子串问题

1. 我干了什么

我写了个程序,运行时内存占用峰值为 323 MB,耗时 6.582s,从 2,827,372 字节的《魔戒》全本中找出了不算空格、大小写敏感的情况下出现两次或更多的最长子串:

All that is gold does not glitter,
Not all those who wander are lost;
The old that is strong does not wither,
Deep roots are not reached by the frost.
 
From the ashes a fire shall be woken,
A light from the shadows shall spring;
Renewed shall be blade that was broken,
The crownless again shall be king.

耗时 7.022 秒,内存占用峰值 351 MB,从 3,872,493 字节的 sqlite 3.6.22 的合并 c 文件中找到了最长重复子串:(忽略空格)

.c****************//**************Beginfileos_common.h********************
****** *************//***2004May22****Theauthordisclaimscopyrighttothissou
rcecode.Inpla ceof**alegalnotice,hereisablessing:****Mayyoudogoodandnotevi
l.**Mayyoufindforgiv enessforyourselfandforgiveothers.**Mayyousharefreely,
nevertakingmorethanyougive.  *********************************************
*********************************** ****Thisfilecontainsmacrosandalittlebi
tofcodethatiscommonto**alloftheplatform-sp ecificfiles(os_*.c)andis#includ
edintothose**files.****Thisfileshouldbe#includedb ytheos_*.cfilesonly.Itis
nota**generalpurposeheaderfile.*/#ifndef_OS_COMMON_H_#de fine_OS_COMMON_H_
/***AtleasttwobugshaveslippedinbecausewechangedtheMEMORY_DEBUG* *macrotoSQ
LITE_DEBUGandsomeoldermakefileshavenotyetmadethe**switch.Thefollowingc ode
shouldcatchthisproblematcompile-time.*/#ifdefMEMORY_DEBUG#error&quot;TheME
MORY_DEB UGmacroisobsolete.UseSQLITE_DEBUGinstead.&quot;#endif#ifdefSQLITE
_DEBUGSQLITE_PRIVATE intsqlite3OSTrace=0;#defineOSTRACE1(X)if(sqlite3OSTra
ce)sqlite3DebugPrintf(X)#de fineOSTRACE2(X,Y)if(sqlite3OSTrace)sqlite3Debu
gPrintf(X,Y)#defineOSTRACE3(X,Y,Z) if(sqlite3OSTrace)sqlite3DebugPrintf(X,
Y,Z)#defineOSTRACE4(X,Y,Z,A)if(sqlite3OST race)sqlite3DebugPrintf(X,Y,Z,A)
#defineOSTRACE5(X,Y,Z,A,B)if(sqlite3OSTrace)sqli te3DebugPrintf(X,Y,Z,A,B)
#defineOSTRACE6(X,Y,Z,A,B,C)\if(sqlite3OSTrace)sqlite3D ebugPrintf(X,Y,Z,A
,B,C)#defineOSTRACE7(X,Y,Z,A,B,C,D)\if(sqlite3OSTrace)sqlite3D ebugPrintf(
X,Y,Z,A,B,C,D)#else#defineOSTRACE1(X)#defineOSTRACE2(X,Y)#defineOSTRA CE3(
X,Y,Z)#defineOSTRACE4(X,Y,Z,A)#defineOSTRACE5(X,Y,Z,A,B)#defineOSTRACE6(X,
Y, Z,A,B,C)#defineOSTRACE7(X,Y,Z,A,B,C,D)#endif/***Macrosforperformancetra
cing.Norm allyturnedoff.Onlyworks**oni486hardware.*/#ifdefSQLITE_PERFORMAN
CE_TRACE/***hwti me.hcontainsinlineassemblercodeforimplementing**high-perf
ormancetimingroutines.* //**************Includehwtime.hinthemiddleofos_com
mon.h****************//******* *******Beginfilehwtime.h*******************
***********************//***2008May27 ****Theauthordisclaimscopyrighttothi
ssourcecode.Inplaceof**alegalnotice,hereisab lessing:****Mayyoudogoodandno
tevil.**Mayyoufindforgivenessforyourselfandforgiveo thers.**Mayyousharefre
ely,nevertakingmorethanyougive.*************************** ***************
******************************************Thisfilecontainsinlinea smcodefo
rretrieving&quot;high-performance&quot;**countersforx86classCPUs.*/#ifndef
_HWTIME_ H_#define_HWTIME_H_/***Thefollowingroutineonlyworksonpentium-clas
s(ornewer)proce ssors.**ItusestheRDTSCopcodetoreadthecyclecountvalueoutoft
he**processorandreturn sthatvalue.Thiscanbeusedforhigh-res**profiling.*/#i
f(defined(__GNUC__)||defined( _MSC_VER))&amp;&amp;\(defined(i386)||defined
(__i386__)||defined(_M_IX86))#ifdefined(__GN UC__)__inline__sqlite_uint64s
qlite3Hwtime(void){unsignedintlo,hi;__asm____volati le__(&quot;rdtsc&quot;
:&quot;=a&quot;(lo),&quot;=d&quot;(hi));return(sqlite_uint64)hi&lt;&lt;32|
lo;}#elifdefined(_MS C_VER)__declspec(naked)__inlinesqlite_uint64__cdeclsq
lite3Hwtime(void){__asm{rdt scret;returnvalueatEDX:EAX}}#endif#elif(define
d(__GNUC__)&amp;&amp;defined(__x86_64__))_ _inline__sqlite_uint64sqlite3Hw
time(void){unsignedlongval;__asm____volatile__(&quot;r dtsc&quot;:&quot;=A
&quot;(val));returnval;}#elif(defined(__GNUC__)&amp;&amp;defined(__ppc__))
__inline__ sqlite_uint64sqlite3Hwtime(void){unsignedlonglongretval;unsigne
dlongjunk;__asm__ __volatile__(&quot;\n\1:mftbu%1\n\mftb%L0\n\mftbu%0\n\cm
pw%0,%1\n\bne1b&quot;:&quot;=r&quot;(retval) ,&quot;=r&quot;(junk));return
retval;}#else#errorNeedimplementationofsqlite3Hwtime()foryour platform./**
*Tocompilewithoutimplementingsqlite3Hwtime()foryourplatform,**youcan remov
etheabove#errorandusethefollowing**stubfunction.Youwilllosetimingsupportfo
r many**ofthedebuggingandtestingutilities,butitshouldat**leastcompileandru
n.*/SQLI TE_PRIVATEsqlite_uint64sqlite3Hwtime(void){return((sqlite_uint64)
0);}#endif#endi f/*!defined(_HWTIME_H_)*//**************Endofhwtime.h*****
********************** *******************//**************Continuingwherew
eleftoffinos_common.h******** **********/staticsqlite_uint64g_start;static
sqlite_uint64g_elapsed;#defineTIMER_ STARTg_start=sqlite3Hwtime()#defineTI
MER_ENDg_elapsed=sqlite3Hwtime()-g_start#de fineTIMER_ELAPSEDg_elapsed#els
e#defineTIMER_START#defineTIMER_END#defineTIMER_EL APSED((sqlite_uint64)0)
#endif/***IfwecompilewiththeSQLITE_TESTmacroset,thenthefo llowingblock**of
codewillgiveustheabilitytosimulateadiskI/Oerror.This**isusedfort estingthe
I/Orecoverylogic.*/#ifdefSQLITE_TESTSQLITE_APIintsqlite3_io_error_hit=0 ;/
*TotalnumberofI/OErrors*/SQLITE_APIintsqlite3_io_error_hardhit=0;/*Numbero
fnon -benignerrors*/SQLITE_APIintsqlite3_io_error_pending=0;/*Countdowntof
irstI/Oerro r*/SQLITE_APIintsqlite3_io_error_persist=0;/*TrueifI/Oerrorspe
rsist*/SQLITE_APIi ntsqlite3_io_error_benign=0;/*Trueiferrorsarebenign*/SQ
LITE_APIintsqlite3_diskfu ll_pending=0;SQLITE_APIintsqlite3_diskfull=0;#de
fineSimulateIOErrorBenign(X)sqli te3_io_error_benign=(X)#defineSimulateIOE
rror(CODE)\if((sqlite3_io_error_persist &amp;&amp;sqlite3_io_error_hit)\||
sqlite3_io_error_pending--==1)\{local_ioerr();CODE;}st aticvoidlocal_ioerr
(){IOTRACE((&quot;IOERR\n&quot;));sqlite3_io_error_hit++;if(!sqlite3_io _e
rror_benign)sqlite3_io_error_hardhit++;}#defineSimulateDiskfullError(CODE)
\if( sqlite3_diskfull_pending){\if(sqlite3_diskfull_pending==1){\local_ioe
rr();\sqlit e3_diskfull=1;\sqlite3_io_error_hit=1;\CODE;\}else{\sqlite3_di
skfull_pending--;\ }\}#else#defineSimulateIOErrorBenign(X)#defineSimulateI
OError(A)#defineSimulateD iskfullError(A)#endif/***Whentesting,keepacounto
fthenumberofopenfiles.*/#ifdefSQ LITE_TESTSQLITE_APIintsqlite3_open_file_c
ount=0;#defineOpenCounter(X)sqlite3_ope n_file_count+=(X)#else#defineOpenC
ounter(X)#endif#endif/*!defined(_OS_COMMON_H_) *//**************Endofos_co
mmon.h*******************************************//** ************Continui
ngwhereweleftoffinos_ 

正如之前在 后缀树的介绍 中说过的那样,俺想要弄出个能压缩 C 程序代码的工具,而上面的就是原型了:找出出现多次且较长的子串, #define 一下,重复多次,差不多了。

2. 怎么做的

有了后缀树,这样的工作可真是简单啊:

/*
 * Author  : If
 * Date    : Oct/14th/2010
 * Version : 0.0.1
 */
/*
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license  (the "Software")  to use,  reproduce,  display,  distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:

The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED,  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/

#include "suffixtree.h"
#include <algorithm>
#include <iterator>
#include <iostream>
#include <string>
#include <cmath>
#include <ctime>
using namespace std;

static void ticToc_(bool s)
{
    static clock_t start_ = clock();
    if(s) {
        start_ = clock();
    } else {
        clock_t end_=clock();
        std::cerr
            << (end_ - start_) << " ticks"
            << "    (" << (end_ - start_) * (1000.)/CLOCKS_PER_SEC <<"ms)"
            << std::endl;
    }
}

inline void tic()
{
    ticToc_(true);
}

inline void toc()
{
    ticToc_(false);
}


struct Info {
    int cv,depth,isLeaf;
    Info():cv(-1),depth(-1),isLeaf(0){}
};

typedef Node<char,Info> NodeT;

void initInfo(NodeT& node) {
    node.info.depth = node.depth + node.len;
    if(node.branches.empty()){
        node.info.isLeaf = 1;
        node.info.cv = node.leafNum;
    } else {
        node.info.isLeaf = 0;
        NodeT* p = node.branches[0];
        if(p->info.cv==-1) {
            initInfo(*p);
        }
        node.info.cv = p->info.cv;
    }
}

void printPath(NodeT const* node)
{
    if(node->parent!=node) {
        printPath(node->parent);
    }
    copy(node->str, node->str+node->len, ostream_iterator<char>(cout));
}

struct LetsHaveFun
{
    NodeT const*& result;
    LetsHaveFun(NodeT const*& root):result(root){}
    void operator()(NodeT& node){
        if(!node.info.isLeaf &&  // more than once
            node.depth != 0 &&   // not root
            node.info.depth > result->info.depth && // better
            abs(node.info.cv - node.branches.back()->info.cv) >=
                node.info.depth  // do not overlap
        ) {
            this->result = &node;
        }
    }
};

int main(int c,char**v) {
    if(c>2)
        return 1;
    SuffixTree<char, Info> stree;
    NodeT const* ans = stree.root();
    LetsHaveFun fun(ans);
    string source;
    if(c==2&&v[1][0]=='-') {
        if(v[1][1]=='S') // do not skip white space
            cin>>noskipws;
        else if(v[1][1]=='s')
            cin>>skipws;
        else
            return 1;
    } else {
        return 1;
    }

    copy(istream_iterator<char>(cin), istream_iterator<char>(),
         back_inserter(source));
    tic();
    stree.addString(source);
    stree.dfs(initInfo);
    stree.dfs(fun);
    printPath(ans);
    cout<<endl;
    toc();
}

3. Question

这次有问题顺便想要请教一下高人们,在上面的代码中有一段让我很不爽:

struct LetsHaveFun
{
    NodeT const*& result;
    ...

我的本意是想用 NodeT const* result ; 然而那么做的话 stree.dfs(fun) 这句话结束之后 fun.result 又回到了 root (LetsHaveFun::operator() 中 result 的确会变的!),百思不得其解,似乎碰到了某个语法的黑暗角落;然而在这个问题上 VC2008 和 GCC 4.5.0 竟然表现得出奇的一致以至于我开始怀疑自己了,谁来救救我?

4. PostScript

suffixtree.h 全部内容在我的 后缀树的介绍 中。

5. Update

Ubuntu 威武,同样的代码,同样的文件系统,同样的计算机配置,同样的编译器,同样的编译选项,在 Ubuntu 上面,此处(Win7)耗时 6.5 秒的程序变得只要 4.5 秒了,Astonishing

CKEditor Plugin For Micolog

个人比较喜欢 CKEditor, 所以就给 micolog 整了个 ckeditor 插件: 

http://micolog.appspot.com/plugins/view?key=agdtaWNvbG9nchsLEgZQbHVnaW4iD0NLRWRpdG9yIHBsdWdpbgw

 

其实 tinymce 也还可以啦,只是我做好这个插件之后才发现原来在 /views/admin/entry.html 中把 tinymce 的初始化代码改成这样,它也完全可以接受了:

(主要是它之前总是自作主张的把 URL 改成相对路径 —— 而且还是个错的,还有 html 的预览把 <p> 弄没了,结构一塌糊涂)

<script type="text/javascript">
$('#content').ready(function() {
    $('.editor-toolbar').show();
    var editor = null;
    $('#edButtonPreview').click(function(){if(editor);else initTinymce();});
    $('#edButtonHTML').click(function(){if(editor)tinymce.execCommand('mceRemoveControl',false,'content'); editor=null;});

    function initTinymce(){
        editor = $('#content').tinymce({
            script_url : '/tinymce/tiny_mce.js',
            theme : "advanced",
            
            plugins : "safari,pagebreak,save,advhr,advimage,advlink, inlinepopups,media,directionality,visualchars,nonbreaking,fullscreen,preview,autoresize",

            theme_advanced_buttons1:"bold,italic,underline,strikethrough,|,bullist,numlist,blockquote,|,forecolor,backcolor,|,justifyleft,justifycenter,justifyright,justifyfull,|,link,unlink,image,code,preview,|,fullscreen",
            theme_advanced_buttons2:"styleselect,formatselect,fontselect,fontsizeselect,|,pastetext,pasteword,removeformat,|,media,charmap,|,outdent,indent,|,undo,redo",
            theme_advanced_buttons3:"",
            theme_advanced_buttons4:"",

            theme_advanced_toolbar_location : "top",
            theme_advanced_toolbar_align : "left",
            theme_advanced_statusbar_location : "bottom",
            theme_advanced_resizing : true,
            
            content_css : "/tinymce/wordpress.css",
            extended_valid_elements : "textarea[cols|rows|disabled|name|readonly|class],script[src,type],canvas",
 
            file_browser_callback: 'micolog_file',
            relative_urls: false
        });
    }
    initTinymce();
});
function micolog_file(field_name, url, type, win){
   if (type=='image')
       {ext='jpg|png|jpeg|gif|svg';}
   else if (type=='mdeia')
       {ext='swf|wmv|avi|wma|mp3|mid|asf|rm|rmvb|flv';}
   else
       {ext="*";}

    tinyMCE.activeEditor.windowManager.open({
        file : '/admin/uploadex?ext='+ext,  
        title : '{%trans "Files"%}',
        width : 420,
        height : 200,
        resizable : "no",
        inline : "yes",
        popup_css: false,
        close_previous : "no"
    }, {
        window : win,  
        input : field_name  
    });
    return false;
}
</script>

50 题

写了个游戏,基本上和 iPhone 上的 >=< 一模一样吧——由于我很喜欢后者,所以自己也做了个,便于在电脑上玩。 玩法很简单:小于 10 的式子丢到左边,大于 10 的式子丢到右边,等于 10 的则丢到下面,看谁速度快。一起来试试吧:

下载 swf


  还有个更具有挑战性的版本,玩的时候建议把音量开到最大:

下载 swf

 

Update:

其实,还有个比较符合宅男品位的:

下载 swf

 

Update Oct/23th/2010:

  • 多谢 枪毙自己 童鞋帮我找到的链接(AS3输入法的切换与限制),俺现在加上了号称能关闭输入法的代码——只是由于俺自己压根就没能在游戏界面中打开过输入法,所以效果无从验证了,希望大家玩得愉快,有问题欢迎反馈,而如果像枪毙自己童鞋这样还帮我找到了解决方案的话我会感动得一塌糊涂的。

  • 另,也欢迎大家把 flash 游戏下载下去玩;flash 的独立播放器看上去似乎下载不到了,但那是假象,其实只是被 adobe 藏起来了而已,它在这里:
    http://download.macromedia.com/pub/flashplayer/updaters/10/flashplayer_10_sa.exe

  • 最后,我自己目前的最好成绩是 16.65 秒全对,欢迎挑战。

Update Oct/24th/2010 :

Suffix Tree 学习笔记 I

在这篇文章中你将会看到:





* 什么是后缀树

* 后缀树有啥用

* 如何构建后缀树

* 构建后缀树的算法复杂度分析

* 具体实现的源代码

* 以及更多









文章本身很长……还是点进来看吧…… ..more

图:mississippimississippi + iamif 的后缀树

花了很多功夫,俺终于实现了生成后缀树的线性算法,添加多个字符串真麻烦,而且后缀树的结构也真够复杂的,有图为证:


Suffix-Tree(mississippimissippi)+SuffixTree(iamif)

黑线是父子关系,红线是后缀链接,图像是用俺的程序生成 dot 文件、然后用 Graphviz 自动生成的(真丑),程序以及算法下次再解释,那也预计将是俺本学期的最后一篇日志,没时间复习了。

BTW,近期通过 www.if-yu.info 域名访问俺的博客似乎很慢 —— 那么直接访问 http://iamyuguo.appspot.com/ 应该好些