问题描述
求解下面的非线性方程组

其中,x1,x2,…,xn是实变量,fi是未知量x1,x2,…,xn的非线性实函数。要求确定上述方程组在指定求根范围内的一组解
。
问题分析
解决这类问题有多种数值方法,如:牛顿法、拟牛顿法、粒子群算法等。最常用的有线性化方法和求函数极小值方法。为了求解所给的非线性方程组,构造一目标函数

式中,x=(x1,x2,……xn)。易知,上式取得极小值点即是所求非线性方程组的一组解。
求解思路
在指定求根区域D内,选定一个随机点x0作为随机搜索的出发点。在算法的搜索过程中,假设第j步随机搜索得到的随机搜索点为xj。在第j+1步,计算出下一步的随机搜索增量dxj。从当前点xj依dxj得到第j+1步的随机搜索点。当x<
时,取为所求非线性方程组的近似解。否则进行下一步新的随机搜索过程。
题外话:笔者在读王晓东《算法设计与分析》中这一节时,发现书上所给的代码似乎有些问题。在这里指出,如果提得不对,还请大侠们拍砖。书中给出的代码具体如下:
- bool nonlinear(double *x0,double *dx0, double *x,double a0,
- double epsilon, double k,int n, int steps,int m)
- {
- static randomnumber rad;
- bool success;
- double *dx, *r;
- dx=new double[n+1];
- r=new double[n+1];
- int mm=0;
- int j=0;
- double a=a0;
- for(int i=1;i<=n;i++)
- {
- x[i]=x0[i];
- dx[i]=dx0[i];
- }
- double fx=f(x,n);
- double min=fx;
- while ((min>epsilon)&&(j<steps))
- {
- if(fx<min)
- {
- min=fx;
- a*=k;
- success=true;
- }
- else
- {
- mm++;
- if (mm>m)a/=k;
- success=false;
- }
- for(int i=1;i<=n;i++)
- r[i]=2.0*rnd.fRandom()-1;
- if(success)
- for(int i=1;i<n;i++)
- dx[i]=a*r[i];
- else
- for (int i=1;i<n;i++)
- dx[i]=a*r[i]-dx[i];
- for (int i=1;i<n;i++)
- x[i]+=dx[i];
- fx=f(x,n);
- }
- if(fx<=epsilon)
- return ture;
- else
- return false;
- }
问题1:
while ((min>epsilon)&&(j<steps))句中,循环控制变量j<steps,但在循环体中,j没有自加,while循环永远不会结束。
问题2:在循环体结束时,有赋值语句:fx=f(x,n); 笔者发现如果此时fx<epsilon时,程序while循环体会继续执行,到while开始几句是会更新min的值,可循环不会跳出,而会继续往下执行,更新数组x的值,重新计算目标函数f(x)。这样即时程序找到方程的解,也不会停下来。
笔者纠正后具体程序如下:
-
- #include "stdafx.h"
- #include "RandomNumber.h"
- #include <iostream>
- using namespace std;
-
- bool NonLinear(double *x0,double *dx0,double *x,double a0,
- double epsilon,double k,int n,int Steps,int M);
- double f(double *x,int n);
-
- int main()
- {
- double *x0,
- *x,
- *dx0,
- a0 = 0.0001,
- epsilon = 0.01,
- k = 1.1;
- int n = 2,
- Steps = 10000,
- M = 1000;
-
- x0 = new double[n+1];
- dx0 = new double[n+1];
- x = new double[n+1];
-
-
- x0[1] = 0.0;
- x0[2] = 0.0;
-
-
- dx0[1] = 0.01;
- dx0[2] = 0.01;
-
- cout<<"原方程组为:"<<endl;
- cout<<"x1-x2=1"<<endl;
- cout<<"x1+x2=3"<<endl;
-
- cout<<"此方程租的根为:"<<endl;
-
- bool flag = NonLinear(x0,dx0,x,a0,epsilon,k,n,Steps,M);
- while(!flag)
- {
- flag = NonLinear(x0,dx0,x,a0,epsilon,k,n,Steps,M);
- }
- for(int i=1; i<=n; i++)
- {
- cout<<"x"<<i<<"="<<x[i]<<" ";
- }
- cout<<endl;
-
- return 0;
- }
-
-
- bool NonLinear(double *x0,double *dx0,double *x,double a0,
- double epsilon,double k,int n,int Steps,int M)
- {
- static RandomNumber rnd;
- bool success;
- double *dx,*r;
-
- dx = new double[n+1];
- r = new double[n+1];
- int mm = 0;
- int j = 0;
- double a = a0;
-
- for(int i=1; i<=n; i++)
- {
- x[i] = x0[i];
- dx[i] = dx0[i];
- }
-
- double fx = f(x,n);
- double min = fx;
-
- while(j<Steps)
- {
-
- if(fx<min)
- {
- min = fx;
- a *= k;
- success = true;
- }
- else
- {
- mm++;
- if(mm>M)
- {
- a /= k;
- }
- success = false;
- }
-
- if(min<epsilon)
- {
- break;
- }
-
-
- for(int i=1; i<=n; i++)
- {
- r[i] = 2.0 * rnd.fRandom()-1;
- }
-
- if(success)
- {
- for(int i=1; i<=n; i++)
- {
- dx[i] = a * r[i];
- }
- }
- else
- {
- for(int i=1; i<=n; i++)
- {
- dx[i] = a * r[i] - dx[i];
- }
- }
-
-
- for(int i=1; i<=n; i++)
- {
- x[i] += dx[i];
- }
-
-
- fx = f(x,n);
- j++;
- }
-
- if(fx<=epsilon)
- {
- return true;
- }
- else
- {
- return false;
- }
- }
-
- double f(double *x,int n)
- {
- return (x[1]-x[2]-1)*(x[1]-x[2]-1)
- +(x[1]+x[2]-3)*(x[1]+x[2]-3);
- }
程序运行结果如图:
0042算法笔记——【随机化算法】计算π值和计算定积分
分类: 算法
2013-06-01 15:32
297人阅读
收藏
举报
随机化算法 计算值 计算定积分 数值随机化算法 算法笔记
1、计算π值
问题描述
设有一半径为r的圆及其外切四边形。向该正方形随机地投掷n个点。设落入圆内的点数为k。由于所投入的点在正方形上均匀分布,因而所投入的点落入圆内的概率为
。所以当n足够大时,k与n之比就逼近这一概率。从而
。

