【发布时间】:2018-01-22 12:56:28
【问题描述】:
我正在用 Haskell 编写一个函数,它接收两个整数 a 和 b,并计算 [a,b] 的最大子集的长度,使得所有元素互质。现在,这样做的原因是因为我相信研究这些值可能会产生产生微不足道的较小界限的方法(可能一个大到足以暗示各种推测范围内的实际素数,例如连续平方)。所以很自然地,我试图对一些相当大的数字运行它。
不幸的是,下面的代码运行速度不够快,以至于我无法使用它。我认为缺陷是 Haskell 的惰性策略导致问题,但我既不知道语法也不知道我需要强制执行以防止操作累积的位置。
subsets [] = [[]]
subsets (x:xs) = subsets xs ++ map (x:) (subsets xs)
divides a b = (mod a b == 0)
coprime a b = ((gcd a b) == 1)
mutually_coprime [] = True
mutually_coprime (x:xs) | (coprime_list x xs) = mutually_coprime xs
| otherwise = False
coprime_list _ [] = True
coprime_list a (x:xs) | (coprime a x) = coprime_list a xs
| otherwise = False
coprime_subsets a b = coprime_subsets_helper (subsets [a..b])
coprime_subsets_helper [] = []
coprime_subsets_helper (x:xs) | (mutually_coprime x) = [x] ++ (coprime_subsets_helper xs)
| otherwise = coprime_subsets_helper xs
coprime_subset_length a b = max_element (map list_length (coprime_subsets a b))
list_length [] = 0
list_length (x:xs) = 1 + list_length xs
max_element a = max_element_helper a 0
max_element_helper [] a = a
max_element_helper (x:xs) a | (x > a) = max_element_helper xs x
| otherwise = max_element_helper xs a
只是为了弄清楚这是什么类型的输入,“coprime_subsets 100 120”对我来说似乎从未停止过。我实际上让它运行,起床,做了一些其他的事情,然后又回来了。 它仍在运行。我怀疑一个很大的瓶颈是立即计算所有子集。但是,我不想对生成的子集设置人为的下限。这可能会筛选掉所有互质集,让我一无所有。
到目前为止我所尝试的:
我用 gcd 替换了原来的互质函数。最初,这对所有整数使用模数和迭代检查。我认为 gcd 使用了类似 Euclid's Algorithm 的东西,理论上它应该运行得更快。
我一直在尝试想办法将子集的生成构建到互质集生成器中。到目前为止,我还没有想出任何东西。我也不确定这是否真的会有所帮助。
我一直在寻找 Haskell 的懒惰政策可能会伤害我的任何地方。没什么特别的,但我敢肯定。
我也知道这可能是我使用的环境 (winhugs) 的效率问题。我会说这就是问题所在;但是,当我询问如何在 Math Stack Exchange 上确定最大子集(对于一般 n 大小的范围)时,我收到的答案表明,从计算上讲,这是一件非常缓慢的事情。如果是这样,那很好。我只是希望能够跑完一些我感兴趣的范围,而不用花几乎永远的时间。
我知道这个网站一般不允许效率;但是,我已经尝试了很多,我不只是想在这里偷懒。我知道 Haskell 有很多奇怪的怪癖可以迫使它变得看似低效。我已经有一段时间没有在其中编写代码了,我怀疑我遇到了其中一个怪癖。
【问题讨论】:
-
您已经重新实现了标准库中已有的很多东西。
subsets是subsequences;divides根本没有使用;coprime_list是all . coprime;coprime_subsets_helper是filter mutually_coprime;list_length是length;max_element是maximum . (0:)。这些更改都不会对运行时产生太大影响,但它会使您的代码对这里的人们更具可读性,并使您的问题的计算核心更加明显。 -
顺便说一下,这个问题可以表述为在图中找到largest maximal cliques,它的节点是数字并且在互质节点之间有边。寻找最大团的最著名的通用算法是指数的(大约 O(1.3^n)),所以如果你想要一个比这更有效的算法,你将不得不以某种方式利用互质性的结构——这是先验的对我来说听起来很难。
标签: algorithm performance haskell optimization