PyR*_*lez 2 math optimization haskell
我正在尝试euler挑战14.我想知道我是否可以在haskell中快速计算它.我试过这种天真的方法.
import Data.List import Data.Function collatz n | even n = n
quot
2 | otherwise = 3*n+1 colSeq = takeWhile (/= 1) . (iterate collatz) main=print $ maximumBy (compareon
(length . colSeq)) [1..999999]
但这花了太长时间.
? <*Main System.Timeout>: timeout (10^6*60) main
Nothing
Run Code Online (Sandbox Code Playgroud)
我也尝试使用反向collatz关系,并在地图中保留长度以消除冗余计算,但这也不起作用.并且不想要解决方案,但是有没有人有一些数学文献或编程技术可以使这更快,或者我只需要让它过夜?
首先,你的程序运行正常并在两分钟内完成,如果你编译-O2
并增加堆栈大小(我使用过+RTS -K100m
,但你的系统可能会有所不同):
$ .\collatz.exe +RTS -K100m -s
65,565,993,768 bytes allocated in the heap
16,662,910,752 bytes copied during GC
77,042,796 bytes maximum residency (1129 sample(s))
5,199,140 bytes maximum slop
184 MB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 124724 colls, 0 par 18.41s 18.19s 0.0001s 0.0032s
Gen 1 1129 colls, 0 par 16.67s 16.34s 0.0145s 0.1158s
INIT time 0.00s ( 0.00s elapsed)
MUT time 39.98s ( 41.17s elapsed)
GC time 35.08s ( 34.52s elapsed)
EXIT time 0.00s ( 0.00s elapsed)
Total time 75.06s ( 75.69s elapsed)
%GC time 46.7% (45.6% elapsed)
Alloc rate 1,639,790,387 bytes per MUT second
Productivity 53.3% of total user, 52.8% of total elapsed
Run Code Online (Sandbox Code Playgroud)
生产率约为50%意味着GC使用的时间是我们盯着屏幕的一半,等待我们的结果.在我们的例子中,我们通过迭代每个值的序列来创建很多垃圾.
Collatz序列是递归序列.因此,我们应该将其定义为递归序列而不是迭代序列,并查看发生的情况.
colSeq 1 = [1]
colSeq n
| even n = n : colSeq (n `div` 2)
| otherwise = n : colSeq (3 * n + 1)
Run Code Online (Sandbox Code Playgroud)
Haskell中的列表是一个基本类型,因此GHC应该有一些漂亮的优化(-O2
).所以试试吧:
$ .\collatz_rec.exe +RTS -s
37,491,417,368 bytes allocated in the heap
4,288,084 bytes copied during GC
41,860 bytes maximum residency (2 sample(s))
19,580 bytes maximum slop
1 MB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 72068 colls, 0 par 0.22s 0.22s 0.0000s 0.0001s
Gen 1 2 colls, 0 par 0.00s 0.00s 0.0001s 0.0001s
INIT time 0.00s ( 0.00s elapsed)
MUT time 32.89s ( 33.12s elapsed)
GC time 0.22s ( 0.22s elapsed)
EXIT time 0.00s ( 0.00s elapsed)
Total time 33.11s ( 33.33s elapsed)
%GC time 0.7% (0.7% elapsed)
Alloc rate 1,139,881,573 bytes per MUT second
Productivity 99.3% of total user, 98.7% of total elapsed
Run Code Online (Sandbox Code Playgroud)
请注意,我们现在在约80%的MUT时间内(与原始版本相比)的生产率高达99%.只是通过这个小小的改变,我们大大减少了运行时间.
有一件事很奇怪.为什么我们计算的长度都 1024和512?毕竟,后来无法创建更长的Collatz序列.
但是,在这种情况下,我们必须将问题视为一项重大任务,而不是地图.我们需要跟踪已经计算的值,并且我们希望清除那些已经访问过的值.
我们Data.Set
用于此:
problem_14 :: S.Set Integer -> [(Integer, Integer)]
problem_14 s
| S.null s = []
| otherwise = (c, fromIntegral $ length csq) : problem_14 rest
where (c, rest') = S.deleteFindMin s
csq = colSeq c
rest = rest' `S.difference` S.fromList csq
Run Code Online (Sandbox Code Playgroud)
我们这样使用problem_14
:
main = print $ maximumBy (compare `on` snd) $ problem_14 $ S.fromList [1..999999]
Run Code Online (Sandbox Code Playgroud)
$ .\collatz_set.exe +RTS -s
18,405,282,060 bytes allocated in the heap
1,645,842,328 bytes copied during GC
27,446,972 bytes maximum residency (40 sample(s))
373,056 bytes maximum slop
79 MB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 35193 colls, 0 par 2.17s 2.03s 0.0001s 0.0002s
Gen 1 40 colls, 0 par 0.84s 0.77s 0.0194s 0.0468s
INIT time 0.00s ( 0.00s elapsed)
MUT time 14.91s ( 15.17s elapsed)
GC time 3.02s ( 2.81s elapsed)
EXIT time 0.00s ( 0.00s elapsed)
Total time 17.92s ( 17.98s elapsed)
%GC time 16.8% (15.6% elapsed)
Alloc rate 1,234,735,903 bytes per MUT second
Productivity 83.2% of total user, 82.9% of total elapsed
Run Code Online (Sandbox Code Playgroud)
我们放松了一些生产力,但这是合理的.毕竟,我们现在使用Set
而不是列表,使用79MB而不是1MB.但是,我们的程序现在运行时间为17秒而不是34秒,这只是原始时间的25%.
ST
int main(){
std::vector<bool> Q(1000000,true);
unsigned long long max_l = 0, max_c = 1;
for(unsigned long i = 1; i < Q.size(); ++i){
if(!Q[i])
continue;
unsigned long long c = i, l = 0;
while(c != 1){
if(c < Q.size()) Q[c] = false;
c = c % 2 == 0 ? c / 2 : 3 * c + 1;
l++;
}
if(l > max_l){
max_l = l;
max_c = i;
}
}
std::cout << max_c << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
该程序运行130毫秒.我们最好的版本需要100倍以上.我们可以解决这个问题
problem_14_vector_st :: Int -> (Int, Int)
problem_14_vector_st limit =
runST $ do
q <- V.replicate (limit+1) True
best <- newSTRef (1,1)
forM_ [1..limit] $ \i -> do
b <- V.read q i
when b $ do
let csq = colSeq $ fromIntegral i
let l = fromIntegral $ length csq
forM_ (map fromIntegral csq) $ \j->
when (j<= limit && j>= 0) $ V.write q j False
m <- fmap snd $ readSTRef best
when (l > m) $ writeSTRef best (i,l)
readSTRef best
Run Code Online (Sandbox Code Playgroud)
$ collatz_vector_st.exe +RTS -s
2,762,282,216 bytes allocated in the heap
10,021,016 bytes copied during GC
1,026,580 bytes maximum residency (2 sample(s))
21,684 bytes maximum slop
2 MB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 5286 colls, 0 par 0.02s 0.02s 0.0000s 0.0000s
Gen 1 2 colls, 0 par 0.00s 0.00s 0.0001s 0.0001s
INIT time 0.00s ( 0.00s elapsed)
MUT time 3.09s ( 3.08s elapsed)
GC time 0.02s ( 0.02s elapsed)
EXIT time 0.00s ( 0.00s elapsed)
Total time 3.11s ( 3.11s elapsed)
%GC time 0.5% (0.7% elapsed)
Alloc rate 892,858,898 bytes per MUT second
Productivity 99.5% of total user, 99.6% of total elapsed
Run Code Online (Sandbox Code Playgroud)
~3秒.其他人可能知道更多技巧,但这是我可以挤出Haskell的最多.
归档时间: |
|
查看次数: |
256 次 |
最近记录: |