Saturday, June 23, 2007

Ubuntu 7.04

“Ubuntu" is an ancient African word that means "humanity to others".

继firefox热潮之后,ubuntu今年开始热起来,可以说这款打着“为人民服务”旗号的操作系统,已经愈来愈受“人民”的欢迎了。现在已经出现了不少get Ubuntu的连接,而且 ubuntu的传播方式比firefox更疯狂(下载/邮寄+免费+鼓励传播的标语)。

头条是Dell表示将在几款机型上预装linux操作系统,而且选择的就是ubuntu这么一个非企业级别(至少和redhat/suse相比)的个人系统。可以参考该网站 http://www.dellideastorm.com/

Dell的问卷调查表示,有70%的人要求能在机器上预装l inux。在Dell确定了预装的计划后,ubuntu及一些linux的粉丝们便开始组织要求各大公司发布linux版本软件的需求,其中包括要求暴雪发布基于linux的星际2,要求google移植主要软件到linux平台等等。(网站我都没记,不好意思)

除了ubuntu本身的四套发行版,Ubuntu/Kubuntu/Xubuntu/Edubuntu以外,不少网友都在为ubuntu扩大阵容:
  • Wubuntu - ubuntu狂热fans写的基于web的模拟ubuntu环境的网站,用的都是js写的。
  • Hiweed & Dubuntu — 在ubuntu中文站看到的,不知道其目的是什么,可能就是一个发行包吧。
  • Medibuntu - 提供关于media的ubuntu包。
当然,ubuntu本身在7.04 里作了不少工作。ubuntu本身bug改了不少,感觉更加舒适稳定了。如果是邮寄ubuntu光盘的话,可以用win来运行光盘,可以看到ubuntu为win用户同样提供了一些win下的软件。果然是for human beings,即使是对头的用户也要服务,这种精神太伟大了(当然不排除抢客户的嫌疑-_-b),如果仔细观察的话,就可以发现,目前大量的开源软件都是同时支持win和linux的,而且有许多人是同时在使用win和linux的,我想这和linux包容的心态是分不开的,毕竟人不应该在一棵树上吊死,不然就死得太冤了(我也支持win的哦),多样化的世界才是多彩的世界嘛。

最后不得不谈的是ubuntu疯狂的传播策略,真的是钱多得用来砸人了。要获得ubuntu或其他发行版,可以通过免费下载,这是大部分linux采用的。而ubuntu还提供了免费邮寄的服务,可以在ubuntu的网站上直接申请少量的光盘,或者申请大量光盘(需要确定)。在上大,机器人协会就申请了500张光盘,Canonical尽还都是免费地寄过来了(-_-b, so cool)。同时,ubuntu每张光盘上会写到,请与你的朋友分享该光盘的字样。我就要求了10张光盘(送贴纸),其中有一些是重复的,也就是~你可以拿出去送人,汗,要ubuntu找我。

Thursday, June 21, 2007

Thursday, June 14, 2007

Netbeans的UML工具

今天差点被Netbeans的UML工具感动得哭了出来。从Netbeans4.0开始知道除了Eclipse还有一个这么好的IDE。虽然当时Netbeans离JBuilder和Eclipse还有很大的距离,但我一直十分坚持使用Netbeans,因为我从Netbeans身上学到的不光是使用一个IDE,更多的是做程序开发的思想。

记得那次我是写一个八数码的搜索算法分析的程序,用到了GUI,那时我发现在Label修改文本的时候可以有一个叫Resource Bundle的东西,于是我知道了Java的国际化,当时感叹啊。。。 于是呼,以后写程序坚决不在程序内直接放字符串常量,要么在头上声明个final引用,要么就Resource Bundle一下。

Netbeans的GUI编辑器是让我感动最多的地方,它的易用性给我减轻了很多的负担,使编程变得愉快而轻松。除了GUI编辑器以外,当时他的Web开发包也是让我感动死掉~,整合的Tomcat使得调式如此简便,就在当时我一再向周围的人推荐Netbeans,希望大家能把目光从Eclipse身上转移一点给Netbeans,但最后都是以失败告终。

后来Netbeans的合作开发包,虽然当时一直是对Eclipse的ECF感兴趣,但Netbeans的合作开发实在太先进了。还有JavaHelp等等,从Netbeans身上学了好多好多东西。

今天,为了在毕业设计里加几个UML图,尝试性地下载了Netbeans的UML工具包。哇!哭了,一上来就有三个选项:平台无关模型、Java模型、对Java项目做逆向工程。思路非常清晰,Java模型是主打,其他的仍然可以用平台无关模型。而我选择的是Java逆向工程,完美!把我那个项目所有引用到的类都解析出来了。然后使用使用看看,哦~~~太感动了,功能so强大。前一阵子在唠叨Poseidon For UML又慢又龊,于是乎去网上找了N个UML 开发工具,要么是付费的,要么就是太龊,而Eclipse的UML工具,建模没问题,只是图形开发环境得用最新的Eclipse,更新最新的满麻烦的。

以下这张UML,是我通过选中几个模型之后,自动创建的类图,然后稍加修饰的结果:


总之,Netbeans的UML工具智能化程度已经很高了,可能还不如一些收费工具来的厉害。但作为一款免费的IDE,免费的UML工具,已经是相当相当专业的。Sun不愧是开源的老大啊。

Friday, June 08, 2007

X over SSH

今天又学到一招,用ssh来图形远程登录。原本linux的图形远程登录比较熟悉的是vnc,但这次用ssh登录,比vnc快,而且犹如在本地运行一般。

可以用windows或linux登录有ssh server和x server的linux服务器。理论上,本地需要的是能运行x,及ssh客户端。

详细文档见此:X over SSH - A Tutorial

Wednesday, June 06, 2007

《为什么时光不能倒流》推荐

终于看完了。我一共参加过三次ACM/ICPC的比赛,另外有一次是在本校组织的地区赛。很幸运,四次都见到了CJ教授,而第五次则是CJ教授到学校里来宣传书的那一次。感觉教授有点嬉皮,很能调节气氛,就这一点算是个满讨人喜欢的教授。

昨天晚上睡不着,有点头疼(疼了有一段日子了),再加上蚊子嗡嗡的折磨,更加令我难以入睡。于是,捧起CJ教授的书,本打算是用作催眠的,却没有想到直到凌晨3点才有意识到太晚了。