程序具体代码如下:
-
- #include "stdafx.h"
- #include "RandomNumber.h"
- #include <iostream>
- using namespace std;
-
- double Darts(int n);
-
- int main()
- {
- int n1 = 100,n2 = 1000,n3 = 1000,n4 = 10000,n5 = 10000000;
- cout<<"n1="<<n1<<",π1="<<Darts(n1)<<endl;
- cout<<"n2="<<n2<<",π2="<<Darts(n2)<<endl;
- cout<<"n3="<<n3<<",π3="<<Darts(n3)<<endl;
- cout<<"n4="<<n4<<",π4="<<Darts(n4)<<endl;
- cout<<"n5="<<n5<<",π5="<<Darts(n5)<<endl;
- return 0;
- }
-
-
- double Darts(int n)
- {
- static RandomNumber dart;
- int k = 0;
-
- for(int i=1; i<=n; i++)
- {
- double x = dart.fRandom();
- double y = dart.fRandom();
- if((x*x + y*y)<=1)
- {
- k++;
- }
- }
-
- return 4*k/double(n);
- }
程序运行结果如图:

2、计算定积分
例:设f(x)=x^2,求
解:
1)随机投点法计算定积分
基本思想是在矩形区域上随机均匀的投点实现。本算法的基本思想是在积分区间上随机均匀的产生点, 即在[a,b]上随机均匀的取点, 求出由这些点产生的函数值的算术平均值, 再乘以区间宽度, 即可解出定积分得近似解
。

算法具体代码如下:
-
- #include "stdafx.h"
- #include "RandomNumber.h"
- #include <iostream>
- using namespace std;
-
- double Darts(int n,double a,double b);
- double f(double x);
-
- int main()
- {
- int n1 = 100,n2 = 1000,n3 = 1000,n4 = 10000,n5 = 10000000;
- double a = 2.0,b = 3.0;
- cout<<"n1="<<n1<<",r1="<<Darts(n1,a,b)<<endl;
- cout<<"n2="<<n2<<",r2="<<Darts(n2,a,b)<<endl;
- cout<<"n3="<<n3<<",r3="<<Darts(n3,a,b)<<endl;
- cout<<"n4="<<n4<<",r4="<<Darts(n4,a,b)<<endl;
- cout<<"n5="<<n5<<",r5="<<Darts(n5,a,b)<<endl;
- return 0;
- }
-
-
-
-
-
-
- double Darts(int n,double a,double b)
- {
- static RandomNumber dart;
- double sum = 0.0;
- for(int i=0; i<n; i++)
- {
- double x = (b-a)*dart.fRandom() + a;
- sum = sum + f(x);
- }
- return (b-a)*sum/n;
- }
-
- double f(double x)
- {
- return x*x;
- }
程序运行结果如图:

2)概率法法计算定积分
设f:[a,b]→[c,d]连续函数(如图2 所示), 则由曲线y=f(x)以及x 轴和直线x=a,x=b 围成的面积由定积分
给出。根据几何概型可知
。假设向矩形区域随机均匀的投镖n 次, 落入阴影为K次, 又设M为x=a、x=b、y=c、y=d 所围成的矩形面积, s 为定积分面积,则
, 所以s= k/n×M。

算法具体代码 如下:
-
- #include "stdafx.h"
- #include "RandomNumber.h"
- #include <iostream>
- using namespace std;
-
- double Darts(int n,double a,double b,double d);
- double f(double x);
-
- int main()
- {
- int n1 = 100,n2 = 1000,n3 = 1000,n4 = 10000,n5 = 10000000;
- double a = 2.0,b = 3.0;
- double d = f(b);
- cout<<"n1="<<n1<<",r1="<<Darts(n1,a,b,d)<<endl;
- cout<<"n2="<<n2<<",r2="<<Darts(n2,a,b,d)<<endl;
- cout<<"n3="<<n3<<",r3="<<Darts(n3,a,b,d)<<endl;
- cout<<"n4="<<n4<<",r4="<<Darts(n4,a,b,d)<<endl;
- cout<<"n5="<<n5<<",r5="<<Darts(n5,a,b,d)<<endl;
- return 0;
- }
-
-
-
-
-
-
- double Darts(int n,double a,double b,double d)
- {
- static RandomNumber dart;
- int k = 0;
- for(int i=0; i<n; i++)
- {
- double x = (b-a)*dart.fRandom() + a;
- double y = d * dart.fRandom();
-
- if(y<=f(x))
- {
- k++;
- }
- }
- return d*(b-a)*k/n;
- }
-
- double f(double x)
- {
- return x*x;
- }
程序运行结果如图:

