2010年1月4日
bufpos插件的下载地址:
http://www.vim.org/scripts/script.php?script_id=1836
buftabs插件的下载地址:
http://micampe.it/files/buftabs.vim
这是bufpos的作者做了小修改的版本的buftabs.
将下载的bufpos.vim 和 buftabs.vim放到
~/.vim/plugin/下面
vim不一定是安转在上面这个地址,但是可以自己建立这样的目录,用来放自己下载的插件,也方便管理备份。
然后在~/.vimrc文件(我自己建立的,自定义的配置信息都写在这里面)中添加:
" buftabs settings
set laststatus=2
let g:buftabs_in_statusline=1 "显示标签栏
map <C-b> :bprev!<CR> "设置向后切换的快捷键"
map <C-n> :bnext!<CR> "设置向前切换的快捷键"
然后打开gvim ,当打开多个文件的时候,文件窗口下面就会有一栏显示打开的文件:

如图片中显示:1-Program/test/a.c 和 2-Program/test/b.c
这个时候可以用两种方式切换代码:
一是:Ctrl-N 向后或者 Ctrl-B向前切换
二是:Alt-N (N 就是一个数字,比如上面的1或者2)
By:洪庚伟
2010年1月3日
最近对问题:这个问题非常简单,在一个平面中有N个点,查找距离最近的两个点。用蛮力法解决最简单,两两匹配,找出最近的一对,不过用蛮力法只局限于点比较少的情况,因为蛮力法要达到O(N*N)的时间复杂度。如果用分治的算法来做可以达到O(nlogn) 的时间复杂度,实际上需要多一个O(nlogn)的时间来就行预排序。
这两天用分治的思想实现平面最近对算法,并同时写了一个蛮力查找的算法作比较,果然用分治的算法快了很多。写的过程主要使用STL的容器存储数据,而算法中使用到的排序算法也没有自己实现,就直接使用STL中的sort排序。写的过程都把自己的思路写在注释上面了。
一、代码实现:
算法声明文件:CloestCouple.h
//-----------------------------------------
// CloestCouple.h
// 最近对算法
// 使用分治法实现
// 洪庚伟 2010.01.03
// http://www.cnblogs.com/G_Weber
//-----------------------------------------
#ifndef _CLOESTCOUPLE_HGW_
#define _CLOESTCOUPLE_HGW_
#include<vector>
#include<algorithm>
#include<cmath>
//分治法计算最近对
class CCloestCouple
{
public:
struct Point
{
int x;
int y;
Point():x(0),y(0){}
Point(int ix,int iy)
:x(ix),y(iy)
{}
};
//最近对算法还需要对点按照x轴、y轴进行排序
//直接使用stl算法排序
//因此需要编写比较点的函数对象
//按x轴非递减排序
struct PointCmpX
{
bool operator()(const Point & lhr,const Point & rhr)
{
if(lhr.x < rhr.x)
return true;
else if(lhr.x > rhr.x)
return false;
else
return lhr.y < rhr.y;
}
};
//按y轴非递减排序
struct PointCmpY
{
bool operator()(const Point & lhr,const Point & rhr)
{
if(lhr.y < rhr.y)
return true;
else if(lhr.y > rhr.y)
return false;
else
return lhr.x < rhr.x;
}
};
typedef std::vector<Point> PointVector;
public:
//给出平面的一个点的集合,分治找出最近的两个点
//算法基本是在位运行
void find(PointVector& Points,Point& from,Point& to);
//计算两点距离的平方
float distance(const Point &from,const Point &to);
//另外编写了一个蛮力法找最近对,用来检查分治法的正确性
//以及比较运行效率
void ForceFind(PointVector& Points,Point& from,Point& to);
private:
//对有序的点集合Points[begin,end),递归返回最近对两个点from 和 to
void _find(PointVector& Points,int begin,int end,Point& from,Point& to);
//在left、right 两个集合中查找是否有距离小于
//ldis,的两个分别分布于left和right的点对,
//用他们的值改写 from to
//由于分治找出最近对后,还有可能存在这样的最近对:
//两个点位于不同的分治组合,因此要有这一步检查
void compare(PointVector &left,PointVector &right,
float dis,Point &from,Point &to);
};
#endif
还有的是算法的实现文件:CloestCouple.cpp
//-----------------------------------------
// CloestCouple.cpp
// 最近对算法
// 使用分治法实现
// 洪庚伟 2010.01.03
// http://www.cnblogs.com/G_Weber
//-----------------------------------------
#include "CloestCouple.h"
void CCloestCouple::find(PointVector &Points,Point &from,Point &to)
{
if(Points.size() < 2)
return;
//先对输入点集合做按x轴的排序
std::sort(Points.begin(),Points.end(),PointCmpX());
//递归分治查找
_find(Points,0,Points.size(),from,to);
}
void CCloestCouple::_find(PointVector &Points,int begin,int end,Point &from,Point &to)
{
if(end - begin == 2)
{
//两个点,直接返回结果
from = Points[begin];
to = Points[begin+1];
}
else if(end - begin == 3)
{
//三个点 A、B、C
//先找出 AB近 还是 AC近,保存在 (from,to)中
from = Points[begin];
to = Points[begin+1];
float ab = distance(from,to);
float ac = distance(from,Points[begin+2]);
if( ab > ac )
{
to = Points[begin+2];
ab = ac;//始终用ab保存最小距离
}
//再与BC作比较
if(ab > distance(Points[begin+1],Points[begin+2]))
{
from = Points[begin+1];
to = Points[begin+2];
}
}
else /*if(end - begin >= 4)*/
{
//当前集合元素足够多,平分成两份分治处理
int mid = begin + (end - begin) / 2;
//假设这两个集合根据直线 x=c进行划分的
float c = (Points[mid].x + Points[mid-1].x) / 2;
Point rfrom,rto;
Point lfrom,lto;
//递归找出两个集合中的最近对
//在区间[begin,mid)中查找
_find(Points,begin,mid,lfrom,lto);
//在区间[mid,end)中查找
_find(Points,mid,end,rfrom,rto);
//计算较近的一对
float ldis = distance(lfrom,lto);
float rdis = distance(rfrom,rto);
//总是用ldis,lfrom,lto保存最近的距离
if(ldis > rdis)
{
lfrom = rfrom;
lto = rto;
ldis = rdis;
}
//在左集合中提取位于[c-d,c]中的点
//在右集合中提取位于[c,c+d]中的点
PointVector left;
PointVector right;
//dis是距离的平方
float d = sqrt(ldis);
int i;
for(i = mid-1; i>=begin && c - Points[i].x <= d;i--)
left.push_back(Points[i]);
for(i = mid; i < end && Points[i].x - c <= d;i++)
right.push_back(Points[i]);
//在left、right 两个集合中查找是否有距离的平方小于
//ldis,的两个分别分布于left和right的点对,
//用他们的值改写 lfrom lto
compare(left,right,ldis,lfrom,lto);
//记录返回值然后返回
from = lfrom;
to = lto;
}
}
float CCloestCouple::distance(const Point &from,const Point &to)
{
return (from.x - to.x ) * (from.x - to.x)
+ (from.y - to.y) * (from.y - to.y);
}
void CCloestCouple::compare(PointVector &left,PointVector &right,
float dis,Point &from,Point &to)
{
//先对两个集合按照y轴排序
std::sort(left.begin(),left.end(),PointCmpY());
std::sort(right.begin(),right.end(),PointCmpY());
//dis是距离的平方
float d = sqrt(dis);
//遍历左集合,逐个跟右集合中的点进行比较
//考虑到集合的已按y轴排好许了
//如果遇到right集合中的点y坐标比
//left集合中当前点的y坐标大d,就可以结束了
PointVector::iterator lit = left.begin();
for(;lit!=left.end();lit++)
{
PointVector::iterator rit = right.begin();
for(;rit!=right.end();rit++)
{
if(lit->y - d > rit->y)
continue;
if(lit->y + d < rit->y)
break;
//(x,y)为当前left集合正在处理的点
//只有right中的点位于[y-d,y+d]
//才有可能两个集合中的点距离小于d
float tdis = distance(*lit,*rit);
if(tdis < dis)
{
from = *lit;
to = *rit;
dis = tdis;//再次记录最近一对
}
}
}
}
void CCloestCouple::ForceFind(PointVector& Points,Point& from,Point& to)
{
//嗯···暴力查找,超简单的代码
float dis=distance(*Points.begin(),*Points.end());
PointVector::iterator lit,rit;
lit = Points.begin();
for(;lit!=Points.end();lit++)
{
rit=lit+1;
for(;rit!=Points.end();rit++)
{
float newd = distance(*lit,*rit);
if(dis > newd)
{
dis = newd;
from = *lit;
to = *rit;
}
}
}
}
二、测试程序
写了个Win32窗口测试一下代码
1.首先是随便点几个点,测试结果分治法和蛮力法都是一样的,看不出区别:
2.自动生成5000个点的时候,这时分治法的效率已经开始表现出优势了
3.用100000个点进行测试的时候,分治法还能高效处理,蛮力法直接让程序用完CPU,只得中断进程
最近对算法演示程序下载
By:洪庚伟
出处:http://www.cnblogs.com/G_Weber
2009年12月29日
学习了分治的思想,并看了凸包问题的理论分析,然后就用代码实现了凸包算法。凸包算法跟快速排序很类似,所以叫快包!
一、几何知识
算法涉及到了一个平面几何的知识。就是三角形p1p2p3的面积等于以下行列式的二分之一:
| x1 y1 1 |
| x2 y2 1 | = x1*y2+x3*y1+x2*y3-x3*y2-x2*y1-x1*y3
| x3 y3 1 |
= x1*y2-x2*y1+x3*(y1-y2)+y3*(x2-x1)
而且当点P3 在射线P1P2的左侧的时候,表达式为正,右侧表达式为负,三点同线的话表达式为0;算法中就利用该几何特性判断一个点在一条线的左侧还是右侧。
二、代码实现
习惯性把算法封装在一个类里面,只是不喜欢很多全局定义类型而已,留出接口
算法声明文件:ConvexHull.h
#ifndef _CONVEXHULL_HGW_
#define _CONVEXHULL_HGW_
/*
凸包算法
使用分治思想解决
洪庚伟 2009.12.28
http://www.cnblogs.com/g_weber
*/
#include<vector>
#include<stack>
class CConvexHull
{
public:
//点
struct Point
{
float x;
float y;
Point(float a,float b)
:x(a),y(b)
{}
Point()
:x(0),y(0)
{}
};
//线
struct Line
{
Point from;
Point to;
Line(Point f,Point t)
:from(f),to(t)
{}
};
//该算法可以用递归实现,我这里是用非递归实现
//所有一个辅助栈实现
struct StackElement
{
//当前需要处理的点的集合的索引
std::vector<int> PointsIndex;
Point from;
Point to;
//在划分集合的同时计算出一个最大点
Point Pmax;
};
typedef std::vector<Point> PointVector;
typedef std::vector<Line> LineVector;
typedef std::stack<StackElement> ConvexHullStack;
public:
//给定点的集合,返回由线组成的凸包
void go(PointVector & Points,LineVector &Lines);
};
#endif
实现文件ConvexHull.cpp就实现了一个函数:
#include "ConvexHull.h"
void CConvexHull::go(PointVector & Points,LineVector &Lines)
{
Lines.clear();
if(Points.size() < 2)
return;
//1.找出点中最左和最右的两个点,这两个点一定是凸包顶点
Point mostLeft = *(Points.begin());
Point mostRight = *(Points.begin());
PointVector::iterator it = Points.begin();
while(it != Points.end())
{
if(it->x < mostLeft.x )
mostLeft = *it;
if(it->x > mostRight.x)
mostRight = *it;
it++;
}
//2.把集合分治为两个集合,上包和下包
StackElement UpHull; //上包
StackElement DownHull; //下包,实际处理跟上包一样
UpHull.from = mostLeft;
UpHull.to = mostRight;
DownHull.from = mostRight;
DownHull.to = mostLeft;
// |x1 y1 1|
//行列式 |x2 y2 1| = x1*y2+x3*y1+x2*y3-x3*y2-x2*y1-x1*y3
// |x3 y3 1|
// = x1*y2-x2*y1+x3*(y1-y2)+y3*(x2-x1)
//当P3位于射线P1P2的左侧时,该表达式为正。
//同时该表达式的值为三角形P1P2P3的面积的一半
//遍历所有点
it = Points.begin();
//减小while循环负担的辅助变量
//即先求出 x1*y2-x2*y1
float xy = UpHull.from.x * UpHull.to.y -
UpHull.to.x * UpHull.from.y;
float max = 0;
float min = 0;
//遍历所有的点,划分为两个集合
//同时求出两个集合中的最大点
while(it != Points.end())
{
//计算行列式的值
float express =
xy + it->x*(UpHull.from.y - UpHull.to.y)+
(UpHull.to.x - UpHull.from.x) * it->y;
if(express > 0.0f)
{
//当前点在P1P2的左端,划分到上包
//记录索引而已
UpHull.PointsIndex.push_back(it - Points.begin());
//同时算出面积最大的一个点
if(express > max)
{
max = express;
UpHull.Pmax = *it;
}
}
//如果不能划分到上包,那么可能划分到下包
if(express < 0.0f)
{
DownHull.PointsIndex.push_back(it - Points.begin());
if(express < min)
{
min = express;
DownHull.Pmax = *it;
}
}
it++;
}
//将上包和下包压入栈,等待进一步处理
ConvexHullStack chstack;
chstack.push(UpHull);
chstack.push(DownHull);
//3.分治处理上包和下包
do
{
StackElement element = chstack.top();
chstack.pop();
//该集合当中不需要再扩展,则两个点就为组成凸包的线段
if(element.PointsIndex.size() == 0)
{
Lines.push_back(Line(element.from,element.to));
}
else
{
//再次将当前集合划分到不同的集合中去
StackElement left,right;
left.from = element.from;
left.to = element.Pmax;
right.from = element.Pmax;
right.to = element.to;
float express;
float leftmax = 0;
float rightmax = 0;
std::vector<int>::iterator indexit=element.PointsIndex.begin();
while(indexit != element.PointsIndex.end())
{
//检测是否在 left的左侧
express = left.from.x * left.to.y - left.to.x *left.from.y +
Points[*indexit].x * (left.from.y - left.to.y) +
Points[*indexit].y * (left.to.x - left.from.x);
if(express > 0.0f)
{
left.PointsIndex.push_back(*indexit);
//同时算出面积最大的一个点
if(express > leftmax)
{
leftmax = express;
left.Pmax = Points[*indexit];
}
indexit++;
continue;
}
express = right.from.x * right.to.y - right.to.x *right.from.y +
Points[*indexit].x * (right.from.y - right.to.y) +
Points[*indexit].y * (right.to.x - right.from.x);
if(express > 0.0f)
{
right.PointsIndex.push_back(*indexit);
//同时算出面积最大的一个点
if(express > rightmax)
{
rightmax = express;
right.Pmax = Points[*indexit];
}
indexit++;
continue;
}
indexit++;
}
chstack.push(left);
chstack.push(right);
}
}while(!chstack.empty());
}
三、测试程序
为了测试算法,不得不写了一个可视化的程序来测试。总不能在Dos程序下一个个点输入坐标,然后观察输出吧。实在看不出结果,就写个小程序测试,比较直观可以判断算法是否正确
凸包演示程序
测试截图:
By:洪庚伟
出处:http://www.cnblogs.com/G_Weber
2009年12月28日
继续实现书上的算法,现在是分治法咯。很多基础的算法都是分治思想的,而分治这个思想也符合我们平时的思维方式,将问题简化为同个类型但是规模较小的问题,化大为小,通过解决众多小问题而最终实现解决大问题。
先解决下几个比较简单的基础问题,然后再另开两篇解决“凸包问题”和“最近对问题”。
一、合并排序
// 将有序的data[i,m],data[m+1,n]合并到data[i,n]
// temp为临时空间
void merge(int data[],int temp[],int i,int m,int n)
{
int pos=i;
int j,k;
for(j=m+1,k=i;i<=m&&j<=n;k++)
{
if(data[i]<data[j])
temp[k]=data[i++];
else
temp[k]=data[j++];
}
//归并剩余之一
while(i<=m)
temp[k++]=data[i++];
while(j<=n)
temp[k++]=data[j++];
//重新复制到原数组
for(k--;k>=pos;k--)
data[k]=temp[k];
}
//归并排序data[i,j],还需提供额外空间temp[i,j]
void msort(int data[],int temp[],int i,int j)
{
if(i<j)
{
int mid = (i+j)/2;
//分治归并,temp为临时空间
msort(data,temp,i,mid);
msort(data,temp,mid+1,j);
merge(data,temp,i,mid,j);
}
}
//对数组data[n]进行归并排序
void MergeSort(int data[],int n)
{
int * temp = new int[n];
msort(data,temp,0,n-1);
delete [] temp;
}
合并排序虽然可以达到O(nlogn)的时间复杂度,但是它确需要线性的额外空间。对于内部排序来说,实在是找不到使用二路归并排序的理由;但是在外部排序,使用多路归并排序就应该能解决一下实际问题。
二、快速排序
算是非常好的内部排序算法了,首先编码够简单,效率也非常高:最好的情况是 O(nlogn);根据书上的分析,其平均效率也只需要O(1.38logn);就是最差效率为O(n*n)这个缺点,但是一般比较少遇到这种情况,也有其它的解决方案。
//快排中的分割算法
//对 data[low,high]进行分割、返回枢轴
int Partition(int data[],int low,int high)
{
int temp = data[low];
while(low < high)
{
while(low < high && data[high] >= temp)
high--;
data[low] = data[high];
while(low <high && data[low] <= temp)
low++;
data[high] = data[low];
}
data[low]=temp;
return low;
}
//快排中的递归排序
//对data[low,high]进行快排
void QSort(int data[],int low,int high)
{
if(low < high)
{
int pivotloc = Partition(data,low,high);
QSort(data,low,pivotloc-1);
QSort(data,pivotloc+1,high);
}
}
三、二分查找法
对于查找有序数组,二分查找算是非常好了。效率高:O(logn),代码实现起来又非常简单:
//二分查找算法
//在非递减数组data[low,high]查找key,返回位置
int BinarySearch(int data[],int low,int high,int key)
{
while(low < high)
{
int mid = (low+high)/2;
if(data[mid] > key)
high = mid -1;
else if(data[mid] < key)
low = mid + 1;
else
return mid;
}
return -1;
}
嗯嗯,非常简单的几个算法,分治法。接下来写一个比较复杂的算法:凸包!
By:洪庚伟
http://www.cnblogs.com/g_weber
2009年12月23日
首先,添加ScribeFire插件,在下面网址添加就可以了。
https://addons.mozilla.org/en-US/firefox/addon/1730
重启浏览器后如果插件安装成功,那么FireFox右下角就出现一个记事本图标,点击打开ScribeFire,按F8也同样可以打开。
使用用户向导添加用户,对于博客园的用户,就输入个人博客的主页:
http://www.cnblogs.com/g_weber
然后点击下一步,ScribeFire已经检测出来博客API的类型了,如果没有的话那就选择相应的,比如博客园的MetaWebblog API。
然后API Url写入:
http://www.cnblogs.com/你的博客名称/services/metablogapi.aspx
我就写入了
http://www.cnblogs.com/g_weber/services/metablogapi.aspx
再下一步,填入博客的后台用户名和密码,然后就搞定了!
现在我就是在Ubuntu下用FireFox发布这篇文章~
By:洪庚伟
2009年12月22日
摘要: 很久之前发布了一个小外挂,是我自己捣鼓出来的QQ游戏连连看外挂。 见:http://www.cnblogs.com/G_Weber/archive/2009/06/02/1494871.html 在做这个外挂的时候,还是有一点点基于对象的思想的,小弟才疏学浅,还不敢说自己是做到面向对象。说基于对象,就是对其中最核心的消去算法做了封装,...
阅读全文
2009年12月16日
摘要: 要重温算法思想,并以《算法设计与分析基础》这本书作为教材。该书每一章介绍一种算法设计思想。今天从最简单的开始写起,打好基础。以后再逐步深入,学习更深入的算法。 蛮力法就是一种解决问题的最简单最直观的最容易理解方法,虽然它简单,而且在实际应用中因为效率的原因可能不能派上用场,但是还是不能忽略它。正如书中作者所说,在解决小规模问题的时候也不失为一个方法,而且也是更...
阅读全文
2009年12月15日
摘要: 过去大学这三年随便也勉强算是写过几万行代码以上,不好意思,我把重复功能的代码页算上。但是这些代码都是重应用层次的,缺少底层算法的实践。平时参加过的竞赛也是软件设计的,没有参加过ACM、Topcoder等之类的算法竞赛。长时间下来,自己的算法水平也就是停留在数据结构的基本水平,会写个快速排序,冒泡或堆而已。算法功底不过关,让我这段时间在找工作的过程中碰到了钉子。...
阅读全文
2009年6月2日
摘要: 这几天期末考,完全没有考试的心情,又无聊到玩起连连看,可惜输得太多,非常不爽,于是自己做个连连看外挂玩一下。网上有很多连连看的外挂可以下载,不过自己做一个来用,感觉自然不一样,毕竟还是学计算机的嘛~~这里简要记下做的过程,有兴趣自己写的朋友可以参考参考~~~ 我是用MFC做的~开发环境:Xp sp2 、Visual Studio.NET 2003 一、QQ游戏连连看玩法。 玩法非常简单,用鼠标...
阅读全文
2009年5月19日
摘要: 突然发现在Ogre里面Vector3居然没有相关的旋转的方法,不过这样也合理,设计Vector3不需要考虑到这个。 还好,用Ogre里面的四元数Quaternion可以很容易做到按照任意轴旋转一个Vector3。只需要构造一个旋转四元数就可以,使用该接口: Quaternion::FromAngleAxis ( const Radian & rfAngle, const Vector3...
阅读全文