《为什么时光不能倒流》宛如一碗鸡汤,滋补着我的心灵。拿到书的第一反应是惊讶CJ教授传奇的人生经历,而后从感动到感悟,每个故事都给了我一个人生的哲学。




今早起床,只睡了6个小时,但却额外精神,头不疼了,咦?真是有点奇怪哈,难道是心灵治愈了,身体就可以自然地治愈么。乘着头还没开始疼,赶快读完了书的剩余部分,确实是被感动了。这是一本适合年轻人,特别是那些儿时还是充满了憧憬和幻想,而现在却是忙忙碌碌,只是忙碌地不再是为了儿时所有的美好愿景的人,停下片刻,细细品味的美味鸡汤。

Tuesday, June 05, 2007

Stoer-Wagner算法

Stoer-Wagner算法是用来计算无向图的全局最小割的,理论上复杂度可以为O(|E|+|V|log|V|)。我的实现不是最好的,但觉得还行吧。

网友的好文章:最小割Stoer-Wagner算法

题目是百度06年的复赛题,星球大战。可以在POJ上练习,POJ2914。

相关论文和偶的代码,可点此下载

Stoer-Wagner算法的实现:


#include <iostream>
#include <algorithm>
using namespace std;

#define initSet(n,Arr) for(int i=0;i<n;++i)Arr[i]=i;
#define MAX 1<<30;
int graph[600][600];

// Stoer-Wagner Algorithm
int globalMinCut(int n){
// A is A set for Stoer-Wagner Algorithm
bool* A=new bool[n];
// V is vertex index
int* V=new int[n];
int* W=new int[n];

initSet(n,V);

int best=MAX;
while(n>1){

//the most tightly connected vertex.
int maxj=1;

// initialize set A and other vertex's weight
A[V[0]] = true;
for(int i=1; i<n; ++i){
A[V[i]]=false;
W[i]=graph[V[0]][V[i]];

if(W[i]>W[maxj])
maxj=i;
}

// find a min-cut
int prev=0,buf=n;
while(--buf){
// add it to A
A[V[maxj]]=true;

if(buf==1){
// update min cut
best=min(best,W[maxj]);

// merge prev and last vertex
for(int k=0; k<n; ++k)
graph[V[k]][V[prev]]=(graph[V[prev]][V[k]]
+=graph[V[maxj]][V[k]]);
V[maxj]=V[--n];
}
prev=maxj;
maxj=-1;

// update the weights
for(int j=1; j<n; ++j)
if(!A[V[j]]){
W[j]+=graph[V[prev]][V[j]];

if(maxj<0 || W[j]>W[maxj])
maxj=j;
}
}
}

delete[] A;
delete[] V;
delete[] W;
return best;
}


int main(){
// n - vertex number
// m - edge number
int n,m;

while(scanf("%d %d",&n,&m)==2){
memset(graph,0,sizeof(graph)/sizeof(bool));

// v-w is an edge with c weight
int v,w,c;

while(m--){
scanf("%d %d %d",&v,&w,&c);
graph[v][w]+=c;
graph[w][v]+=c;
}

// output min cut
printf("%d\n",globalMinCut(n));
}
}


Saturday, June 02, 2007

百度之星2007初赛

HOHO,恭喜恭喜,上大的四人都过初赛啦! 我,梁老大,沈还有Jackie Yu。我和Jackie两天都做了,沈做了第一天,梁做了第二天。最后沈和梁晋级,我靠第一天晋级,Jackie靠第二天晋级。我第一天拿了30分(题1全过,题2过一半),比期望的低哦,最后的那道SQL本来希望能过两个CASE的,结果全挂了,555,沈也是用暴力解的,但他过了两个CASE,估计是我哪里细节出了问题,导致全错。第二天很郁闷,中午做题果然没晚上精神爽,直打瞌睡。第二天只做了两道(1,4),最后的结果是第1题只对了一个case, -_-b,超级郁闷,后来发现程序里少了两句判断,555,不然第二天也能晋级啦,现在只有9分,不过还好有第一天保底(只是对不住陈胖子了呀)。

题目可以见某网友的帖子,感谢他保留了百度试题:
http://www.ninstein.com/blog/article.asp?id=199
http://www.ninstein.com/blog/article.asp?id=202

其他的题目就不说了,百度有点评(第二天要准备trie的模板呀),我个人对第一天的第四题SQL的SELECT语句比较感兴趣,所以重新做了一遍:

基本上是做了两点的优化(较暴力法),一个是结构上加了索引,二是计算满足条件的几个集合的交集(在结构的基础上优化的求交算法)。

为了说明结构我画了图,还不错吧 ^_^


简单说明一下,该结构完全是为了针对题目而做的,我考虑下,为了实现Delete, Update还需要修正一下结构,而且结构改动后后面的Select算法也要调整。另外,Select是单向的,即只能由条件找出记录号,不能由记录号找出记录的所有信息,要实现的话应该在Record上再加一些变量。不管了,只是针对AND逻辑的嘛。

Table[i][j]的位置不是存放第 i 记录的第 j 字段值,而是存放一个指针,指向下一个与Table[i][j]值相同的记录号,也就是链表结构。索引表存放的才是字段值,对应一个startId是在Table中的第一个拥有该字段值的记录号,即链表的head。

假设,按图中sample有5条记录,0 1 3的c3字段值为a,2 4的c3为b。那么Select c3为a的所有记录,就是在IndexTable中找c3的字段索引,然后找对应的a的startid,得0。然后回表中开始找出所有记录,Table[0][c3]=1, Table[1][c3]=3, Table[3][c3]=-1。-1为终止,于是有 0 1 3 三条记录。

由上可以求出,满足某字段值的一个集合。第二步是求满足多个字段值的一个交集。由于结构本身提供了一个很有利的条件,即若Table[i][j]的字段值等于Table[i][k]且两者均不为-1,则Table[i][j]<table[i][k]当且仅当 j<k,算法描述如下:

(假设满足求n个condition的记录数count=0)
if condition is empty
count=recordNum, exit

准备一个索引数组tb,tb[i]代表 i 条件下的当前记录号。tb初始全-2。
foreach condition as ci
int tmp= IndexTable中ci条件的startid值,无法满足ci,则tmp=-1
if tb[ci] 已存在,即 !=-2
if tb[ci]!= tmp
count=0, exit
else
next condition
else
tb[ci]=tmp

依某算法(算法很多,自定吧)取字段c为主键,可以理解为取某个condition为主条件

