关键词
Julia | Python | 线性 | 矩阵 | 数学 | 方程 | 矢量 | 聚类 | 几何 | 函数 | 弹性需求 | 价格 | 平衡 | 化学 | 动态 | 质量 | 离散 | 模型 | 建模 | 复合 | 二次状态 | 算法 | 拟合 | 回归 | 作用力 | 序列 | 投资 | 组合 | 价值 | 自适应矩估计 | 逻辑 | 分类 | 深度学习 | K均值 | K最近邻 | 贝叶斯 | 决策树 | 多项式 | 主成分分析 | 隐藏层 | 支持向量机 | 动态模型 | 时间分量 | 轨迹 | 弹簧 | 确定性系统 | 传染病 | 马尔科夫链 | 生存 | 随机性 | 队列 | 噪声 | 卡尔曼滤波 | 动态网络 | 稳定性
大型仿真系统不应该是一个所有事情都在一个地方完成的整体程序。将核心模拟与可视化或数据分析分开是有意义的。显然,模拟系统需要与可视化进行通信,例如使用输出数据文件离线或使用网络流在线。
该模拟是一个简单的空间捕食者-猎物关系模拟。 所有动物,无论是捕食者还是猎物,都被建模为代理。 面向对象编程非常适合这个小例子。 每个代理都是一个对象,我们有不同类型的代理。
class Agent():
def __init__(self, x=None, y=None, world_width=0, world_height=0):
super().__init__()
self.vmax = 2.0
self.world_width = world_width
self.world_height = world_height
self.x = x if x else random.randint(0, self.world_width)
self.y = y if y else random.randint(0, self.world_height)
self.dx = 0
self.dy = 0
self.is_alive = True
self.target = None
self.age = 0
self.energy = 0
def update(self, food=()):
self.age = self.age + 1
if self.vmax == 0:
return
if self.target and not self.target.is_alive:
self.target = None
if self.target:
squared_dist = (self.x - self.target.x) ** 2 + (self.y - self.target.y) ** 2
if squared_dist < 400:
self.target.is_alive = False
self.energy = self.energy + 1
if self.target:
fx += 0.1*(self.target.x - self.x)
fy += 0.1*(self.target.y - self.y)
self.dx = self.dx + 0.05 * fx
self.dy = self.dy + 0.05 * fy
velocity = math.sqrt(self.dx ** 2 + self.dy ** 2)
if velocity > self.vmax:
self.dx = (self.dx / velocity) * (self.vmax)
self.dy = (self.dy / velocity) * (self.vmax)
self.x = self.x + self.dx
self.y = self.y + self.dy
self.x = max(self.x, 0)
self.x = min(self.x, self.world_width)
self.y = max(self.y, 0)
self.y = min(self.y, self.world_height)
一旦我们决定测量程序的速度,我们就必须定义将如何准确地做到这一点。乍一看,这似乎是一项微不足道的任务。只需运行该程序并测量需要多长时间。我们的模拟最初由捕食者、猎物和植物组成。 他们每个人都有一个随机位置。 使用固定值来为随机数生成器提供种子是已知的技术。 这保证了如果您运行同一个程序两次,结果将是相同的。
如果您用两种不同的语言编写程序,可能会存在细微的差异。 例如,在像我们这里使用的模拟这样的情况下,舍入误差可能会产生影响。 一种语言可能使用不同的方法进行算术或使用不同的内部符号和浮点数精度。
即使起点完全相同,结果也可能完全不同,因此运行时间也可能完全不同。一种可能的方法是使用随机值(例如当前时间)为随机数生成器提供种子。然后运行实验几次并使用平均运行时间。
%%julia
Base.@kwdef mutable struct Agent
vmax::Float64 = 2.5
world_width::Int = 10
world_height::Int = 10
x::Int = rand(0:world_width)
y::Int = rand(0:world_height)
dx::Float64 = 0.0
dy::Float64 = 0.0
is_alive::Bool = true
target::Union{Nothing, Agent} = nothing
age::Int = 0
energy::Int = 0
end
Predator(; kwargs...) = Agent(; vmax = 2.5, kwargs...)
Prey(; kwargs...) = Agent(; vmax = 2.0, kwargs...)
Plant(; kwargs...) = Agent(; vmax = 0.0, kwargs...)
%%julia
function update!(self, food)
self.age = self.age + 1
if self.vmax == 0.0
return
end
if self.target !== nothing && !self.target.is_alive
self.target = nothing
end
if self.target !== nothing
squared_dist = (self.x - self.target.x) ^ 2 + (self.y - self.target.y) ^ 2
if squared_dist < 400
self.target.is_alive = false
self.energy = self.energy + 1
end
else
min_dist = 9999999
min_agent = nothing
for a in food
if a !== self && a.is_alive
sq_dist = (self.x - a.x) ^ 2 + (self.y - a.y) ^ 2
if sq_dist < min_dist
min_dist = sq_dist
min_agent = a
end
end
end
if min_dist < 100000
self.target = min_agent
end
end
fx = 0.0
fy = 0.0
if self.target !== nothing
fx += 0.1 * (self.target.x - self.x)
fy += 0.1 * (self.target.y - self.y)
end
self.dx = self.dx + 0.05 * fx
self.dy = self.dy + 0.05 * fy
velocity = sqrt(self.dx ^ 2 + self.dy ^ 2)
if velocity > self.vmax
self.dx = (self.dx / velocity) * (self.vmax)
self.dy = (self.dy / velocity) * (self.vmax)
end
self.x = self.x + Int(round(self.dx))
self.y = self.y + Int(round(self.dy))
self.x = max(self.x, 0)
self.x = min(self.x, self.world_width)
self.y = max(self.y, 0)
self.y = min(self.y, self.world_height)
end