我正在编写一种算法,用于在给定坐标列表(描述路径)的情况下在几个转折点上查找长路径。动态编程算法在O(kn ^ 2)中很好地工作,其中k是转折点的数量和n个点。短篇小说:最慢的部分是2个坐标之间的距离计算;该算法要求对同一对点重新计算“k”次。记忆不是一种选择(太多点)。有可能“反转”算法 - 但不知何故倒置算法在haskell中非常慢并且占用太多内存。
在我看来,问题在于:你得到一个固定大小的数组(加上一些动态计算的值 - 例如,这将是用列表压缩值的结果:
arr = [ (2, [10,5,12]), (1, [2,8, 20]), (4, [3, 2, 10]) ]
我试图找到列表元素的最大值加上固定值:
[12, 9, 21]
我在做什么 - 比如:
foldl' getbest (replicate 3 0) arr
getbest acc (fixval, item) = map comparator $ zip acc item
comparator orig new
| new + fixval > orig = new + fixval
| otherwise = orig
问题是每次调用'getbest'时都会构建一个新的'acc' - 这是n ^ 2,这是很多。分配是昂贵的,这可能是问题。你知道如何有效地做这件事吗?
说清楚:这是函数的实际代码:
dynamic2FreeFlight :: Int -> [ Coord ] -> [ Coord ]
dynamic2FreeFlight numpoints points = reverse $ (dsCoord bestPoint) : (snd $ (dsScore bestPoint) !! (numpoints - 2))
where
bestPoint :: DSPoint
bestPoint = maximumBy (\x y -> (getFinalPointScore x) `compare` (getFinalPointScore y)) compresult
getFinalPointScore :: DSPoint -> Double
getFinalPointScore sc = fst $ (dsScore sc) !! (numpoints - 2)
compresult :: [ DSPoint ]
compresult = foldl' onestep [] points
onestep :: [ DSPoint ] -> Coord -> [ DSPoint ]
onestep lst point = (DSPoint point (genmax lst)) : lst
where
genmax :: [ DSPoint ] -> [ (Double, [ Coord ]) ]
genmax lst = map (maximumBy comparator) $ transpose prepared
comparator a b = (fst a) `compare` (fst b)
distances :: [ Double ]
distances = map (distance point . dsCoord) lst
prepared :: [ [ (Double, [ Coord ]) ] ]
prepared
| length lst == 0 = [ replicate (numpoints - 1) (0, []) ]
| otherwise = map prepare $ zip distances lst
prepare :: (Double, DSPoint) -> [ (Double, [ Coord ]) ]
prepare (dist, item) = (dist, [dsCoord item]) : map addme (take (numpoints - 2) (dsScore item))
where
addme (score, coords) = (score + dist, dsCoord item : coords)
答案 0 :(得分:5)
使用以下方法对Travis Browns,SCLV,Kennys以及您的答案进行基准测试:
import Data.List
import Criterion.Main
import Criterion.Config
import qualified Data.Vector as V
-- Vector based solution (Travis Brown)
bestVector :: V.Vector (V.Vector Int) -> V.Vector Int -> V.Vector Int
bestVector = (V.foldl1' (V.zipWith max) .) . (V.zipWith . flip $ V.map . (+))
convertVector :: [[Int]] -> V.Vector (V.Vector Int)
convertVector = V.fromList . map V.fromList
arrVector = convertVector arr
valVector = V.fromList val :: V.Vector Int
-- Shared arr and val
arr = [map (x*) [1, 2.. 2000] | x <- [1..1000]]
val = [1..1000]
-- SCLV solution
bestSCLV = foldl' (zipWith max) (repeat 0) . map (\(fv,xs) -> map (+fv) xs)
-- KennyTM Solution
bestKTM arr = map maximum $ transpose [ map (a+) bs | (a,bs) <- arr]
-- Original
getbest :: [Int] -> (Int, [Int]) -> [Int]
getbest acc (fixval, item) = map (uncurry comparator) $ zip acc item
where
comparator o n = max (n + fixval) o
someFuncOrig = foldl' getbest acc
where acc = replicate 2000 0
-- top level functions
someFuncVector :: (V.Vector (V.Vector Int), V.Vector Int) -> V.Vector Int
someFuncVector = uncurry bestVector
someFuncSCLV = bestSCLV
someFuncKTM = bestKTM
main = do
let vec = someFuncVector (arrVector, valVector) :: V.Vector Int
print (someFuncOrig (zip val arr) == someFuncKTM (zip val arr)
, someFuncKTM (zip val arr) == someFuncSCLV (zip val arr)
, someFuncSCLV (zip val arr) == V.toList vec)
defaultMain
[ bench "someFuncVector" (whnf someFuncVector (arrVector, valVector))
, bench "someFuncSCLV" (nf someFuncSCLV (zip val arr))
, bench "someFuncKTM" (nf someFuncKTM (zip val arr))
, bench "original" (nf someFuncOrig (zip val arr))
]
也许我的基准会以某种方式搞砸了,但结果却相当令人失望。
矢量:379.0164毫秒(密度也差 - 什么事?) SCLV:207.5399毫秒 肯尼:200.6028毫秒 原文:138.4270 ms
[tommd@Mavlo Test]$ ./t
(True,True,True)
warming up
estimating clock resolution...
mean is 13.65277 us (40001 iterations)
found 3378 outliers among 39999 samples (8.4%)
1272 (3.2%) high mild
2106 (5.3%) high severe
estimating cost of a clock call...
mean is 1.653858 us (58 iterations)
found 3 outliers among 58 samples (5.2%)
2 (3.4%) high mild
1 (1.7%) high severe
benchmarking someFuncVector
collecting 100 samples, 1 iterations each, in estimated 54.56119 s
bootstrapping with 100000 resamples
mean: 379.0164 ms, lb 357.0403 ms, ub 401.0113 ms, ci 0.950
std dev: 112.6714 ms, lb 101.8206 ms, ub 125.4846 ms, ci 0.950
variance introduced by outliers: 4.000%
variance is slightly inflated by outliers
benchmarking someFuncSCLV
collecting 100 samples, 1 iterations each, in estimated 20.92559 s
bootstrapping with 100000 resamples
mean: 207.5399 ms, lb 207.4099 ms, ub 207.8410 ms, ci 0.950
std dev: 955.1629 us, lb 507.1857 us, ub 1.937356 ms, ci 0.950
found 3 outliers among 100 samples (3.0%)
2 (2.0%) high severe
variance introduced by outliers: 0.990%
variance is unaffected by outliers
benchmarking someFuncKTM
collecting 100 samples, 1 iterations each, in estimated 20.14799 s
bootstrapping with 100000 resamples
mean: 200.6028 ms, lb 200.5273 ms, ub 200.6994 ms, ci 0.950
std dev: 434.9564 us, lb 347.5326 us, ub 672.6736 us, ci 0.950
found 1 outliers among 100 samples (1.0%)
1 (1.0%) high severe
variance introduced by outliers: 0.990%
variance is unaffected by outliers
benchmarking original
collecting 100 samples, 1 iterations each, in estimated 14.05241 s
bootstrapping with 100000 resamples
mean: 138.4270 ms, lb 138.2244 ms, ub 138.6568 ms, ci 0.950
std dev: 1.107366 ms, lb 930.6549 us, ub 1.381234 ms, ci 0.950
found 15 outliers among 100 samples (15.0%)
7 (7.0%) low mild
7 (7.0%) high mild
1 (1.0%) high severe
variance introduced by outliers: 0.990%
variance is unaffected by outliers
答案 1 :(得分:2)
我还没有检查效率,但是怎么样
map maximum $ transpose [ map (a+) bs | (a,bs) <- arr]
?由于结果总是以总和为单位,因此首先将值和列表相加。然后我们采用列表的转置,使它现在是列专业。最后,我们计算每列的最大值。 (你需要import Data.List
,BTW。)
答案 2 :(得分:1)
您可以尝试使用Data.Vector
:
import qualified Data.Vector as V
best :: V.Vector (V.Vector Int) -> V.Vector Int -> V.Vector Int
best = (V.foldl1' (V.zipWith max) .) . (V.zipWith . flip $ V.map . (+))
convert :: [[Int]] -> V.Vector (V.Vector Int)
convert = V.fromList . map V.fromList
arr = convert [[10, 5, 12], [2, 8, 20], [3, 2, 10]]
val = V.fromList [2, 1, 4] :: V.Vector Int
这有效:
*Main> best arr val
fromList [12,9,21] :: Data.Vector.Vector
答案 3 :(得分:1)
best = foldl' (zipWith max) (repeat 0) . map (\(fv,xs) -> map (+fv) xs)
像Kenny一样,我们首先添加。和你的一样,我们只进行一次遍历,除了使用zipWith max之外,我们更普遍和简洁地进行遍历。没有严肃的基准,但这应该是相当不错的。