由于作为软件工程课pair project II (电梯调度程序)的志愿者,我负责为大家产生至关重要的用于最后评定各个pair的调度程序性能的测试数据(压力好大)。
完整的源码下载(VS2010):https://files.cnblogs.com/codingcrazy/GenerateElevTestData.rar
1. 要求
详细要求: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>
2. 正态分布随机数的产生
根据要求,需要模拟上班时和下班时的电梯乘客情况,使其满足正态分布:即在规定上班之前(下班时间之后)某个时间点乘客达到最高峰,两边依近似正态分布递减。因此,这里的核心需求是:如何产生满足正态分布的随机数?
这里使用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);
}
}
3. XML文件的产生
由于原来测试程序框架中需要在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(v-xxu@microsoft.com)