0041算法笔记——【随机化算法】随机化算法与随机数问题
分类: 算法
2013-05-27 11:03
295人阅读
收藏
举报
随机化算法 随机数 线性同余法 算法笔记 抛硬币
1、随机化算法
(1)描述:随机化算法是这样一种算法,在算法中使用了随机函数,且随机函数的返回值直接或者间接的影响了算法的执行流程或执行结果。随机化算法基于随机方法,依赖于概率大小。
(2)分类:一般情况下,可将概率(随机化)算法大致分为四类:数值概率算法,蒙特卡罗(Monte Carlo)算法,拉斯维加斯(Las Vegas)算法和舍伍德(Sherwood)算法。
数值随机化算法:数值概率算法常用于数值问题的求解。这类算法所得到的往往是近似解。而且近似解的精度随计算时间的增加不断提高。在许多情况下,要计算出问题的精确解是不可能或没有必要的,因此用数值概率算法可得到相当满意的解。
蒙特卡罗(Monte Carlo)算法:蒙特卡罗(Monte Carlo)算法用于求问题的准确解。用蒙特卡罗算法能求得问题的一个解,但这个解未必是正确的。求得正确解的概率依赖于算法所用的时间。算法所用的时间越多,得到正确解的概率就越高。蒙特卡罗算法的主要缺点就在于此。一般情况下,无法有效判断得到的解是否肯定正确。
拉斯维加斯(Las Vegas)算法:拉斯维加斯(Las Vegas)算法不会得到不正确的解,一旦用拉斯维加斯算法找到一个解,那么这个解肯定是正确的。但是有时候用拉斯维加斯算法可能找不到解。与蒙特卡罗算法类似。拉斯维加斯算法得到正确解的概率随着它用的计算时间的增加而提高。对于所求解问题的任一实例,用同一拉斯维加斯算法反复对该实例求解足够多次,可使求解失效的概率任意小。
舍伍德(Sherwood)算法:舍伍德(Sherwood)算法总能求得问题的一个解,且所求得的解总是正确的。当一个确定性算法在最坏情况下的计算复杂性与其在平均情况下的计算复杂性有较大差别时,可以在这个确定算法中引入随机性将它改造成一个舍伍德算法,消除或减少问题的好坏实例间的这种差别。舍伍德算法精髓不是避免算法的最坏情况行为,而是设法消除这种最坏行为与特定实例之间的关联性。这意味着不存在坏的输入,只有坏的随机数。
2、随机数
随机数在随机化算法设计中扮演着十分重要的角色。在现实计算机上无法产生真正的随机数,因此在随机化算法中使用的随机数都是一定程度上随机的,即伪随机数。
线性同余法是产生伪随机数的最常用的方法。由线性同余法产生的随机序列a0,a1,…,an满足:

其中b>=0,c>=0,d<=m。d称为该随机序列的种子。如何选取该方法中的常数b、c和m直接关系到所产生的随机序列的随机性能。从直观上看,m应取得充分大,因此可取m为机器大数,另外应取gcd(m,b)=1,因此可取b为一素数。
利用线性同余法原理,可以设计出一个随机数类RandomNumber。该类包含一个由用户初始化的种子randSeed。给定初始种子后,即可产生与之相对应的随机序列。种子randSeed是一个无符号整型数,可由用户选定也可用系统时间自动产生。函数Random在每次计算时,用线性同余式计算新的种子randSeed。它的高16位的随机性较好。将randSeed右移16位得到一个0~65535间的随机整数。然后将此随机数映射到0~(n-1)范围内。函数fRandom,先用函数Random(maxshort)产生一个0~(maxshot-1)之间的整型随机序列,将每个整型随机数除以maxshort,就得到[0,1)区间中的随机实数。具体代码如下:
- #include"time.h"
-
- const unsigned long maxshort = 65536L;
- const unsigned long multiplier = 1194211693L;
- const unsigned long adder = 12345L;
-
- class RandomNumber
- {
- private:
-
- unsigned long randSeed;
- public:
- RandomNumber(unsigned long s = 0);
- unsigned short Random(unsigned long n);
- double fRandom(void);
- };
-
- RandomNumber::RandomNumber(unsigned long s)
- {
- if(s == 0)
- {
- randSeed = time(0);
- }
- else
- {
- randSeed = s;
- }
- }
-
- unsigned short RandomNumber::Random(unsigned long n)
- {
- randSeed = multiplier * randSeed + adder;
- return (unsigned short)((randSeed>>16)%n);
- }
-
- double RandomNumber::fRandom(void)
- {
- return Random(maxshort)/double(maxshort);
- }
3、随机数测试
用计算机产生的伪随机数来模拟抛硬币试验。假设抛10次硬币,每次抛硬币得到正面和反面是随机的。拋10次硬币构成一个事件。调用Random(2)返回一个二值结果。在主程序中反复调用函数TossCoins模拟拋10次硬币这一事件50000次。用head[i](0<=i<=10)记录这50000次模拟恰好得到i次正面的刺手。最终输出模拟抛硬币事件得到的正面事件的概率图。具体代码如下:
-
- #include "stdafx.h"
- #include "RandomNumber.h"
- #include <iostream>
- using namespace std;
-
- int TossCoins(int numberCoins);
-
- int main()
- {
-
- const int NCOINS = 10;
- const long NTOSSES = 50000L;
-
- long i,heads[NCOINS+1];
-
- int j,position;
-
-
- for(int j=0; j<NCOINS+1;j++)
- {
- heads[j] = 0;
- }
-
-
- for(int i=0; i<NTOSSES; i++)
- {
- heads[TossCoins(NCOINS)]++;
- }
-
-
- for(int i=0; i<=NCOINS; i++)
- {
- position = int(float(heads[i])/NTOSSES*72);
- cout<<i<<" ";
- for(int j=0; j<position-1; j++)
- {
- cout<<" ";
- }
- cout<<"*"<<endl;
- }
-
- return 0;
- }
-
- int TossCoins(int numberCoins)
- {
-
- static RandomNumber coinToss;
- int i,tosses = 0;
- for(int i=0; i<numberCoins; i++)
- {
-
- tosses += coinToss.Random(2);
- }
- return tosses;
- }
程序运行结果如图:

