【问题标题】:Recursive Function in Haskell very slow [duplicate]Haskell中的递归函数非常慢[重复]
【发布时间】:2014-06-03 23:41:19
【问题描述】:

我编写了一个简单的 Haskell 程序来解决一个难题。该算法是正确的,它为n = 40 生成了正确的结果,即 14466。但是,对于n = 100,程序变得如此缓慢,以至于我什至没有足够的耐心等待它。

我不明白它为什么这么慢,因为我希望它缓存所有中间函数调用的结果,你知道,Haskell 是一种惰性语言。

我的编译器是 GHCi 7.6.3。

我尝试过分析,但这只是告诉我 99.9% 的时间都花在了 isLoosing 函数上。

isLoosing :: Int -> Int -> Bool
isLoosing x y
    | y < x             = isLoosing y x
    | x == 0            = True
    | x == 1            = False
    | y `mod` x == 0    = False
    | otherwise         = and [ not (isLoosing (y-x*m) x) |
                                m <- [1..(y `div` x)] ]

loosingSum :: Int -> Int
loosingSum n = sum  [ x + y |
                        x <- [1..(n-1)],
                        y <- [(x+1)..n],
                        isLoosing x y == True ]

main = print $ loosingSum 40

【问题讨论】:

  • 嗯,好吧,很有趣。我不明白的一件事是为什么编译器不会自动执行此操作?
  • @popovitsj GHC 从不记忆,因为如果保存 每个 函数调用,它会占用太多内存。
  • 展示您的 C++ 解决方案。您可能会将苹果与橙子进行比较。
  • 你可能是对的......我在我的答案中添加了 C++ 代码。

标签: performance haskell lazy-evaluation


【解决方案1】:

我发现需要使用记忆化来加快速度。我找到了一个不错的库来为我做这件事,如下所示。这段代码现在运行时间为 0.12 秒,但不幸的是,这仍然太慢了。使用n = 10000 运行它再次花费了相当多的时间,而使用 C++ 中的类似算法则需要几秒钟。所以我开始认为 Haskell 可能不是所有问题的解决方案......

import Data.MemoCombinators (memo2,integral)

bigN :: Int
bigN = 100

isLoosingCached = memo2 integral integral isLoosing

isLoosing :: Int -> Int -> Bool
isLoosing x y
    | y < x             = isLoosingCached y x
    | x == 0            = True
    | x == 1            = False
    | y `mod` x == 0    = False
    | otherwise         = and [ not (isLoosingCached (y-x*m) x) |
                            m <- [1..(y `div` x)] ]

loosingSum :: Int
loosingSum = sum    [ x + y |
                        x <- [1..(n-1)],
                        y <- [(x+1)..n],
                        isLoosingCached x y == True ]
                where n = bigN

main = print $ loosingSum

这个解决方案是C++是这样的。最大的不同是它使用迭代而不是递归。

#include <iostream>
#include <stdint.h>
#define N 10000
short STATES[N + 1][N + 1];
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
    uint64_t sum = 0;
    for (int y = 0; y <= N; y++)
    {
        STATES[0][y] = 1;
        STATES[1][y] = -1;
    }
    for (int x = 2; x < N; x++)
        for (int y = x; y <= N; y += x)
            STATES[x][y] = -1;
    for (int x = 2; x < N; x++)
    {
        for (int y = x + 1; y <= N; y++)
        {
            if (STATES[x][y] == 0)
            {
                int k;
                for (int y_ = y - x; y_ > 0; y_ -= x)
                {
                    if (y_ > x)
                        k = STATES[x][y_] * -1;
                    else
                        k = STATES[y_][x] * -1;
                    if (k == -1)
                        break;
                }
                STATES[x][y] = k;
            }
            if (STATES[x][y] == 1)
                sum += x + y;
        }
        cout << sum << endl;
        system("pause");
    }
}

【讨论】:

  • “所以我开始认为 Haskell 可能不是解决所有问题的方法”:BLASPHEMER!
  • 最大的不同是C代码预先分配了一个数组,而haskell代码不断分配新的cons单元。也就是说,您正在比较两种完全不同的算法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-31
  • 2023-04-09
  • 1970-01-01
  • 1970-01-01
  • 2011-02-14
  • 2019-02-28
相关资源
最近更新 更多