while tb[c]!=-1
foreach condition as ci
if ci==c
continue
else
while tb[ci]<tb[c] && tb[ci]>=0
tb[ci] = Table[tb[ci]][ci] // next record
if tb[ci]>tb[c] || tb[ci]==-1
break;
if all condition matched
count++
else
tb[c]=Table[tb[c]][c]

count is result

复杂度分析: 空间复杂度上是不会亏的啦,字段值没有重复保存过,索引用的都是数字。设记录数n,字段数c,构造DB时的复杂度O(nc)。设某个查询的条件是q条,查询的复杂度min(q,c)+sum(count(qi)) min(q,c)是条件数和字段数取小者,qi表示第i个条件,count是满足i条件的记录数(可以缓存一下的),sum()为求和函数。

其他建议:取主键的算法很多(甚至可以不取,直接假设第一个条件对应的键为主键),可以单纯地用缓存过的满足条件的记录数来决定记录数最少的那个为主键。但对于复杂度不会有提高,因为如果按我上述的算法,复杂度是固定的,主键不影响复杂度。

存在不同主键的情况下会影响复杂度的算法,假设已经求到Table[i]是满足所有条件的记录,那么求下max(Table[i][cj]) cj为每个条件对应的字段,只有最大值对应的记录才有可能为下一条满足所有条件的记录,即使他不是满足所需要的记录,那么只要循着该字段一定能找到真正的下一条满足所有条件的记录。这种跳跃式的算法,可以有效减小复杂度。

缺点:结构只是针对select and做了优化,并没考虑太多,可能会不方便其他操作。优化中将记录标号了,虽然现在大部分表都喜欢加个id,但数据库本意是记录无序的。如我图中所画,DB有许多Table,而只有一个IndexTable,我的想法是一个IndexTable可以管理所有有关联的Table的所有字段,以优化做select and操作。但还没设计完,现在的IndexTable显然是不行的,以后有机会再说吧。

偶的代码(C++),大致实现了上面的结构和算法,测试数据使用MySQL 的sakila sample 的payment表,修正后的数据以及代码点此下载 (放在上大ACM论坛,-_-b荒废好久的论坛啊,只能用来摆摆文件了):



#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <utility>
#include <sstream>
#include <algorithm>
using namespace std;

// DB structs
// first is startId, second is len(len may be tempory used for other target)
typedef pair<int,int> Index;
typedef map<string,Index> FieldIndex;
typedef vector<FieldIndex> IndexTable;
typedef map<string,int> ColumnName;
typedef vector<int> Record;

struct DB{

// a table (since only one table provided)
string name;
vector<Record> records;
int recordNum,columnNum;
ColumnName nameMap;

IndexTable it;

DB(int n,int c):recordNum(n),columnNum(c){
records.resize(n);
it.resize(c);
for(int i=0;i<n;++i){
records[i].resize(c);
}
}
};

// Query structs
typedef pair<string,string> Expression;
typedef vector<Expression> Query;

// called while constructing the DB and IndexTable
int addValue(FieldIndex& index, string& value,int curIndex){
map<string,Index>::iterator iter=index.find(value);
int lastIndex=-1;

if(iter==index.end()){
index[value]=Index(curIndex,curIndex);
}else{
lastIndex=index[value].second;
index[value].second=curIndex;
}

return lastIndex;
}

void initDB(DB& db){
string buf;
// parse db name
cin>>db.name;
getline(cin,buf);

// parse column name
getline(cin,buf);
stringstream ss(buf);
string name;
for(int i=0;i<db.columnNum;++i){
ss>>name;
db.nameMap[name]=i;
}

// parse db records
for(int i=0;i<db.recordNum;++i){
getline(cin,buf);
stringstream ss(buf);

for(int j=0;j<db.columnNum;++j){
string value;
ss>>value;
db.records[i][j]=-1;
int lastId=addValue(db.it[j],value,i);
if(lastId>=0){
db.records[lastId][j]=i;
}
}
}
}

bool isExpression(string& str){
int len=str.length();
for(int i=0;i<len;++i){
if(str[i]=='=')return true;
}
return false;
}

bool validChar(char c){
return (c>='0' && c<='9') || (c>='a' && c<='z') || (c>='A' && c<='Z');
}

void parseExpression(string& str, Expression& exp){
int i=0,st=0,len=0;

while(!validChar(str[i]))++i;
st=i;
while(validChar(str[i]))++i;
len=i-st;

exp.first=str.substr(st,len);

while(!validChar(str[i]))++i;
st=i;
while(validChar(str[i]))++i;
len=i-st;

exp.second=str.substr(st,len);
}

int doQuery(DB& db,string& q){
int cnt=0;
stringstream ss(q);
string buf;
Query query;

while(ss>>buf){
if(isExpression(buf)){
Expression exp;
parseExpression(buf,exp);
query.push_back(exp);
}
}

if(query.empty())return db.recordNum;

int* tb=new int[db.columnNum];
fill(tb,tb+db.columnNum,-2);

for(Query::iterator iter=query.begin(); iter!=query.end(); ++iter){
int col=db.nameMap[iter->first];
map<string,Index>::iterator mapIter=db.it[col].find(iter->second);
if(mapIter!=db.it[col].end()){
int st=(mapIter->second).first;
if(tb[col]>=0 && st!=tb[col]){
return 0;
}else if(tb[col]<0){
tb[col]=st;
}
}else tb[col]=-1;
}

int lastValue=-1;
while(true){
int i=0,curValue=-1;
for(;i<db.columnNum;++i){
if(tb[i]==-2)continue;
if(lastValue>=0 && tb[i]==lastValue){
tb[i]=db.records[tb[i]][i];
}
if(tb[i]==-1)break;

if(curValue==-1){
curValue=tb[i];
}else{
while(tb[i]<curValue && tb[i]>=0){
tb[i]=db.records[tb[i]][i];
}

if(tb[i]>curValue || tb[i]<0){
break;
}
}
}
lastValue=curValue;
if(tb[i]==-1 || curValue==-1)break;
if(i==db.columnNum){
++cnt;
}
}

delete[] tb;
return cnt;
}

// main entry
int main(){
int c,n,q;
cin>>c>>n>>q;
DB db(n,c);
initDB(db);

string line;
while(q--){
getline(cin,line);
cout<<doQuery(db,line)<<endl;
}
}


Tuesday, May 29, 2007