0044算法笔记——【随机化算法】舍伍德(Sherwood)算法和线性时间选择问题
分类: 算法
2013-06-06 14:51
236人阅读
收藏
举报
随机化算法 舍伍德算法 线性时间选择 算法笔记 划分基准
1、舍伍德(Sherwood)算法
设A是一个确定性算法,当它的输入实例为x时所需的计算时间记为tA(x)。设Xn是算法A的输入规模为n的实例的全体,则当问题的输入规模为n时,算法A所需的平均时间为
。这显然不能排除存在x∈Xn使得
的可能性。希望获得一个随机化算法B,使得对问题的输入规模为n的每一个实例均有
。这就是舍伍德算法设计的基本思想。当s(n)与tA(n)相比可忽略时,舍伍德算法可获得很好的平均性能。
2、线性时间选择算法
1)随机划分选择基准
对于选择问题而言,用拟中位数作为划分基准可以保证在最坏的情况下用线性时间完成选择。如果只简单地用待划分数组的第一个元素作为划分基准,则算法的平均性能较好,而在最坏的情况下需要O(n^2)计算时间。舍伍德选择算法则随机地选择一个数组元素作为划分基准,这样既保证算法的线性时间平均性能,又避免了计算拟中位数的麻烦。非递归的舍伍德型选择算法如下:
-
- #include "stdafx.h"
- #include "RandomNumber.h"
- #include <iostream>
- using namespace std;
-
- template<class Type>
- Type select(Type a[],int l,int r,int k);
-
- template<class Type>
- Type select(Type a[],int n,int k);
-
- template <class Type>
- inline void Swap(Type &a,Type &b);
-
- int main()
- {
- int a[] = {5,7,3,4,8,6,9,1,2};
- cout<<"原数组为:"<<endl;
- for(int i=0; i<9; i++)
- {
- cout<<a[i]<<" ";
- }
- cout<<endl;
- cout<<"所给数组第7小元素为:"<<select(a,9,7)<<endl;
- return 0;
- }
-
-
-
- template<class Type>
- Type select(Type a[],int n,int k)
- {
- if(k<1 || k>n)
- {
- cout<<"请输入正确的k!"<<endl;
- return 0;
- }
- return select(a,0,n-1,k);
- }
-
-
- template<class Type>
- Type select(Type a[],int l,int r,int k)
- {
- static RandomNumber rnd;
- while(true)
- {
- if(l>=r)
- {
- return a[l];
- }
-
- int i = l,
- j = l + rnd.Random(r-l+1);
-
- Swap(a[i],a[j]);
-
- j = r+1;
- Type pivot = a[l];
-
-
- while(true)
- {
- while(a[++i]<pivot);
- while(a[--j]>pivot);
- if(i>=j)
- {
- break;
- }
- Swap(a[i],a[j]);
- }
-
- if(j-l+1 == k)
- {
- return pivot;
- }
-
-
- a[l] = a[j];
- a[j] = pivot;
-
-
- if(j-l+1<k)
- {
- k = k-j+l-1;
- l = j + 1;
- }
- else
- {
- r = j - 1;
- }
- }
- }
-
- template <class Type>
- inline void Swap(Type &a,Type &b)
- {
- Type temp = a;
- a = b;
- b = temp;
- }
程序运行结果如图:

