Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Performance 更高效的算法在Haskell中的性能更差 我的一个朋友在他参加的C++课程中给我看了一个家庭练习。既然我已经知道C++,只是刚开始学习Haskell,我试着在Haskell方式下解决这个练习。_Performance_Haskell_Recursion_Equation Solving - Fatal编程技术网

Performance 更高效的算法在Haskell中的性能更差 我的一个朋友在他参加的C++课程中给我看了一个家庭练习。既然我已经知道C++,只是刚开始学习Haskell,我试着在Haskell方式下解决这个练习。

Performance 更高效的算法在Haskell中的性能更差 我的一个朋友在他参加的C++课程中给我看了一个家庭练习。既然我已经知道C++,只是刚开始学习Haskell,我试着在Haskell方式下解决这个练习。,performance,haskell,recursion,equation-solving,Performance,Haskell,Recursion,Equation Solving,以下是练习说明(我是从我们的母语翻译过来的,如果说明不清楚,请发表评论): 编写一个程序,从用户处读取非零系数(a、B、C、D),并将其放入以下等式中: A*x+B*y+C*z=D 程序还应该从用户N读取数据,它表示一个范围。该程序应找到-N/2到N/2范围内方程的所有可能的积分解 例如: Input: A = 2,B = -3,C = -1, D = 5, N = 4 Output: (-1,-2,-1), (0,-2, 1), (0,-1,-2), (1,-1, 0), (2,-1,2),

以下是练习说明(我是从我们的母语翻译过来的,如果说明不清楚,请发表评论):

编写一个程序,从用户处读取非零系数(a、B、C、D),并将其放入以下等式中: A*x+B*y+C*z=D 程序还应该从用户N读取数据,它表示一个范围。该程序应找到-N/2到N/2范围内方程的所有可能的积分解

例如:

Input: A = 2,B = -3,C = -1, D = 5, N = 4
Output: (-1,-2,-1), (0,-2, 1), (0,-1,-2), (1,-1, 0), (2,-1,2), (2,0, -1)
最直接的算法是用蛮力尝试所有的可能性。我通过以下方式在Haskell中实现了它:

triSolve :: Integer -> Integer -> Integer -> Integer -> Integer -> [(Integer,Integer,Integer)]
triSolve a b c d n =
  let equation x y z = (a * x + b * y + c * z) == d
      minN = div (-n) 2
      maxN = div n 2
  in [(x,y,z) | x <- [minN..maxN], y <- [minN..maxN], z <- [minN..maxN], equation x y z]
solutions :: (Integer -> Integer -> Integer -> Bool) -> Integer -> Integer -> Integer -> Integer -> Integer ->     [(Integer,Integer,Integer)]
solutions f maxN minN x y z
  | solved = (x,y,z):nextCall x (y + 1) minN
  | x >= maxN && y >= maxN && z >= maxN = []
  | z >= maxN && y >= maxN = nextCall (x + 1) minN minN
  | z >= maxN = nextCall x (y + 1) minN
  | otherwise = nextCall x y (z + 1)
  where solved = f x y z
        nextCall = solutions f maxN minN

triSolve' :: Integer -> Integer -> Integer -> Integer -> Integer -> [(Integer,Integer,Integer)]
triSolve' a b c d n =
  let equation x y z = (a * x + b * y + c * z) == d
      minN = div (-n) 2
      maxN = div n 2
  in solutions equation maxN minN minN minN minN
两者产生相同的结果。但是,尝试测量执行时间会产生以下结果:

*Main> length $ triSolve' 2 (-3) (-1) 5 100
3398
(2.81 secs, 971648320 bytes)
*Main> length $ triSolve 2 (-3) (-1) 5 100
3398
(1.73 secs, 621862528 bytes)
这意味着哑算法实际上比更复杂的算法表现得更好。基于假设我的算法是正确的(我希望不会变成错误的:),我假设第二个算法受到递归产生的开销的影响,而第一个算法不是,因为它是使用列表理解实现的。 有没有办法在Haskell中实现比哑巴算法更好的算法?
(另外,我很高兴收到关于我的编码风格的一般反馈)