07年5月29日 在复旦大学的topcoder比赛

先是匹萨,棒约翰的,还不错,我和肖各吃了两块,就饱了,其实是不太好意思吃多 ^_^

然后是比赛,感觉脑子的转速要比在家里做快得多,时间也貌似比一般的一个半小时长得多(因为单位时间的思考能力强了,所以感觉时间就多了)。算是三校联赛吧,被复旦踩是必然的,目标是踩东华。最后338.31,是room2的第7,divsion的第13,还是被两个东华的踩了。

很久没写tc报告了,因为很久不关心算法。但今天还是满有意思的,写一下,纪念一下:

总得来说DIV2的题目就是比较简单的呀。

problem 250:
一个Set包括0-9个数字,其中6和9可以互用,隐含的意思就是可以表示 66 或 99 或 69。
然后给个门牌号,问至少买多少Set能够摆出这个门牌号。

算法很简单,统计一下门牌号中出现过的数字个数。因为6和9可以互用,所以把6的个数和9的个数平均一下,有两个写法:


// method one, after counting
count[6]=count[9]=(count[6]+count[9]+1)/2; // forget add one will be chanllenged

// method two, in counting iteration
if(count[6]<count[9]) count[6]++;
else count[9]++;


看到两个人用第一种写法忘记+1,cha之~,HOHO,赚100分。

problem 500:
有金G银S铜B三种钱币,去银行转换的规则如下:
11 S -> 1 G
11 B -> 1 S
1 G -> 9 S
1 S -> 9 B
问从G1 S1 B1换到 G2 S2 B2的最少交换次数,或-1表示不可能

与其说是贪心,不如说是逻辑题,只要依存简单的逻辑,就能既保证代码清晰,又可保证准确率:
step 1. 如果B不够,只能用S换B
step 2. 如果G不够,只能用S换G
step 3. B和G都够了,若S不够,先用G换S,再用B换S。因为G换S一次可以满足的S多,所以先G换S。

5555,前面的代码都是好好的,可惜最后在B换S的地方,忘记减去B,加上S了。。。好粗心啊 >.<
一开始沈发现了,但他给的cha数据没能把我cha掉~,洋洋得意地笑沈时,忽然某人把我cha了~ 瞬间郁闷

problem 1000:
比赛的时候想了n套方案,结果还是没能决定用什么方法解之。
题目意思是给出一个序列,要求用最小的cost排序,将i放到某个值后面/前面,花费的cost是i。

大大的程序千奇百怪,有STL库牛人的,有搜索解的,有DP的,不过最欣赏的是这个解法:
可以把问题转换为, 求顺序的最大cost的序列,然后用总cost减一下就行了。

例如sample 中的 6 4 5 3 8 2 7 2 11 2 2,顺序序列可以是 4 5 8 11,也可以是4 5 7 11,对于一个顺序序列,剩余的部分便是待移动的数,为了保证待移动的数的cost尽可能小,就是保证顺序序列的cost尽可能大,所以取 4 5 8 11 的cost和为28,所以只需要用最少52-28=24来完成排序。按照这个思路,有程序:


public int calcMinimalCost(int[] arr){
int total=0,len=arr.length,maxCost=0;
int[] cost=new int[len];

for(int i=0;i<len;++i){
cost[i]=arr[i];
total+=arr[i];

for(int j=0;j<i;++j){
if(arr[j]<=arr[i] && cost[j]+arr[i]>cost[i]){
cost[i]=cost[j]+arr[i];
}
}
maxCost=Math.max(cost[i],maxCost);
}

return total-maxCost;
}

Tuesday, May 22, 2007

Portlet with Ajax Tech (Jetspeed)

I was required to upload a large file via portlet with the process message displaying in the client side, and ajax would be the best choice. In order to ease the job, prototype was included first, so that the Ajax instance would do me a great help.

I've referred so many articles about the ajax application in portal, mostly work for WebSphere Portal rather than Jetspeed, tomcat-based portal. At last, I solved this problem, and as a conclude, I found that the problem could be broken down into three problem:

  1. Understanding the principle of the ajax application in portal, JSR-168 would be the most helpful reference. And here is a well-spread diagram to show this principle:

    As JSR168 said, portlet session could share the data with servlet session, so render the page via portlet and request the update from servlet, which makes the ajax realizable. Here is the article where the above diagram from.

  2. Share the data between portlet and servlet. Of cause, there are some applications do not need the servlet and portlet bundled, there would be unnecessary to consider this problem in such cases. But as to my problem, portlet and servlet should bundled together. As the articles I have read, I found it maybe was not a problem for WebSphere, but it's really a problem for tomcat based portal. The solution was so easy, just configure the server.xml like this, I mean to add emptySessionPath="true" attribute:

    <connector port="8080"
    maxthreads="150" minsparethreads="25" maxsparethreads="75"
    enablelookups="false" redirectport="8443" acceptcount="100"
    connectiontimeout="20000" disableuploadtimeout="true"
    emptysessionpath="true" />

    And read this article to know what the session identifier do to make such tricky problem.

  3. Last work is to write the codes and test it. So the last problem is about the coding.
    Most of these coding barriers could be taken easily via careful reading JSR168 Spec. Such as, portlet and servlet share data in APPLICATION_SCOPE, so using APPLICATION_SCOPE to store session. Url and javascript debuging would also cost a lot of time.


A good article about Ajax with WebSphere Portal:
http://www-128.ibm.com/developerworks/websphere/library/techarticles/0606_bishop/0606_bishop.html?ca=dgr-lnxw03AjaxPortals

So, it's really fun to work with portal, especially that using the ajax tech to make the portal more vivid. Hope this article help you solve your problem, thanks.

Sunday, May 06, 2007

DataTypes in PostgreSQL

It's quit a different world of PostgreSQL from MySQL. Before I began to use PostgreSQL, I thought all the relational DB are the same, just be familiar with ANSI SQL, and everything will be done. PostgreSQL proved me that it's a ludicrous thought, as an advance object-relational DB, PostgreSQL has guide me a new idea of DB world. It's not just a dull warehouse,where I put and get, but an amazing technology that with so many smart and fashion thoughts.

I learned two new tips of PostgreSQL's datatype today:

First is a notational convenience for unique identifier columns. In other relational DB, I am so familiar with the command

CREATE TABLE sometable (some_id INT(8) AUTO_INCREMENT, PRIMARY KEY(some_id));