2)随机洗牌预处理
有时也会遇到这样的情况,即所给的确定性算法无法直接改造成舍伍德型算法。此时可借助于随机预处理技术,不改变原有的确定性算法,仅对其输入进行随机洗牌,同样可收到舍伍德算法的效果。例如,对于确定性选择算法,可以用下面的洗牌算法shuffle将数组a中元素随机排列,然后用确定性选择算法求解。这样做所收到的效果与舍伍德型算法的效果是一样的。
-
- #include "stdafx.h"
- #include "RandomNumber.h"
- #include <iostream>
- using namespace std;
-
- template<class Type>
- Type select(Type a[],int l,int r,int k);
-
- template<class Type>
- Type select(Type a[],int n,int k);
-
- template<class Type>
- void Shuffle(Type a[],int n);
-
- template <class Type>
- inline void Swap(Type &a,Type &b);
-
- int main()
- {
- int a[] = {5,7,3,4,8,6,9,1,2};
- cout<<"原数组为:"<<endl;
- for(int i=0; i<9; i++)
- {
- cout<<a[i]<<" ";
- }
- cout<<endl;
- Shuffle(a,9);
- cout<<"洗牌后数组为:"<<endl;
- for(int i=0; i<9; i++)
- {
- cout<<a[i]<<" ";
- }
- cout<<endl;
- cout<<"所给数组第7小元素为:"<<select(a,9,7)<<endl;
- return 0;
- }
-
-
-
- template<class Type>
- Type select(Type a[],int n,int k)
- {
- if(k<1 || k>n)
- {
- cout<<"请输入正确的k!"<<endl;
- return 0;
- }
- return select(a,0,n-1,k);
- }
-
-
- template<class Type>
- Type select(Type a[],int l,int r,int k)
- {
- while(true)
- {
- if(l>=r)
- {
- return a[l];
- }
- int i = l;
- int j = r+1;
- Type pivot = a[l];
-
-
- while(true)
- {
- while(a[++i]<pivot);
- while(a[--j]>pivot);
- if(i>=j)
- {
- break;
- }
- Swap(a[i],a[j]);
- }
-
- if(j-l+1 == k)
- {
- return pivot;
- }
-
-
- a[l] = a[j];
- a[j] = pivot;
-
-
- if(j-l+1<k)
- {
- k = k-j+l-1;
- l = j + 1;
- }
- else
- {
- r = j - 1;
- }
- }
- }
-
- template <class Type>
- inline void Swap(Type &a,Type &b)
- {
- Type temp = a;
- a = b;
- b = temp;
- }
-
-
- template<class Type>
- void Shuffle(Type a[],int n)
- {
- static RandomNumber rnd;
- for(int i=0; i<n; i++)
- {
- int j = rnd.Random(n-i)+i;
- Swap(a[i],a[j]);
- }
- }
程序运行结果如图:
分享到:
上一篇:0043算法笔记——【随机化算法】解
0045算法笔记——【随机化算法】舍伍德随机化思想搜索有序表
分类: 算法
2013-06-17 15:21
315人阅读
收藏
举报
有序表 随机化搜索 舍伍德算法 算法笔记
问题描述
用两个数组来表示所给的含有n个元素的有序集S。用value[0:n]存储有序集中的元素,link[0:n]存储有序集中元素在数组value中位置的指针(实际上使用数组模拟链表)。link[0]指向有序集中的第一个元素,集value[link[0]]是集合中的最小元素。一般地,如果value[i]是所给有序集S中的第k个元素,则value[link[i]]是S中第k+1个元素。S中元素的有序性表现为,对于任意1<=i<=n有value[i]<=value[link[i]]。对于集合S中的最大元素value[k]有,link[k]=0且value[0]是一个大数。
例:有序集S={1,2,3,5,8,13,21}的一种表现方式如图所示:

搜索思想
对于有序链表,可采用顺序搜索的方式在所给的有序集S中搜索值为x的元素。如果有序集S中含有n个元素,则在最坏的情况下,顺序搜索算法所需的计算时间为O(n)。利用数组下标的索引性质,可以设计一个随机化搜索算法,一改进算法的搜索时间复杂性。算法的基本思想是,随机抽取数组元素若干次,从较接近搜索元素x的位置开始做顺序搜索。如果随机搜索数组元素k次,则其后顺序搜索所需的平均比较次数为O(n/k+1)。因此,如果去k=|sqrt(n)|,则算法所需的平均计算时间为(Osqrt(n))。
随机化思想下的有序表实现具体代码如下:
1、RandomNumber.h
- #include"time.h"
-
- const unsigned long maxshort = 65536L;
- const unsigned long multiplier = 1194211693L;
- const unsigned long adder = 12345L;
-
- class RandomNumber
- {
- private:
-
- unsigned long randSeed;
- public:
- RandomNumber(unsigned long s = 0);
- unsigned short Random(unsigned long n);
- double fRandom(void);
- };
-
- RandomNumber::RandomNumber(unsigned long s)
- {
- if(s == 0)
- {
- randSeed = time(0);
- }
- else
- {
- randSeed = s;
- }
- }
-
- unsigned short RandomNumber::Random(unsigned long n)
- {
- randSeed = multiplier * randSeed + adder;
- return (unsigned short)((randSeed>>16)%n);
- }
-
- double RandomNumber::fRandom(void)
- {
- return Random(maxshort)/double(maxshort);
- }
2、7d3d2.cpp
-
- #include "stdafx.h"
- #include "RandomNumber.h"
- #include "math.h"
- #include <iostream>
- using namespace std;
-
- template<class Type>
- class OrderedList
- {
- friend int main();
- public:
- OrderedList(Type Small,Type Large,int MaxL);
- ~OrderedList();
- bool Search(Type x,int& index);
- int SearchLast(void);
- void Insert(Type k);
- void Delete(Type k);
- void Output();
- private:
- int n;
- int MaxLength;
- Type *value;
- int *link;
- RandomNumber rnd;
- Type Small;
- Type TailKey;
- };
-
-
- template<class Type>
- OrderedList<Type>::OrderedList(Type small,Type Large,int MaxL)
- {
- MaxLength = MaxL;
- value = new Type[MaxLength+1];
- link = new int[MaxLength+1];
- TailKey = Large;
- n = 0;
- link[0] = 0;
- value[0] = TailKey;
- Small = small;
- }
-
-
- template<class Type>
- OrderedList<Type>::~OrderedList()
- {
- delete value;
- delete link;
- }
-
-
- template<class Type>
- bool OrderedList<Type>::Search(Type x,int& index)
- {
- index = 0;
- Type max = Small;
- int m = floor(sqrt(double(n)));
-
- for(int i=1; i<=m; i++)
- {
- int j = rnd.Random(n)+1;
- Type y = value[j];
-
- if((max<y)&& (y<x))
- {
- max = y;
- index = j;
- }
- }
-
-
- while(value[link[index]]<x)
- {
- index = link[index];
- }
-
- return (value[link[index]] == x);
- }
-
-
- template<class Type>
- void OrderedList<Type>::Insert(Type k)
- {
- if((n == MaxLength)||(k>=TailKey))
- {
- return;
- }
- int index;
-
- if(!Search(k,index))
- {
- value[++n] = k;
- link[n] = link[index];
- link[index] = n;
- }
- }
-
-
- template<class Type>
- int OrderedList<Type>::SearchLast(void)
- {
- int index = 0;
- Type x = value[n];
- Type max = Small;
- int m = floor(sqrt(double(n)));
-
- for(int i=1; i<=m; i++)
- {
- int j = rnd.Random(n)+1;
- Type y = value[j];
-
- if((max<y)&&(y<x))
- {
- max = y;
- index = j;
- }
- }
-
-
- while(link[index]!=n)
- {
- index = link[index];
- }
- return index;
- }
-
-
- template<class Type>
- void OrderedList<Type>::Delete(Type k)
- {
- if((n==0)&&(k>=TailKey))
- {
- return;
- }
-
- int index;
- if(Search(k,index))
- {
- int p = link[index];
- if(p == n)
- {
- link[index] = link[p];
- }
- else
- {
- if(link[p]!=n)
- {
- int q = SearchLast();
- link[q] = p;
- link[index] = link[p];
- }
- value[p] = value[n];
- link[p] = link[n];
- }
- n--;
- }
- }
-
-
- template<class Type>
- void OrderedList<Type>::Output()
- {
- int index = 0,i = 0;
- while(i<n)
- {
- index = link[index];
- cout<<value[index]<<" ";
- i++;
- }
- cout<<endl;
- cout<<"value:";
- for(i=0; i<=n; i++)
- {
- cout.width(4);
- cout<<value[i];
- }
- cout<<endl;
- cout<<"link:";
- for(i=0; i<=n; i++)
- {
- cout.width(4);
- cout<<link[i];
- }
- cout<<endl;
- }
-
- int main()
- {
- static RandomNumber rnd;
- OrderedList<int> *ol = new OrderedList<int>(0,100,100);
-
-
- cout<<"=========创建==========="<<endl;
- while(ol->n<10)
- {
- int e = rnd.Random(99);
- ol->Insert(e);
- }
- ol->Output();
- cout<<endl;
-
-
- cout<<"=========搜索==========="<<endl;
- int index;
- cout<<"搜索有序表value数组中第5个元素如下:"<<endl;
- cout<<"value[5]="<<ol->value[5]<<endl;
- ol->Search(ol->value[5],index);
- cout<<"位置搜索结果为:"<<ol->link[index]<<endl;
- cout<<endl;
-
-
- cout<<"=========删除==========="<<endl;
- cout<<"删除有序表value数组中第5个元素如下:"<<endl;
- cout<<"value[5]="<<ol->value[5]<<endl;
- ol->Delete(ol->value[5]);
- ol->Output();
-
- delete ol;
- return 0;
- }
程序运行结果如图:

0046算法笔记——【随机化算法】舍伍德随机化思想解决跳跃表问题
分类: 算法
2013-06-24 15:36
200人阅读
收藏
举报
跳跃表 算法笔记 随机化算法 舍伍德
问题描述
如果用有序链表来表示一个含有n个元素的有序集S,则在最坏情况下,搜索S中一个元素需要O(n)计算时间。提高有序链表效率的一个技巧是在有序链表的部分结点处增设附加指针以提高其搜索性能。在增设附加指针的有序链表中搜索一个元素时,可借助于附加指针跳过链表中若干结点,加快搜索速度。这种增加了向前附加指针的有序链表称为跳跃表。
应在跳跃表的哪些结点增加附加指针以及在该结点处应增加多少指针完全采用随机化方法来确定。这使得跳跃表可在O(logn)平均时间内支持关于有序集的搜索、插入和删除等运算。
例如:如图,(a)是一个没有附加指针的有序表,而图(b)在图(a)的基础上增加了跳跃一个节点的附加指针。图(c)在图(b)的基础上又增加了跳跃3个节点的附加指针。

