Python仿真模拟电路,python 通信系统仿真
感谢博主们的辛苦。
为什么要模拟?
需要模拟的原因有很多。简单来说,就是纯数学建模的优缺点和实际系统的折中。对于数学模型,仿真不需要高超的数学技巧,也不需要过度简化和假设。在求解复杂系统时,不面临状态和空间爆炸的问题。此外,仿真还用来验证数学模型的正确性。与实际系统相比,仿真的成本要低得多,而且可以方便地获得更多的统计信息。实际系统可能不可用。例如,对于对等(p2p)网络的研究人员来说,大多数人无法获得拥有数百万用户的p2p网络的控制权。没有模拟,很多研究人员可能什么都做不了。
如何模拟
要进行模拟,首先需要对模拟的系统进行建模。该模型由多个实体和实体之间的关系组成。属性构成系统状态,事件是改变系统状态的行为。通常,系统的状态会随着时间而变化。因此,要模拟系统的行为,最重要的是模拟系统中的事件。
一般来说,仿真模型分为两部分。一部分称为周期驾驶模型,也称为时间驾驶。系统以一定的时间间隔递增时间,每次时钟移动,都需要一些动作。这种模式的优点很简单,但是真实性差。另一种是事件驱动模型。在这个模型中,系统状态的变化只是由事件的发生引起的,系统时间也是由事件的发生时间决定的。由于事件发生时间的随机性,系统时间的推进步长也是随机的。因为系统状态在两个相邻事件之间的时间内不会改变,所以系统时钟可以从一个事件发生的时间直接前进到下一个事件发生的时间。事件驱动仿真模型能够准确地模拟复杂系统,因此具有更广泛的应用范围。这里只讨论事件驱动的仿真模型。
驱动事件模拟模型的核心组件是“事件队列”。队列头总是指向下一个事件,因为事件队列存储的是系统中尚未发生的事件,事件按照发生时间排序。请注意,在某个时间点,事件队列中可能不会存储所有的发生时间。这是因为一些事件发生在另一个事件发生的时候。
事件驱动仿真模型的过程一般可以概括如下。
初始化事件队列
Whilenotevent _ queue.empty(与当前时间相比
event=event_queue.front(
事件_队列.弹出_前端(
当前时间=事件发生时间
事件发生(
后模拟过程
其中event_queue表示事件队列,end_time表示模拟结束时间。如果时间队列为空或者系统时间达到结束时间,则模拟结束。在模拟之后,通常会进行统计工作。
实现一个简单的事件驱动模拟模型。
事件驱动仿真模型的实现其实相当简单。下面是c的实现,这个实现只是用来演示模拟的基本思想,没有考虑效率和可扩展性以保持代码简单。
此处仅列出部分代码,所有代码均可访问。
这个实现包括两个文件:edsim_naive.h和edsim_naive.cc Edsim_naive.h主要定义Event和TEventHandler,前者代表事件,后者是事件发生时处理函数的函数指针。代码如下。
Type (* teventhandler))透视图;
结构事件{
Event (dual-operator _ time,TEventHandlerhandler))。
3360m _发生时间(发生时间).
、m_Handler(处理程序) )。
{}
布尔运算符
returnm _发生时间
}
doublem _ occure _ time;
TEventHandlerm _ handler
(;
Edsim_naive.cc用list表示事件队列,并定义函数enqueue()将事件插入事件队列。Run_sim))函数提供了事件驱动模型的核心流程。代码如下。
typedef STD:3360 listteventqueue;
TEventQueueg _ event _ queue
双精度_当前_时间;
语音队列(一致事件)。
{
teventqueue :3360迭代器itr=
STD:3360 upper _ bound(g _ event _ queue . begin)、g_event_queue.end)、event);
g_event_queue.insert(ITR,事件);
}
是双精度虚空。
{
returng _ current _ time
}
大众汽车
idrun_sim(doubleend_time)
{
而(!g _事件_队列. empty()g _当前时间
event event=g _ event _ queue . front();
g _ event _ queue . pop _ front();
g _ current _ time=event . m _ occure _ time;
(* event . m _ handler)();
}
}
示例:简单排队系统的模拟
离散事件仿真模型广泛应用于各种排队系统的研究中。这里实现了一个简单的排队系统。假设一家银行有服务员,客户随机时间到银行。如果服务员有空,就为顾客服务;如果服务员忙,顾客就会排队等候。服务顺序是先到先服务,排队长度没有限制,也就是说用户到达后在接受服务前不会留一条线。假设顾客到达的时间间隔和服务时间服从指数分布,懂排队论的人应该知道这是一个M/M/1的队列,不知道也没关系。
此处仅列出部分代码,所有代码均可访问:
系统中只有两个事件,客户到达和客户离开,所以我们只需要定义两个事件处理函数,userArrival和user department,如下所示:
void user department(void)
{
-g _ online _ user _ CNT;
logOnlineUserCount();
}
voiduserArrival(void)
{
g _ online _ user _ cnt
logOnlineUserCount();
doubleinterval=指数型(g _ avg _ arrv _ intv);
doubleserv _ time=指数型(g _ avg _ serv _ time);
Eventnext_arrv(now() interval,user arrival);
event department(now()serv _ time,user department);
enqueue(next _ arrv);
入队(出发);
}
模拟的代码也很简单(省略了一些代码),如下:
intmain(intargc,constchar*argv[])
{
.
//firstarrivalevent
Eventfirst_arrv(0.0,user arrival);
enqueue(first _ arrv);
run _ sim(end _ time);
.
}
假设第一个客户0: 00到达,只需要生成第一个客户到达事件,然后调用run_sim()开始模拟。模拟一个文件online_user_cnt.txt记录系统客户数量随时间的变化,提供一个gnuplot叫这个online_user_cnt.p图。图示如下。需要注意的是,如果需要得到平滑的曲线,可以设置不同的随机数生成种子运行几次模拟,得到平均值。
结束语
本文给出的仿真实现非常简单,在效率和可扩展性上有很多不足。比如每次插入事件对象都需要做一个拷贝,使用函数指针处理事件的处理程序不够灵活。在实际系统中,事件队列通常存储指向事件对象的指针,并且事件处理的处理程序通常由派生类实现。甚至可以优化事件队列的排序。例如,ns2提供了三个队列:列表、堆和日历队列。这些优化实现起来并不困难,我们将在后面讨论。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。