but it was replaced by a notational type -- SERIAL or BIGSERIAL, just like the serialization in Java :)

CREATE TABLE sometable (some_id SERIAL, PRIMARY KEY(some_id));

then it will equal to:

CREATE TABLE sometable (some_id integer NOT NULL DEFAULT nextval('sometable_some_id::regclass')
, PRIMARY KEY(some_id));

Click here to see more.

Another tip is more useful, it's about the enum type in PostgreSQL, (MySQL support Enum, as I know, but it was still convenient to simulate enum type in PostgreSQL):
Here is a good article about it.

Three methods available:
Both 1st and 2nd methods use check constraint to the field, while 3rd use foreign key
Both 2nd and 3rd methods define a new DataType.
As to me, I prefered 2nd.

1st:

CREATE TABLE average_temperature
(
year INTEGER,
temp REAL,
season TEXT (CHECK season IN ('spring', 'summer', 'autumn', 'winter'))
);


2nd:

CREATE DOMAIN season_type
AS TEXT
CHECK (VALUE IN ('spring', 'summer', 'autumn', 'winter'));

CREATE TABLE average_temperature
(
year INTEGER,
temp REAL,
season season_type
);


3rd: (since every value based on an integer key, it will be able to sort and compare values)

CREATE TABLE season_lookup
(
code INT PRIMARY KEY,
value TEXT NOT NULL
);
INSERT INTO season_lookup VALUES(1,'spring');
INSERT INTO season_lookup VALUES(2,'summer');
INSERT INTO season_lookup VALUES(3,'autumn');
INSERT INTO season_lookup VALUES(4,'winter');
CREATE TABLE average_temperature
(
year INTEGER,
temp REAL,
season INT REFERENCES season_lookup(code)
);

Thursday, April 19, 2007

C++学习

发现很多公司要求C++,虽然工作已经有一个了。但7月上班,实在难耐,所以最近想着试几家大一点的公司,看看有没有更好的机会。由于强项在Java,很多大公司都只能望而却步。为了第一份工作一搏!决定重拾C++,发一帖学习日记,一是标出一些重点、难懂的东东;二是记录学习历程;三是可以给google友们多一个找答案的选择。
学习的书是问陈胖子借的,虽然不喜欢中文书,但听说翻译还可以,而且赶时间(就两个月了呀),呵呵,就不计较了。工具是VS2005,-_-b,可能要被小青年鄙视了,因为我整天就唠叨不要用VS,要用GCC。但确实,就业需求所逼,改吧。

Question 1: const
const很复杂,跟指针和引用混在一起更复杂。先两个概念,常量指针和指常量的指针:
int a=0;
const int *p1=&a; //指常量的指针,此处a可以是常量也可以不是,但结果是(*p1)不能再修改
int *const p2=&a; //常量指针,不可以改变p的指向,功能类似引用(但有区别)
const int *const p3=&a; //指常量的常量指针,上面两个特点综合一下

谈到指针和引用,说一下区别,引用是引对象,而指针就是地址。举些例子:
double a=0.0;
const int &b=a; //其实是先 int tmp=a;然后 const int &b=tmp; 因为b要引一个int对象,所以临时创建了一个
const int &c=0; //其实是先 int tmp=0;然后 const int &c=tmp; 同上,先创建一个对象


常量只可以用指常量的指针指。另有书上一例:
int *const &p=a; // 经检验,效果等同 int *const p=a;


Question 2: 陌生关键字
volatile : 表示变量是易变的,Java中也有。但C++中的含义是,编译器不能随意对该变量进行优化处理。即变量在编译器无法察觉的情况下可能改变。
sizeof : sizeof本身并不陌生,只上他有一点比较特殊,,sizeof是编译时刻计算的,因此被看作是常量表达式。


Question 3: 常用STL类型
vector 首当其冲,最好的替代数组的类型。有数组式的使用习惯和STL式的使用习惯,不能随意互用啊:
// 数组习惯
vector<int> vct(10);
for(int i=0 ; i<vct.size() ; ++i)
vct[i]=i;

// STL 习惯
vector<int> vct;
for(int i=0 ; i<10; ++i)
vct.push_back(i);
for(vector<int>::iterator iter=vct.begin() ; iter!=vct.end() ; ++iter)
cout<< *iter << endl;

complex 复数类型,可以用complex<double> 或者 complex<long double>等。
pair 关联两种类型的值。
bitset 向量类型,用 bitset<32>构造向量长度为32。
list,deque,map/multimap, set/multiset
stack 栈,其中与一般栈不同之处是普通栈的 pop删除头并返回头元素,而stl栈只删除不返回,它使用top返回头。
queue和priority_queue,队列和优先队列,priority_queue在Java下满好用的 ^_^。

Question 4: 自己写类
写一个完整的类不容易啊,首先分为三个基本步骤(自己总结的,不知道对不对):
1 类定义,常定义在.h文件中
2 函数、变量定义,.cpp文件的部分
3 添加其他会使用到该类的其他类中的方法,典型的如 istream& operator >>( istream&,类&) 等。

在定义时也能定义部分类函数和变量,在类定义中定义的函数的都是缺省为inline的(使用频率高的,短小的代码可以考虑声明为inline)。在类外定义的需要显示声明inline。

类定义基本结构:

#include <iostream> //包含需要使用的头文件

class Sample; // 先声明一下类

// 声明一些会使用到该类的函数
istream& operator >( istream&, Sample&amp;);

// 类定义
class Sample
{
public:
Sample(); //默认构造函数及其他构造函数
Sample( const Sample&); //拷贝构造函数,如果需要

~Sample(); //析构

Sample& operator = (const Sample&amp;); //重载操作符

void setValue(int); //定义成员函数
private:
int _value; //定义成员变量
};

Question 5: 显式转换
新式(标准C++)强制类型转换: static_cast,dynamic_cast,const_cast,reinterpret_cast
任何非const数据类型的指针都可以被赋值给void* 指针。
const_cast< type >( expression ) 用于转换掉表达式的常量性 (以及volatile对象的volatile性)。
static_cast< type >( expression ) 编译器隐式执行的任何类型转换都可以由它显式完成。
reinterpret_cast< type >( expression) 对于操作数的位模式执行一个比较低层次的重新解释,它的正确性很大程度上依赖于程序员的主动管理。
dynamic_cast<type>( expression )支持运行时刻识别由指针或引用指向的对象。