在跳跃表中,如果一个节点有k+1个指针,则称此节点为一个k级节点。以图(c)中跳跃表为例,看如何在改跳跃表中搜索元素8。从该跳跃表的最高级,即第2级开始搜索。利用2级指针发现元素8位于节点7和19之间。此时在节点7处降至1级指针进行搜索,发现元素8位于节点7和13之间。最后,在节点7降至0级指针进行搜索,发现元素8位于节点7和11之间,从而知道元素8不在搜索的集合S中。
相关原理
在一般情况下,给定一个含有n个元素的有序链表,可以将它改造成一个完全跳跃表,使得每一个k级结点含有k+1个指针,分别跳过2^k-1,2^(k-1)-1,…,2^0-1个中间结点。第i个k级结点安排在跳跃表的位置i2^k处,i>=0。这样就可以在时间O(logn)内完成集合成员的搜索运算。在一个完全跳跃表中,最高级的结点是
级结点。
完全跳跃表与完全二叉搜索树的情形非常类似。它虽然可以有效地支持成员搜索运算,但不适应于集合动态变化的情况。集合元素的插入和删除运算会破坏完全跳跃表原有的平衡状态,影响后继元素搜索的效率。
为了在动态变化中维持跳跃表中附加指针的平衡性,必须使跳跃表中k级结点数维持在总结点数的一定比例范围内。注意到在一个完全跳跃表中,50%的指针是0级指针;25%的指针是1级指针;…;(100/2^(k+1))%的指针是k级指针。因此,在插入一个元素时,以概率1/2引入一个0级结点,以概率1/4引入一个1级结点,…,以概率1/2^(k+1)引入一个k级结点。另一方面,一个i级结点指向下一个同级或更高级的结点,它所跳过的结点数不再准确地维持在2^i-1。经过这样的修改,就可以在插入或删除一个元素时,通过对跳跃表的局部修改来维持其平衡性。
跳跃表中节点的级别在插入是确定,一旦确定便不再更改。下图是遵循上述原则的跳跃表的例子。对其进行搜索与对完全跳跃表所作的搜索是一样的。如果希望在所示跳跃表中插入一个元素8,则应现在跳跃表中搜索其插入位置。经搜索发现应在节点7和11之间插入元素8.此时在节点7和11之间增加1个存储元素8的新节点,并以随机的方式确定新节点的级别。例如,如果元素8是作为一个2级节点插入,则应对图中虚线相交的指针进行调整,如果新插入的节点是一个1级节点,则只要修改2个指针,虚线相交的指针是有可能被修改的指针,这些指针可在搜索元素插入位置时动态地保存起来,以供插入时使用。

