【问题标题】:Haskell Particle simulation - calculating velocities of particlesHaskell 粒子模拟 - 计算粒子的速度
【发布时间】:2015-06-17 18:54:02
【问题描述】:

我正在使用 Haskell 开发一个粒子模拟程序。对于其中一个函数,我试图根据所有周围粒子的质量和速度来确定模拟中所有粒子的新速度。

函数是这样的形式:

accelerate :: Float -> [Particle] -> [Particle]

粒子是一种包含质量、位置向量和速度向量的数据类型,'Float' 参数表示模拟中各个时间步长的增量时间

我想要一些关于可能的函数的建议,我可以用来遍历列表,同时计算每个粒子相对于列表中其他粒子的速度。

我能想到的一种可能的方法:

  1. 假设有另一个函数“velocityCalculator”,其定义如下:

    velocityCalculator :: Particle -> Particle -> (Float,Float)
    

这需要两个粒子并返回第一个粒子的更新速度向量。

  1. 应用折叠;使用上述函数作为二元运算符,粒子和粒子列表作为参数,即

    foldl velocityCalculator particle particleList
    
  2. 遍历粒子列表,将 foldl 应用于每个元素并构建包含具有更新速度的粒子的新列表

我不确定这是否是最有效的方法,因此非常感谢任何建议和改进。

请注意 -> 正如我所说,我只是在寻找建议而不是答案!

谢谢!

【问题讨论】:

  • 你想应用velocity verlet算法还是类似的东西?我的意思是,你想用一些解析势或数值势来整合牛顿的运动方程吗?
  • 没什么特别的,我只是在 haskell 中寻找可能的高阶函数,以便在跨列表应用 velocityCalculator 函数时更快地完成工作

标签: algorithm haskell functional-programming simulation particle-system


【解决方案1】:

看来你已经准备好使用foldl了。例如

  1. 遍历粒子列表,将foldl 应用于每个元素并构建包含具有更新速度的粒子的新列表

真的没有意义。根据一些二进制汇总函数,您将foldl 应用于列表以将其减少为“汇总值”。将其应用于单个粒子并没有什么意义。

我在回答这个问题时假设您首先在编写程序时遇到了困难——通常最好在担心效率之前这样做。如果我假设错了,请告诉我。

我不确定您要使用什么规则来更新速度,但我认为这是某种成对的力模拟,例如重力或电磁力。如果是这样,这里有一些提示可以指导您找到解决方案。

type Vector = (Float, Float)

-- Finds the force exerted on one particle by the other.
-- Your code will be simplified if this returns (0,0) when the two
-- particles are the same. 
findForce :: Particle -> Particle -> Vector

-- Find the sum of all forces exerted on the particle
-- by every particle in the list. 
totalForce :: [Particle] -> Particle -> Vector

-- Takes a force and a particle to update, returns the same particle with
-- updated velocity. 
updateVelocity :: Vector -> Particle -> Particle

-- Calculate mutual forces and update all particles' velocities
updateParticles :: [Particle] -> [Particle]

这些函数中的每一个都很短,只有一两行。如果您需要进一步提示使用哪些高阶函数,请注意您尝试编写的函数的类型,并注意

map :: (a -> b) -> [a] -> [b]           -- takes a list, returns a list
filter :: (a -> Bool) -> [a] -> [a]     -- takes a list, returns a list
foldl :: (a -> b -> a) -> a -> [b] -> a -- takes a list, returns something else
foldr :: (a -> b -> b) -> b -> [a] -> b -- takes a list, returns something else

【讨论】:

    【解决方案2】:

    如果你给每个Particle 一个particle_id :: Int ID,然后定义它,你可能会通过记忆提高 2 倍的速度:

    forceOf a b | particle_id a > particle_id b = -(forceOf b a)
                | otherwise = (pos a - pos b) *:. charge a * charge b / norm (pos a - pos b) ^ 3
    

    其中(*:.) :: Vector -> Double -> Vector 是向量标量乘法,所以上面是1/r^2 强制定律。请注意,这里我们记住了pos a - pos b,然后我们还记住了forceOf a b,以用作forceOf b a

    然后你想用dvs = [dt * sum (map (forceOf a) particles) / mass a | a <- particles]来获取速度变化的列表,然后zipWith (+) (map velocity particles) dvs

    一个问题是这种方法在数值不确定性方面表现不佳:时间 $t+1$ 的所有内容都基于时间 $t$ 的真实情况。您可以通过求解矩阵方程开始解决此问题;而不是 v+ = v + dt * M v (其中 v = v(t) 和 v+ = v(t + 1) ),可以写成v+ = v + dt * M v+,这样就有v+ = (1 - dt * M)-1 v. 这通常在数值上更稳定。以 50/50 的比例混合这两种解决方案可能会更好。 v+ = v + dt * M (v + v+) / 2.这里有很多选择。

    【讨论】:

      猜你喜欢
      • 2014-02-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多