旧式(标准C++前)强制类型转换:可以替代static_cast、const_cast、reinterpret_cast。

Question 6: 容器
抽象容器类型{
顺序容器(sequence container): list,vector,deque
关联容器(associative container):map,set
}

每个容器支持一组关系操作符,可以用来比较两个容器。第一个不相等元素的比较决定了两个容器的大小关系。
容器的类型有三个限制
  • 元素类型必须支持等于操作符
  • 元素类型必须支持小于操作符
  • 元素类型必须支持一个缺省值
所有预定义数据类型,包括指针,都满足这些限制,C++标准库给出的所有类型也一样。

Question 6: 迭代器
除了iterator类型,还定义了一个const_iterator类型,后者对于遍历const容器是必需的。 iterator算术运算只适用于vector或deque,而不适用于list。

Question 7: 函数
参数为数组,数组确实满麻烦的,首先数组参数绝不会传值,传的是指针。举以下五个例子,前三者相同:

void putValues(int*);
void putValues(int[]);
void putValues(int[ 10 ]);
void putValues(int (&arr)[10]); //参数为10个int的数组,因为参数是引用的,即数组长度成为参数的一部分
void putValues(int matrix[][10], int rowSize); // 第一维长度为rowSize,第二维长度是10
void putValues( vector<int> &vec ); //vector是替代数组的最好类型


指定函数的缺省实参:

int func1(int a,int b=0,int c=0);

int func2(int a,int b,int c=0); // 初始化最右边参数
int func2(int a,int b=0,int c); //可以!此时b c都有缺省参数
int func2(int a,int b=0,int c=0); //错误,bc已经有缺省参数

int func3(int a,int b,int c=func1(1)); //缺省实参可以是任意表达式

int func4(...); // ellipsis 省略号,告知编译器,函数调用时,可以有0或多个实参,而类型未知


命名返回值优化,易犯错误:
1 返回一个指向局部对象的引用。局部对象的生命周期随函数的结束而结束。

Matrix& func()
{
Matrix a;
return a; // 错误,结果将指向一个错误的位置
}



2 函数返回一个左值,对返回值的任何修改都将改变被返回的实际对象。

int& get_val( vector<int> &vi, int ix)
{
return vi[ix];
}

int ai[4] = {0,1,2,3};

vector vec(ai, ai+4);

int main()
{
get_val(vec,0)++;
}


正确使用命名返回值优化:

Matrix& get(Matrix *p)
{
Matrix *res = new Matrix(); //动态分配的,函数结束不会结束
*res=*p;
return *res;
}

函数指针(指向函数的指针类型):

int cmp1(const string &s1, const string &s2)
{
return 1;
}

int cmp2(const string &s1, const string &s2)
{
return 0;
}

int *pf(const string&, const string&amp;amp;amp;amp;amp;amp;); // 错误,返回类型指针
int (*pv)(const string&, const string&amp;amp;amp;amp;amp;amp;) = 0; //初始化,不指向任何东西
int (*pf)(const string&, const string&amp;amp;amp;amp;amp;amp;) = cmp1; //正确,pf 是指向函数的指针
pf=cmp2; //赋值,可以
pf=&cmp1; //可以,同上

cmp1("a","b");
pf("a","b");
(*pf)("a","b"); //三个效果相同,返回1


函数指针的数组(so...神奇):

int func1(const int &a,const int &amp;amp;amp;amp;amp;amp;b);
int func2(const int &a,const int &amp;amp;amp;amp;amp;amp;b);

typedef int (*PFV)(); //定义函数类型指针的typedef
PFV tc1[10]; // 函数指针数组长度为10
PFV tc2 = { func1, func2 }; // 初始化
tc1[0] = &func1; // 赋值
tc1[0](10,20); //调用
((*tc1)[0])(10,20); // 显式调用


函数指针可以作为返回类型,但是函数不能做为返回类型:

int (*func(int))(int, int); // ff为函数,有一个int参数,返回一个指向函数的指针,该指针类型为 int (*)(int, int);

typedef int (*PF)(int, int);
PF ff( int ); //更优雅

typedef int func(int, int);
func ff(int); // 错误,返回类型不能是函数类型



Question 8: extern
链接指示符 extern "C" / extern "Ada" / extern "FORTRAN"
告诉编译器,该函数是使用其他语言编写的:

extern "C" void exit(int);

extern "C"
{
int printf( const char* ...);
int scanf(const char* ...);
}

extern "C"
{
#include <cmath>
}

Question 9: 头文件
头文件为所有extern对象声明、函数声明以及inline函数定义提供了一个集中的位置。
预编译头文件而不是普通头文件可以大大降低应用程序的编译时间。
头文件不应该含有非inline函数或对象的定义。否则将可能使同一程序的两个或多个文件中包含,就会产生重复定义的编译错误。


Question 10: 域和生命期
局部对象:自动对象、寄存器对象、局部静态对象

自动对象地址不应该被用作函数的返回值,因为函数一旦结束了,该地址就指向一个无效的存储区。当一个自动变量的地址被存储在一个生命周期长于它的指针时,该指针被称为空悬指针(dangling pointer)。

寄存器自动对象:在函数中频繁被使用的自动变量可以用register声明。

