G_Weber

Keep Hungry Keep Stupid !

公告

统计

2010年1月4日

vim中使用bufpos和buftabs两个插件实现文件标签式管理

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:洪庚伟



posted @ 2010-01-04 21:26 G_Weber 阅读(350) 评论(0) 编辑

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.首先是随便点几个点,测试结果分治法和蛮力法都是一样的,看不出区别:
1

2.自动生成5000个点的时候,这时分治法的效率已经开始表现出优势了
2
3

3.用100000个点进行测试的时候,分治法还能高效处理,蛮力法直接让程序用完CPU,只得中断进程
4

最近对算法演示程序下载

 

By:洪庚伟

出处:http://www.cnblogs.com/G_Weber

posted @ 2010-01-03 21:40 G_Weber 阅读(1049) 评论(0) 编辑

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程序下一个个点输入坐标,然后观察输出吧。实在看不出结果,就写个小程序测试,比较直观可以判断算法是否正确
凸包演示程序
测试截图:
CH

 

By:洪庚伟

出处:http://www.cnblogs.com/G_Weber

posted @ 2009-12-29 22:59 G_Weber 阅读(1105) 评论(2) 编辑

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

posted @ 2009-12-28 21:39 G_Weber 阅读(175) 评论(0) 编辑

2009年12月23日

用FireFox插件ScribeFire写博客

首先,添加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:洪庚伟

posted @ 2009-12-23 15:29 G_Weber 阅读(53) 评论(0) 编辑

2009年12月22日

连连看外挂消去算法分析

摘要:     很久之前发布了一个小外挂,是我自己捣鼓出来的QQ游戏连连看外挂。    见:http://www.cnblogs.com/G_Weber/archive/2009/06/02/1494871.html 在做这个外挂的时候,还是有一点点基于对象的思想的,小弟才疏学浅,还不敢说自己是做到面向对象。说基于对象,就是对其中最核心的消去算法做了封装,...阅读全文

posted @ 2009-12-22 23:53 G_Weber 阅读(492) 评论(0) 编辑

2009年12月16日

《算法设计与分析基础》学习 --- 蛮力法

摘要: 要重温算法思想,并以《算法设计与分析基础》这本书作为教材。该书每一章介绍一种算法设计思想。今天从最简单的开始写起,打好基础。以后再逐步深入,学习更深入的算法。     蛮力法就是一种解决问题的最简单最直观的最容易理解方法,虽然它简单,而且在实际应用中因为效率的原因可能不能派上用场,但是还是不能忽略它。正如书中作者所说,在解决小规模问题的时候也不失为一个方法,而且也是更...阅读全文

posted @ 2009-12-16 20:46 G_Weber 阅读(428) 评论(0) 编辑

2009年12月15日

重新从头踏实学习算法

摘要:     过去大学这三年随便也勉强算是写过几万行代码以上,不好意思,我把重复功能的代码页算上。但是这些代码都是重应用层次的,缺少底层算法的实践。平时参加过的竞赛也是软件设计的,没有参加过ACM、Topcoder等之类的算法竞赛。长时间下来,自己的算法水平也就是停留在数据结构的基本水平,会写个快速排序,冒泡或堆而已。算法功底不过关,让我这段时间在找工作的过程中碰到了钉子。...阅读全文

posted @ 2009-12-15 22:59 G_Weber 阅读(181) 评论(0) 编辑

2009年6月2日

自制 QQ游戏 连连看 外挂 ~~

摘要: 这几天期末考,完全没有考试的心情,又无聊到玩起连连看,可惜输得太多,非常不爽,于是自己做个连连看外挂玩一下。网上有很多连连看的外挂可以下载,不过自己做一个来用,感觉自然不一样,毕竟还是学计算机的嘛~~这里简要记下做的过程,有兴趣自己写的朋友可以参考参考~~~ 我是用MFC做的~开发环境:Xp sp2 、Visual Studio.NET 2003 一、QQ游戏连连看玩法。 玩法非常简单,用鼠标...阅读全文

posted @ 2009-06-02 21:27 G_Weber 阅读(783) 评论(0) 编辑

2009年5月19日

在Ogre中旋转一个Vector3

摘要: 突然发现在Ogre里面Vector3居然没有相关的旋转的方法,不过这样也合理,设计Vector3不需要考虑到这个。 还好,用Ogre里面的四元数Quaternion可以很容易做到按照任意轴旋转一个Vector3。只需要构造一个旋转四元数就可以,使用该接口: Quaternion::FromAngleAxis ( const Radian & rfAngle, const Vector3...阅读全文

posted @ 2009-05-19 23:25 G_Weber 阅读(779) 评论(0) 编辑