在一个完全跳跃表中,具有i级指针的结点中有一半同时具有i+1级指针。为了维持跳跃表的平衡性,可以事先确定一个实数0<p<1,并要求在跳跃表中维持在具有i级指针的结点中同时具有i+1级指针的结点所占比例约为p。为此目的,在插入一个新结点时,先将其结点级别初始化为0,然后用随机数生成器反复地产生一个[0,1]间的随机实数q。如果q<p,则使新结点级别增加1,直至q>=p。由此产生新结点级别的过程可知,所产生的新结点的级别为0的概率为1-p,级别为1的概率为p(1-p),…,级别为i的概率为p^i(1-p)。如此产生的新结点的级别有可能是一个很大的数,甚至远远超过表中元素的个数。为了避免这种情况,用
作为新结点级别的上界。其中n是当前跳跃表中结点个数。当前跳跃表中任一结点的级别不超过
。
算法具体实现如下:
1、RandomNumber.h
- #include"time.h"
-
- const unsigned long maxshort = 65536L;
- const unsigned long multiplier = 1194211693L;
- const unsigned long adder = 12345L;
-
- class RandomNumber
- {
- private:
-
- unsigned long randSeed;
- public:
- RandomNumber(unsigned long s = 0);
- unsigned short Random(unsigned long n);
- double fRandom(void);
- };
-
- RandomNumber::RandomNumber(unsigned long s)
- {
- if(s == 0)
- {
- randSeed = time(0);
- }
- else
- {
- randSeed = s;
- }
- }
-
- unsigned short RandomNumber::Random(unsigned long n)
- {
- randSeed = multiplier * randSeed + adder;
- return (unsigned short)((randSeed>>16)%n);
- }
-
- double RandomNumber::fRandom(void)
- {
- return Random(maxshort)/double(maxshort);
- }
2、7d3d3.cpp
-
- #include "stdafx.h"
- #include "RandomNumber.h"
- #include <cmath>
- #include <iostream>
- using namespace std;
-
- template<class EType,class KType> class SkipList;
- template<class EType,class KType>
- class SkipNode
- {
- friend SkipList<EType,KType>;
- private:
- SkipNode(int size)
- {
- next = new SkipNode<EType,KType>*[size];
- }
- ~SkipNode()
- {
- delete []next;
- }
- EType data;
- SkipNode<EType,EType> **next;
- };
-
- template<class EType,class KType>
- class SkipList
- {
- public:
- SkipList(KType Large,int MaxE = 10000,float p = 0.5);
- ~SkipList();
- bool Search(const KType& k,EType& e) const;
- SkipList<EType,KType>& Insert(const EType& e);
- SkipList<EType,KType>& Delete(const KType& k,EType& e);
- void Output();
- private:
- int Level();
- SkipNode<EType,KType> *SaveSearch(const KType& k);
- int MaxLevel;
- int Levels;
- RandomNumber rnd;
- float Prob;
- KType TailKey;
- SkipNode<EType,KType> *head;
- SkipNode<EType,KType> *NIL;
- SkipNode<EType,KType> **last;
- };
-
-
- template<class EType,class KType>
- SkipList<EType,KType>::SkipList(KType Large,int MaxE,float p)
- {
- Prob = p;
- MaxLevel = ceil(log(float(MaxE))/log(1/p))-1;
- TailKey = Large;
- Levels = 0;
-
-
- head = new SkipNode<EType,KType>(MaxLevel+1);
- NIL = new SkipNode<EType,KType>(0);
- last = new SkipNode<EType,KType> *[MaxLevel+1];
- NIL->data = Large;
-
-
- for(int i=0; i<=MaxLevel; i++)
- {
- head->next[i] = NIL;
- }
- }
-
-
- template<class EType,class KType>
- SkipList<EType,KType>::~SkipList()
- {
- SkipNode<EType,KType> *next;
-
-
- while(head!=NIL)
- {
- next = head->next[0];
- delete head;
- head = next;
- }
-
- delete NIL;
- delete []last;
- }
-
- class element
- {
- friend int main(void);
- public:
- operator long() const
- {
- return key;
- }
- element& operator = (long y)
- {
- key = y;
- return *this;
- }
- private:
- int data;
- long key;
- };
-
-
- template<class EType,class KType>
- bool SkipList<EType,KType>::Search(const KType& k,EType& e) const
- {
- if(k>=TailKey)
- {
- return false;
- }
-
- SkipNode<EType,KType> *p = head;
- for(int i=Levels; i>=0; i--)
- {
- while(p->next[i]->data<k)
- {
- p = p->next[i];
- }
- }
- e = p->next[0]->data;
- return (e==k);
- }
-
-
- template<class EType,class KType>
- SkipNode<EType,KType> *SkipList<EType,KType>::SaveSearch(const KType& k)
- {
-
- SkipNode<EType,KType> *p = head;
- for(int i = Levels; i>=0; i--)
- {
- while(p->next[i]->data<k)
- {
- p = p->next[i];
- }
- last[i] = p;
- }
- return (p->next[0]);
- }
-
-
- template<class EType,class KType>
- int SkipList<EType,KType>::Level()
- {
- int lev = 0;
- while(rnd.fRandom()<Prob)
- {
- lev++;
- }
- return (lev<=MaxLevel)?lev:MaxLevel;
- }
-
-
- template<class EType,class KType>
- SkipList<EType,KType>& SkipList<EType,KType>::Insert(const EType& e)
- {
- KType k = e;
- if(k>=TailKey)
- {
- cout<<"元素键值超界!"<<endl;
- return *this;
- }
-
- SkipNode<EType,KType> *p = SaveSearch(k);
- if(p->data == e)
- {
- cout<<"元素已存在!"<<endl;
- return *this;
- }
-
-
- int lev = Level();
-
- if(lev>Levels)
- {
- for(int i=Levels+1; i<=lev; i++)
- {
- last[i] = head;
- }
- Levels = lev;
- }
-
-
- SkipNode<EType,KType> *y = new SkipNode<EType,KType>(lev+1);
- y->data = e;
-
- for(int i=0; i<=lev; i++)
- {
-
- y->next[i] = last[i]->next[i];
- last[i]->next[i] = y;
- }
- return *this;
- }
-
-
- template<class EType,class KType>
- SkipList<EType,KType>& SkipList<EType,KType>::Delete(const KType& k,EType& e)
- {
- if(k>=TailKey)
- {
- cout<<"元素键值超界!"<<endl;
- }
-
- SkipNode<EType,KType> *p = SaveSearch(k);
- if(p->data!=k)
- {
- cout<<"未找到待删除元素!"<<endl;
- }
-
- for(int i=0; i<=Levels && last[i]->next[i] == p; i++)
- {
- last[i]->next[i] = p->next[i];
- }
-
- while(Levels>0 && head->next[Levels] == NIL)
- {
- Levels--;
- }
- e = p->data;
- delete p;
- return *this;
- }
-
-
- template<class EType,class KType>
- void SkipList<EType,KType>::Output()
- {
- SkipNode<EType,KType> *y = head->next[0];
- for(;y!=NIL; y=y->next[0])
- {
- cout<<y->data<<' ';
- }
- cout<<endl;
- }
-
- int main()
- {
- SkipList<int,int> *s = new SkipList<int,int>(100,10,0.5);
-
-
- cout<<"=========创建==========="<<endl;
-
- int a[] = {5,3,2,11,7,13,19,17,23};
-
- for(int i=0; i<9; i++)
- {
- s->Insert(a[i]);
- }
- s->Output();
-
-
- cout<<"=========搜索==========="<<endl;
- int k=17,e;
- cout<<"搜索元素k="<<k<<"如下:"<<endl;
- if(s->Search(17,e))
- {
- cout<<"位置搜索结果为:k="<<k<<",e="<<e;
- }
- cout<<endl;
-
-
- cout<<"=========删除==========="<<endl;
- cout<<"删除跳跃表元素k="<<k<<"剩余元素如下:"<<endl;
- s->Delete(k,e);
- s->Output();
-
- delete s;
- return 0;
- }
程序运行结果如图:

分享到:
上一篇:0045算法笔记——【随机化算法】舍伍德随机
0047算法笔记——【随机化算法】拉斯维加斯(Las Vegas)算法和n后问题
分类: 算法
2013-07-04 18:15
200人阅读
收藏
举报
拉斯维加斯 算法笔记 随机化算法 Las Vegas n后问题