for( register int ix=0; ix


静态局部对象:未初始化的静态局部对象会被程序自动初始化为0,相反,自动对象的值是任意的,除非它被显式初始化。

动态分配的对象:空闲存储区被耗尽时,new表达式失败,所以抛出一个bad_alloc异常。delete会调用操作符delete(), C++保证若指针被设置为0,则不会调用delete(),故之前没必要测试指针是否为0。但delete之后,并不会把指针赋值为0。

auto_ptr
auto_ptr可以帮助程序员自动管理用new表达式动态分配的单个对象,不支持数组。


#include
auto_ptr< type_pointed_to > identifier( ptr_allocated_bynew );
auto_ptr< type_pointed_to > identifier( auto_ptr_of_same_type );
auto_ptr< type_pointed_to > identifier;

auto_ptr< int > pi(new int(1024));

由于操作支持都是内联的,所以效率不比直接使用指针代价高。ptr相关操作:
reset: 初始化后,就只能用reset再对auto_ptr赋值了。
get: 返回auto_ptr对象内部的底层指针。
release:允许将一个auto_ptr对象的底层对象初始化或赋值给第二个对象,而不会使两个auto_ptr对象同时拥有同一对象的所有权。

定位new表达式:

new ( place_address ) type-specifier // 表达式格式

class Foo{};
char *buf = new char [ 1024 ];
Foo *pb = new ( buf ) Foo; //无须delete
delete[] buf;

韩片《无道里》

最近看了几部电影:《忍者神龟》(美)、《汉江怪物》(韩)、《博物馆之夜》(美)、《无道里》(韩)。发觉韩片真的相当地出色,拍摄手法啊这些专业的东西我是不清楚,但是剧情确实相当有看头的。《无道里》我是今天看的,虽然是喜剧,但却折射出了一些内心的东西。最后,剧情的巧妙给了我一个意外地冲击,流了两滴泪。



这里是“无道里”,除非你想跳崖,不然不会到这里来。一日,在悬崖边的小村庄里居住的三个老头发现悬崖边上留着一双鞋。不久,跳崖者的家属来到了小村庄,当老头把鞋递给跳崖者家属的时候,家属留了笔钱给三老头以表示感谢。于是,一边是跳崖(自杀)胜地的扬名,另一边是老头们商量着如何靠跳崖发财。
再看首尔,一名女记者因为找不到够爆的新闻,正在发愁的时候,在一家网吧意外发现了一名准自杀者留下的〈自杀者天堂〉网页。女记者意识到发财的机会来了!于是出动吧。

大家齐聚“无道里”,晚上开开自杀讨论会,第二天早上去自杀,未遂,晚上再开会,早上再自杀,未遂...... 老头们也在想尽办法催大家赶快自杀...... 你急我也急...... 最后...... 想知道最后怎么着了?自己看电影吧。(注意海报最左边的老头~ 很色的哦 ^_^)

Install Jetspeed with PostgreSQL

Since my graduation design request PostgreSQL, I have to give up MySql. PostgreSQL is quit different from MySql, as most extension commands of MySql, like SHOW TABLES , does not work in Postgre, and the structure of Postgre is entirely unfamiliar for me.

Although the installation of Jetspeed's DB is so simple that just three steps:
1 create a database
2 grant the privilege
3 download the JDBC
which still took me almost two hours to accomplish it, for the account and network configuration is some bit complex.

First of all, write a test java file, which is of great help, the following code (replace 'testdb') should be included in the file:
Class.forName("org.postgresql.Driver");
Connection db = DriverManager.getConnection("jdbc:postgresql://localhost:5432/jetspeed", "jetspeed", "jetspeed");

Compile it, JDBC file was not necessary during compilation phase, but it should be appended to the classpath before executing the test program.

Second, install PostgreSQL if it was not installed before. Source installation would be a better choice, and here is the link of PostgreSQL official site. Install via apt would be the most convenient way, I like apt and ubuntu :) .

Third, initialize a DB cluster(apt installation has already initialize one), like the following command:
$ initdb -D /usr/local/pgsql/data

Find pg_hba.conf and postgresql.conf in the directory of DB cluster. Login the cli environment of PostgreSQL (notice: there are two databases already exist, template0 and template1):
$ postmaster -S -D /usr/local/pgsql/data  #start the db server background
$ psql template0


Fourth, assure that the TCP/IP connection was available. Modify postgresql.conf as below:
listen_addresses='*'
port=5432
tcpip_socket=true


Fifth, modify pg_hba.conf (refer to this):
local all    password
host all all 127.0.0.1/32 password

Caution: Start a new line at the end of file. And restart the server after the configuration completed.

Sixth, create a user and create the database, then grant the control privilege to the user. There're several ways to do it, just like this one:
$ createuser -d -P  # create a user with the privilege of database create and a prompt for the password of the new userd
... # input the user info,like username is 'jetspeed' and password is 'jetspeed'
$ createdb jetspeed -U jetspeed -W # create a new db and the creator is 'jetspeed'
# if error reported, maybe pg_hba.conf was incorrected configured.


Seventh, run the JDBC test. If the test succeed, there would be no problem of Jetspeed installation.

Saturday, April 14, 2007

2007年4月的第二周

这一周很忙,也很闲。
首先是上周五,毕业设计需要我去合作的那家单位走一趟。早早地我就起床了,按着昨日网上找好的路线找了2个半小时左右,后来与老师电话后才知道,我找错单位了 -_-b 。随后老师让我不用去了,他们谈好了再通知我。心中一边抱怨着这个不知道在瞎忙什么的毕业设计,一边
低着头沮丧得走着那2小时的回家路。

周一、周二,我奉献给了学校,也就是我那毕业设计。因为了是签了合同的项目,老师又希望我冲优,所以我一直也不敢怠慢。但是,那个单位不懂装懂的一套莫名的号称“创新”的设计实在是把我惹得对该项目厌恶之极。两天的下午,我就坐在那里(傻傻地),装着一些我认为几乎没有价值的,至少对我的毕设而言是没价值的,却又是相当复杂的系统(globus toolkit)。而我要做的那部分(portal),已经完全被搁置了,写完前端后一直等那个单位给我接口,但最后等到的,除了“下周给”还是“下周给”。不是说他们不干事,真的,为什么这么一家还算不错的公司竟然效率这么低呢。

周三,topcoder的marathon结束了。由于浪费了太多时间在毕设上,周三想恶补一下tc的,但最后还是没能如愿,反而最后一次提交的成绩还不如之前的那次好。说实话,这次的marathon是很有趣的,我真的满想静下心来认真做一下。但是除了毕设,周四还有一个相当重要的面试。

周三早5点,我起床,为了周四不知道,周三先去摸摸情况。2小时的路程将我整得相当疲惫,又是2小时的回程,回到家里人像一滩泥,散在椅子上了。不过我硬撑着,如果现在睡了,那么晚上我一定睡不着,所以为了明天早起,不能睡!

周四早5点,起床,准备就绪,出门。今天有个大面试,之所以如此重视它,除了公司名气相当大之外,还有两个原因: 一是已经有我的两个好伙伴收到了公司offer(就是他们推荐我的),为了我们能一起在这家公司里闯事业,我很希望能够进这家公司;二是该公司的面试十分专业,除了让我感受到了公司招人的诚心之外,公司的办事效率也相当值得称赞。面试要持续到下午,做了10道左右的题目,其中写了完整程序的有5道好像。有一道卡住一会,但在面试官的提醒下还是说出了解题的关键算法。还有一道与面试官在题意上有冲突,虽然一开始和面试官有点交流不畅,但后面的两个问题还是解决得满顺的。第二面十分完美,除了题目在很短的时间内做出来以外,中间发生了一段小插曲:我的肚子叫了(-_-b 早饭没吃饱啊),面试官很友善地给我拿了咖啡和饼干,咖啡很好喝。另外,最后一位面试官送我走之前,给了我一个KFC汉堡包,^_^,对公司好感度顿时增加到100%!总体感觉还是不错的,事后我有发觉我的程序还是有漏洞的,但我想结果并不重要,我把我解决问题的思路和方法展现给了面试官,所以面试还是成功的。