当然有。我们有:

a*x + b*y + c*z = d
一旦我们假设x和y的值,我们就得到了

a*x + b*y = n
其中n是我们知道的数字。 因此


我们只保留积分zs。

值得注意的是,GHC对列表理解进行了特殊处理,并且通常速度非常快。这可以解释为什么您的
triSolve
(使用列表理解)比
triSolve'
(不使用列表理解)更快

例如,解决方案

solve :: Integer -> Integer -> Integer -> Integer -> Integer -> [(Integer,Integer,Integer)]
-- "Buffalo buffalo buffalo buffalo Buffalo buffalo buffalo..."
solve a b c d n =
    [(x,y,z) | x <- vals, y <- vals
             , let p = a*x +b*y
             , let z = (d - p) `div` c
             , z >= minN, z <= maxN, c * z == d - p ]
    where
        minN = negate (n `div` 2)
        maxN = (n `div` 2)
        vals = [minN..maxN]
而使用
do
符号编写的等效代码:

solveM :: Integer -> Integer -> Integer -> Integer -> Integer -> [(Integer,Integer,Integer)]
solveM a b c d n = do
    x <- vals
    y <- vals
    let p = a * x + b * y
        z = (d - p) `div` c
    guard $ z >= minN
    guard $ z <= maxN
    guard $ z * c == d - p
    return (x,y,z)
    where
        minN = negate (n `div` 2)
        maxN = (n `div` 2)
        vals = [minN..maxN]

关于在GHCI中进行测试的常见警告适用——如果您真的想看到差异,则需要使用-O2编译代码,并使用一个像样的基准库(如Criteria)。

谢谢,这样就可以了。我很难为情,因为我没有自己考虑这件事。我想这就是在思考“编码”时试图解决数学问题时发生的情况。不过,我还是想知道为什么triSolve的性能比triSolve差。如果这是Frege而不是Haskell,我会说,无论是将函数作为参数传递,还是使用编译器知道的函数,性能上都会有很大的不同——例如,它需要3个严格的整数参数。但是,把这句话看作是一个荒谬的猜测,我并没有资格说Haskell优化……你能解释Ax+Buby=n吗?他只是意味着给定的值<代码> x<代码>和<代码> y>代码>,你知道<代码> a*x+b*y。命名该值
n
。然后,你只需要解出
n+c*z=d
,你知道
n
c
z
。正如Ingo所说,
z=(d-n)/c
。测试某种东西的唯一可靠方法是运行一个独立的可执行文件,通过使用-O2开关编译生成;应用辅助/包装转换:使
nextCall
成为内部定义,在
解决方案
中是本地的,这样您就不必传递非更改参数-嵌套定义将访问外部定义的参数。您的程序必须支持任意大的数字吗?因为Integer就是这么做的。如果你想要效率,如果Int的范围对你来说足够,那么就使用Int!也是的,用氧气建造。首先,这应该可以提高您的性能。您正在使用ghci进行评估—这是一个字节码解释器,比ghc慢30倍—我无法复制这些计时结果。对我来说,列表理解速度较慢,这很有趣。您使用的是什么版本的GHC?我在7.4.17.6.3上,正如
ghc--version
所报道的那样。
> length $ solve 2 (-3) (-1) 5 100
3398
(0.03 secs, 4111220 bytes)
solveM :: Integer -> Integer -> Integer -> Integer -> Integer -> [(Integer,Integer,Integer)]
solveM a b c d n = do
    x <- vals
    y <- vals
    let p = a * x + b * y
        z = (d - p) `div` c
    guard $ z >= minN
    guard $ z <= maxN
    guard $ z * c == d - p
    return (x,y,z)
    where
        minN = negate (n `div` 2)
        maxN = (n `div` 2)
        vals = [minN..maxN]
> length $ solveM 2 (-3) (-1) 5 100
3398
(0.06 secs, 6639244 bytes)