通过大量的幂函数调用优化haskell函数

时间:2016-06-02 04:41:58

标签: performance haskell

以下功能是查找n 1^3 + 2^3 + ... + (n-1) ^3 + n^3 = m的数字findNb :: Integer -> Integer findNb m = findNb' 1 0 where findNb' n m' = if m' == m then n - 1 else if m' < m then findNb' (n + 1) (m' + n^3) else -1 。这个功能有可能针对速度进行优化吗?

main = print (findNb2 152000000000000000000000)

我知道使用数学公式有更快的解决方案。

我问的原因是JavaScript / C#中的类似实现似乎比Haskell快得多。我只是好奇它是否可以优化。谢谢。

EDIT1:添加更多关于朗姆酒时间的证据

Haskell版本:

使用ghc -o testo2.exe -O2 -prof -fprof-auto -rtsopts pileofcube.hs

  1. 使用 -O2 进行编译并进行性能分析:ghc -o testo22.exe -O2 pileofcube.hs。以下是分析报告的总时间:

    总时间= 0.19 秒(190毫秒)(190个滴答@ 1000 us,1个处理器)

  2. 使用-O2进行编译但不进行性能分析:Measure-Command {./testo22.exe}。在powershell中使用function findNb(m) { let n = 0; let sum = 0; while (sum < m) { n++; sum += Math.pow(n, 3); } return sum === m ? n : -1; } var d1 = new Date(); findNb(152000000000000000000000); console.log(new Date() - d1); 运行它。结果是:

    毫秒: 157

  3. JavaScript版本:

    代码:

     static void Main(string[] args)
            {
                BigInteger m = BigInteger.Parse("152000000000000000000000");
                var s = new Stopwatch();
                s.Start();
                long n = 0;
                BigInteger sum = 0;
                while (sum < m)
                {
                    n++;
                    sum += BigInteger.Pow(n, 3);
                }
    
                Console.WriteLine(sum == m ? n : -1);
                s.Stop();
                Console.WriteLine($"Escaped Time: {s.ElapsedMilliseconds} milliseconds.");
            }
    

    结果:在同一台计算机上的Chrome中运行 45 毫秒

    EDIT2:添加C#版本

    正如@Berji和@Bakuriu评论的那样,与上面的JavaScript版本相比,这是不公平的,因为它使用了双精度浮点数,甚至无法给出正确的答案。所以我用C#实现了它,这里是代码和结果:

        $builder
            ->add('information', 'textarea', array(
                'label' => false,
                'required' => true,
                'constraints' => [
                    new NotBlank()
                ],
                'placeholder' => 'support.contact.titleplaceholder',
            ));
    

    结果:转义时间: 457 毫秒。

    结论

    Haskell版本比C#更快...

    我在开始时错了,因为我没有意识到由于我的JavaScript知识不足,JavaScript使用了双精度浮点数。

    此时似乎问题再没有意义......

3 个答案:

答案 0 :(得分:6)

Haskell也可以使用Double在更短的时间内得到错误的答案:

% time ./so
./so  0.03s user 0.00s system 95% cpu 0.038 total

Javascript也可以通过安装big-integer和使用bigInt而不是Double来获得正确的结果:

% node so.js
^C
node so.js  35.62s user 0.30s system 93% cpu 38.259 total

......或者它可能并不像那样微不足道。

答案 1 :(得分:4)

编辑:后来我意识到这不是问题的作者想要的。我会保留它,以防万一有人想知道有问题的公式,但请不要理会。

确实有一个公式可以让你在恒定时间(而不是n次迭代)中计算它。由于我不记得学校的确切公式,我做了一些搜索,现在是:https://proofwiki.org/wiki/Sum_of_Sequence_of_Cubes

在haskell代码中,这将转换为

// THIS WILL GIVE YOU CRASH.
ComponentName receiver = new ComponentName(context, NotificationPublisher.class); 

我认为应该更快。

答案 2 :(得分:0)

不确定该算法的这个措辞是否更快,但试试这个吗?

findNb :: Integer -> Integer
findNb m = length $ takeWhile (<=m) $ scanl1 (+) [n^3 | n <- [1..]]

(但在未定义的情况下,这有不同的语义。)