面试结束后,我去超市买了不少吃的,脑力和体力消耗太多,需要补一下。晚7点,做tc算法的round 1c,虽然人还是有点疲倦,但听说过round 1就有飞盘~,拼了!第一题很容易就解决了。第二题卡了,写了个DP,但结果始终不对。想到了二分答案,但是我不会写二分答案的算法(知道有点像二分搜索,但因为从来没写过,一下子也摸不着头脑)。结果只做了第一题,最后的结果是排名152名好像。晋级是没有问题的,只是只做了一题,始终感觉不是很爽。过两天就是round 2,+U。

今天marathon成绩出来了,排名150+,不理想,rating也跌了不少。另一件关于tc的事是我的blog增加了一个看tc算法赛事的widget。原本我用flash写的一个,有图片,类tc个人资料的那个flash的。但后来发觉导出成swf就怎么都连不上topcoder,那就拿不到feed了,但是exe和直接在flash IDE下调试是完全成功的。郁闷,不能出swf那就不能挂在blog上了,所以花了半天时间再写了一个javascript的,只是是文字,跟图表的效果是没法比的。



附加一个用CSS 做 tooltip的好文章链接:
http://www.communitymx.com/content/article.cfm?cid=4E2C0&print=true#

为什么我一开始说很忙又很闲呢,忙得是这周事情不少,闲的是大部分事情都只是在花时间和体力,比如毕设、flash的tc rating表白做了和面试那两天的8个小时路程,总有一种在浪费时间的感觉。

Saturday, April 07, 2007

Cute Otters.

Otters hand by hand, looks so sweet.

I saw them on the news and decide to check it out, they're stars now!

Tuesday, April 03, 2007

TCO Marathon Online Round #2

TCO Marathon OR2 finished. I got 65/445 rank, 11331.73 score, and advance to round 3 successfully. My friend, FinalLaugh, got rank 191, while he has got 200 rank when the submission parse finished, just by a finger's breadth. The path to Las Vegas is becoming more and more narrow, next time , we will fight for 50 contestants position.

The top 2 players' program scares me a lot...... So many codes that looks like "AA>s;oI2A". As we, FinalLaugh and I, discussed, it's a technology of compress.

I dislike this problem, two reasons: 1st, it's a gambling game; 2nd, it's solution is not so hard, but with a lot of work. My solution is so easy to implement , somewhat like saarixx (rank 7) did.

First of all, list all card combinations: {4,4} {3,3} ... 15 cases from high to low. Then, provide a simple solution to newcard(), round1(), draw() then take them into an array, each owns 15 elements, just like this:


 private int[] newc = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 };
private int[] rd1 = { 2, 2, 2, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 };
private int[] draw = { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2 };



For these three method, I think, a fixed strategy will be ok. As to round2(), it's more complexity, but as I'm so lazy.... I realize it like this:


   public int round2(int card1, int card2, int bets1, int bets2, int drew) {
int idx = getCardIdx(card1, card2);

// pre solution, like the too little card will return 0 immediately

switch (drew) {
case 2:
// some solution
case 1:
// some solution
case 0:
// some solution
}
return -1;
}



That's all, so simple but strong enough to advance to the next round. Wish my good luck next round!

Friday, March 30, 2007

TCO Marathon Online Round #1

TCO Marathon OR1 and OR2 has finished today, which consume my thoughts for two weeks.

To OR1, I got 1309.01 at submission phase and 4990.66 at end, final rank 67/532. See the problem here(need login), which is a really interesting and challenge game. As my solution, the image could be considered as a onion, the target of this prob is to peel the onionskin. So, here is the steps of my algorithm:

1. Generate the rays, those rays will be the easy calculating ones. (Attention, generate rays does not mean call the measure() method.)
2. Pick out the rays that just cross one unit('x' unit), and store them in a queue.
3 Iterate the selected rays, calculate the density of that unit(call measure() here), and then mark the unit as calculated.
4 Repeat step 2, but the more repetition we used, the more precision we will lost. It should be a bound to control the precision before we start this algorithm.

As to step one, the rays should be selected meet the condition that they will be easy to calculate, otherwise, it will be a great amount work of calculation and simulation. As I considered, each ray will start from one of the four corners of the unit, and the ray should cross exactly n row unit while it crossed 1 col unit, and vice versa. The main reason I ensure this condition is that every length of the row unit( according to my example) that the ray would cross is the same, and that would lower the complexity of the ray calculation. As a result, when n is greater than 30(I forget the exactly number, but 30 is enough), every unit will be covered.

As to step four, the repetition times is a hard decision. The performs of different repetition times would result in a normal distribution, and the peak of the distribution is the one we desired. I choose the repetition according to a great amount of test.

Wednesday, March 14, 2007

《大唐西游记》 [探索·发现]

记录片《大唐西游记》,制作在国内算是十分精良的了。全力推荐!

音效出众!配音出色!
对《西游记》名著的动画表现,充满了活力!
而对历史的重现采用虚拟3D技术,虽然技术上还有不是十分完善。但足以用来描述好这段神奇的冒险故事了。

推荐!强力推荐!
喜欢记录片的朋友千万不要错过了!

Wednesday, March 07, 2007

解决系统盘空间不足

我的系统盘只有4G。虽然平时我已经很注意保持系统盘的清爽,但还是总免不了“磁盘空间不足”的麻烦。满惹人的,于是在网上搜搜有没有达人能给我一点好的idea。没想到确有高手,以下是该文的引用:


其中第7、8条真是杀手锏!把100M不到的空间释放到1G多,汗颜,原来有这么多垃圾没清理掉。
第9-12条是清除平时不用的windows文件的,有一点危险性,不过知道东东的含义就不怕啦。

13、14和27的bcd都是值得推荐的方法,算是比较温和的方法却能很大程度上改观当前系统盘的尴尬局面。