Haskell中的嵌套三角环?
我在Java中有以下内容,基本上是一个嵌套的三角形循环:Haskell中的嵌套三角环?,haskell,Haskell,我在Java中有以下内容,基本上是一个嵌套的三角形循环: int n = 10; B bs[] = new B[n]; // some initial values, bla bla double dt = 0.001; for (int i = 0; i < n; i++) { bs[i] = new B(); bs[i].x = i * 0.5; bs[i].v = i * 2.5;
int n = 10;
B bs[] = new B[n];
// some initial values, bla bla
double dt = 0.001;
for (int i = 0; i < n; i++) {
bs[i] = new B();
bs[i].x = i * 0.5;
bs[i].v = i * 2.5;
bs[i].m = i * 5.5;
}
for (int i = 0; i < n; i++) {
for (int j = **(i+1)**; j < n; j++) {
double d = bs[i].x - bs[j].x;
double sqr = d * d + 0.01;
double dist = Math.sqrt(sqr);
double mag = dt / (sqr * dist);
bs[i].v -= d * bs[j].m * mag;
**bs[j].v += d * bs[i].m * mag;**
}
}
// printing out the value v
for (int i = 0; i < n; i++) {
System.out.println(bs[i].v);
}
在每次迭代中,数组索引i和j处的值会同时更新,从而避免执行完整的嵌套循环。下面给出了相同的结果,但它是一个完整的嵌套循环(请原谅我使用的术语,它们可能不正确,但我希望它有意义)
我将有非常大的价值,例如1000、5000等。
当n=1000时,一个完整的嵌套循环给出
长度[(i,j)| i这是一个使用包中未绑定可变向量的简单转换。代码有点难看,但应该非常快:
module Main
where
import qualified Data.Vector.Unboxed as U
import qualified Data.Vector.Unboxed.Mutable as M
numElts :: Int
numElts = 10
dt :: Double
dt = 0.001
loop :: Int -> M.IOVector Double -> M.IOVector Double
-> M.IOVector Double -> IO ()
loop n x v m = go 0
where
doWork i j = do xI <- M.read x i
xJ <- M.read x j
vI <- M.read v i
vJ <- M.read v j
mI <- M.read m i
mJ <- M.read m j
let d = xI - xJ
let sqr = d * d + 0.01
let dist = sqrt sqr
let mag = dt / (sqr * dist)
M.write v i (vI - d * mJ * mag)
M.write v j (vJ + d * mI * mag)
go i | i < n = do go' (i+1)
go (i+1)
| otherwise = return ()
where
go' j | j < n = do doWork i j
go' (j + 1)
| otherwise = return ()
main :: IO ()
main = do x <- generateVector 0.5
v <- generateVector 2.5
m <- generateVector 5.5
loop numElts x v m
v' <- U.unsafeFreeze v
U.forM_ v' print
where
generateVector :: Double -> IO (M.IOVector Double)
generateVector d = do v <- M.new numElts
generateVector' numElts d v
return v
generateVector' :: Int -> Double -> M.IOVector Double -> IO ()
generateVector' n d v = go 0
where
go i | i < n = do M.unsafeWrite v i (fromIntegral i * d)
go (i+1)
| otherwise = return ()
因此,强制解决方案的速度大约是功能解决方案的50倍(当所有内容都放在缓存中时,对于较小的n
,差异更为显著).我试图使Federico的解决方案适用于未绑定向量,但显然它在一个关键方面依赖于惰性,这使得未绑定版本永远循环。“纯向量”版本使用装箱向量。我不确定这是否解决了您的问题,因为我还没有完全掌握它,但三角环本身在Haskell中非常容易实现:
triangularLoop :: (a -> a -> b) -> [a] -> [b]
triangularLoop f xs = do
(x1 : t) <- tails xs
x2 <- t
return $ f x1 x2
在Haskell中编写嵌套循环的典型惯用方法是使用列表理解
以下是我将如何翻译您的代码:
import Data.Array
import Data.List (tails)
data Body = Body {x::Double,v::Double,m::Double}
deriving Show
n::Int
n = 9
dt::Double
dt = 0.001
bs_0 :: Array Int Body
bs_0 = array (0,n) [(i,Body {x = i'*0.5,v = i'*2.5,m = i'*5.5}) |
i <- [0..n], let i' = fromIntegral i]
bs :: Array Int Body
bs = accum (\b dv -> b {v = v b + dv}) bs_0 dvs
where
dvs :: [(Int,Double)]
dvs = concat [[(i,dv_i),(j,dv_j)] | (i:is) <- tails [0..n],
j <- is,
let d = x(bs!i) - x(bs!j)
sqr = d * d + 0.01
dist = sqrt sqr
mag = dt / (sqr * dist)
dv_i = -d * m(bs!j) * mag
dv_j = d * m(bs!i) * mag]
main :: IO()
main = mapM_ print (assocs bs)
导入数据。数组
导入数据列表(尾部)
数据体=体{x::Double,v::Double,m::Double}
衍生节目
n::Int
n=9
dt::双
dt=0.001
bs_0::数组整型体
bs_0=数组(0,n)[(i,Body{x=i'*0.5,v=i'*2.5,m=i'*5.5}]
我想如果你能解释一下update\u i
和update\u j
的作用,以及result
是什么,那会有所帮助,否则就不清楚如何最好地编写你想要的东西了。@Haskell大象:我删除了这两个方法,并将其中的代码放进去了。我通过删除不必要的代码,简化了很多。你可以替换dt
按任何值,都无所谓。Thanks@HaskellElephant:您确定您的两个循环实际执行相同的操作吗?毕竟,该循环可能会读取已更新的值arr
。您提供的两个循环并不等效,因此很难分析您尝试执行的操作。请查看。我知道你的代码是用Java编写的,但在代码板上Java不是一个选项。@HaskellElephant,Anupam:你是对的,代码没有给出相同的结果。我的错。我只是试图简化我的代码,但现在代码更完整了,如果你检查,两个循环都给出相同的结果,除了第一个是“三角形”循环,而第二个是ful循环l嵌套循环。谢谢Mikhail。可变未绑定向量模块对我来说是新的。我实际上如何在主函数中打印/返回结果?我指的是更新的向量。我将使用我现有的代码来处理它,看看它是如何运行的。但我想看看如何使用不可变数组或haskell中的列表来实现它。这将有限地需要累积更改,然后最终将其应用于初始数组/列表。我不确定在这种情况下,纯功能解决方案是否能像命令式解决方案那样快。破坏性更新和恒定时间索引是您的算法的核心。@Mikhail:取消安全真的有意义吗?没有合理的w除了构造一个不可变的向量并不安全地解冻它之外,构造一个可变向量的方法是什么?@Peaker删除了对unsafeThaw
的调用。仅供参考,I。强制解决方案要快得多;然而,我无法让您的代码处理未固定的向量。
module Main
where
import qualified Data.Vector.Unboxed as U
import qualified Data.Vector.Unboxed.Mutable as M
numElts :: Int
numElts = 10
dt :: Double
dt = 0.001
loop :: Int -> M.IOVector Double -> M.IOVector Double
-> M.IOVector Double -> IO ()
loop n x v m = go 0
where
doWork i j = do xI <- M.read x i
xJ <- M.read x j
vI <- M.read v i
vJ <- M.read v j
mI <- M.read m i
mJ <- M.read m j
let d = xI - xJ
let sqr = d * d + 0.01
let dist = sqrt sqr
let mag = dt / (sqr * dist)
M.write v i (vI - d * mJ * mag)
M.write v j (vJ + d * mI * mag)
go i | i < n = do go' (i+1)
go (i+1)
| otherwise = return ()
where
go' j | j < n = do doWork i j
go' (j + 1)
| otherwise = return ()
main :: IO ()
main = do x <- generateVector 0.5
v <- generateVector 2.5
m <- generateVector 5.5
loop numElts x v m
v' <- U.unsafeFreeze v
U.forM_ v' print
where
generateVector :: Double -> IO (M.IOVector Double)
generateVector d = do v <- M.new numElts
generateVector' numElts d v
return v
generateVector' :: Int -> Double -> M.IOVector Double -> IO ()
generateVector' n d v = go 0
where
go i | i < n = do M.unsafeWrite v i (fromIntegral i * d)
go (i+1)
| otherwise = return ()
triangularLoop :: (a -> a -> b) -> [a] -> [b]
triangularLoop f xs = do
(x1 : t) <- tails xs
x2 <- t
return $ f x1 x2
triangularLoop f = concat . map singlePass . tails
where
singlePass [] = []
singlePass (h:t) = map (f h) t
import Data.Array
import Data.List (tails)
data Body = Body {x::Double,v::Double,m::Double}
deriving Show
n::Int
n = 9
dt::Double
dt = 0.001
bs_0 :: Array Int Body
bs_0 = array (0,n) [(i,Body {x = i'*0.5,v = i'*2.5,m = i'*5.5}) |
i <- [0..n], let i' = fromIntegral i]
bs :: Array Int Body
bs = accum (\b dv -> b {v = v b + dv}) bs_0 dvs
where
dvs :: [(Int,Double)]
dvs = concat [[(i,dv_i),(j,dv_j)] | (i:is) <- tails [0..n],
j <- is,
let d = x(bs!i) - x(bs!j)
sqr = d * d + 0.01
dist = sqrt sqr
mag = dt / (sqr * dist)
dv_i = -d * m(bs!j) * mag
dv_j = d * m(bs!i) * mag]
main :: IO()
main = mapM_ print (assocs bs)