由于作为软件工程课pair project II (电梯调度程序)的志愿者,我负责为大家产生至关重要的用于最后评定各个pair的调度程序性能的测试数据(压力好大)。
完整的源码下载(VS2010):http://files.cnblogs.com/codingcrazy/GenerateElevTestData.rar
详细要求:http://www.cnblogs.com/xinz/archive/2010/11/28/1890300.html
3. Testing
TA will simulate a “rush hour” test. The “rush hour” test is to simulate the come-to-work and leave-work scenario in a business building, which has the following 2 parts (they can be run next to each other).
1) Simple test. 20 passengers
20 people going thru random floors within 5 minutes.
2) Come-to-work. 1000 total passengers
a) 80% of them goes from floor 0 and 1 to all other floors, the destination is distributed evenly.
The time each passenger arrives at the elevator can be emulated as a normal distribution.
b) 20% of them are going between any 2 floors of [2, 20], Very few people travel between 2 adjacent floors
(e.g. from floor 5 to 4). Other than this, the distribution is also even.
3) Leave-work. 1000 total passengers
a) 90% of them go from other floors to floor1 or floor0.
b) 10% of them travel between floors [2, 20], again, Very few people travel between 2 adjacent floors.
测试数据文件格式:XML文档描述的乘客信息,如下:
<? xml version="1.0" encoding="utf-8" ?>
< passengers
xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd ="http://www.w3.org/2001/XMLSchema"
xmlns ="http://tempuri.org/passengers.xsd" >
< passenger name ="SENXIANG" comingtime ="50" fromfloor ="10" tofloor ="20" weight ="60" />
< passenger name ="Piggy" comingtime ="100" fromfloor ="1" tofloor ="15" weight ="75" />
< passenger name ="Later" comingtime ="10000" fromfloor ="99" tofloor ="15" weight ="45" />
</ passengers >
根据要求,需要模拟上班时和下班时的电梯乘客情况,使其满足正态分布:即在规定上班之前(下班时间之后)某个时间点乘客达到最高峰,两边依近似正态分布递减。因此,这里的核心需求是:如何产生满足正态分布的随机数?
这里使用Marsaglia方法(见维基百科:http://en.wikipedia.org/wiki/Normal_distribution,小节:Generating values from normal distribution)。
原理:Box-Muller方法:设有两个服从均匀分布U(0,1)的独立随机变量U和V,那么由下式得到的X和Y也是独立随机变量,并且都服从正态分布N(0,1)。
Marsaglia对Box-Muller方法进行了改进,使其不再需要进行三角函数的计算。Marsaglia使用的U和V是两个服从均匀分布U(-1,1)的独立随机变量,令
S = U2 + V2,如果S>=1则重新计算S,否则下面的X和Y就是服从标准正态分布N(0,1)的独立随机变量:
下面是按以上算法产生正态分布随机数的类:
//
// Gaussian Random Number Generator class
// ref. http://blog.csdn.net/holym/archive/2006/05/16/741074.aspx
//
public class GaussianRNG
{
int iset;
double gset;
Random r1, r2;
public GaussianRNG()
{
// use unchecked to disable overflow check
r1 = new Random( unchecked (( int )DateTime.Now.Ticks));
r2 = new Random( ~ unchecked (( int )DateTime.Now.Ticks));
iset = 0 ;
}
// generate Gaussian random numbers of N(0,1)
public double Next()
{
double fac, rsq, v1, v2;
if (iset == 0 )
{
do
{
v1 = 2.0 * r1.NextDouble() - 1.0 ;
v2 = 2.0 * r2.NextDouble() - 1.0 ;
rsq = v1 * v1 + v2 * v2;
} while (rsq >= 1.0 || rsq == 0.0 );
fac = Math.Sqrt( - 2.0 * Math.Log(rsq) / rsq);
gset = v1 * fac;
iset = 1 ;
return v2 * fac;
}
else
{
iset = 0 ;
return gset;
}
}
// generate Gaussian random numbers of N(mu,sigma)
// Note: the return values are constrained to between (mu-sigma) and (mu+sigma)
public double Next( double mu, double sigma)
{
double x = Next();
while (x < - 1 || x > 1 )
{
x = Next();
}
return (mu + x * sigma);
}
}
由于原来测试程序框架中需要在XML中声明命名空间(因为要根据预先定义的xsd文件进行反序列化生成所需的passenger对象)。我没有找到在XML文件中直接写入命名空间的语句。因此偷懒先生成简单的XML文件,再用StreamReader将文件读入,用StreamWriter将其连同命名空间声明一同写入到目标XML文件。如果你有好的方法,请告诉我。下面是生成Come to work测试文件的代码:
static void GenerateData_ComeToWork()
{
Random rnd = new Random();
GaussianRNG rnd_gaussian = new GaussianRNG();
int fromfloor;
int tofloor;
int highestFloor = 20 ;
try
{
using (XmlTextWriter writer = new XmlTextWriter( " temp2.xml " , null ))
{
writer.Formatting = Formatting.Indented;
writer.WriteStartDocument();
writer.WriteStartElement( " passengers " );
// Generate passenger info
for ( int i = 1 ; i <= 800 ; i ++ )
{
writer.WriteStartElement( " passenger " );
writer.WriteAttributeString( " name " , string .Format( " Xiao_{0} " , i));
// passenger come to work in 1 hour
writer.WriteAttributeString( " comingtime " , Math.Ceiling(rnd_gaussian.Next( 1800 , 1800 )).ToString());
// fromfloor: 0 or 1
writer.WriteAttributeString( " fromfloor " , rnd.Next( 2 ).ToString());
// tofloor: [2...20]
writer.WriteAttributeString( " tofloor " , rnd.Next( 2 , highestFloor + 1 ).ToString());
// passenger weight form 45 to 120, average 70
writer.WriteAttributeString( " weight " , rnd.Next((i <= 400 ) ? 45 : 70 , (i <= 400 ) ? 70 : 120 ).ToString());
writer.WriteEndElement();
}
for ( int i = 801 ; i <= 1000 ; i ++ )
{
writer.WriteStartElement( " passenger " );
writer.WriteAttributeString( " name " , string .Format( " Xiao_{0} " , i));
// passenger come to work in 1 hour
writer.WriteAttributeString( " comingtime " , Math.Ceiling(rnd_gaussian.Next( 1800 , 1800 )).ToString());
// Request: fromfloor != tofloor, very few people travel between adjacent floors
fromfloor = rnd.Next( 2 , highestFloor + 1 );
while ((tofloor = rnd.Next( 2 , highestFloor + 1 )) == fromfloor)
;
if (Math.Abs(fromfloor - tofloor) == 1 && (rnd.Next( 101 ) / 100 ) < 80 )
{
fromfloor = rnd.Next( 2 , highestFloor + 1 );
while ((tofloor = rnd.Next( 2 , highestFloor + 1 )) == fromfloor)
;
}
writer.WriteAttributeString( " fromfloor " , fromfloor.ToString());
writer.WriteAttributeString( " tofloor " , tofloor.ToString());
// passenger weight form 45 to 120, average 70
writer.WriteAttributeString( " weight " , rnd.Next((i <= 900 ) ? 45 : 70 , (i <= 900 ) ? 70 : 120 ).ToString());
writer.WriteEndElement();
}
writer.WriteEndElement();
writer.Flush();
writer.Close();
}
using (StreamWriter sw = new StreamWriter( " passenger2.xml " ))
{
sw.Write( " <?xml version=\ " 1.0 \ " encoding=\ " utf - 8 \ " ?>\r\n<passengers \r\n xmlns:xsi=\ " http: // www.w3.org/2001/XMLSchema-instance\" \r\n xmlns:xsd=\" http://www.w3.org/2001/XMLSchema \" \r\n xmlns=\" http://tempuri.org/passengers.xsd \">\r\n");
using (StreamReader sr = new StreamReader( " temp2.xml " ))
{
sr.ReadLine();
sr.ReadLine();
while (sr.Peek() > 0 )
{
sw.WriteLine(sr.ReadLine());
}
}
}
Console.WriteLine( " \nThe following file has been successfully created: passenger2.xml " );
}
catch (Exception)
{
throw ;
}
}
by Xiaobin([email protected])