关于打字/球拍的问题。我目前正在通过Euler Project problems努力学习球拍。 my solutions中的一些非常慢,特别是在处理素数和因子时。因此,对于某些问题,我尝试制作打字/球拍版本,我发现速度没有提高,恰恰相反。 (我尝试使用非常大的数字来减少开销的影响,计算大约是10秒。)
我从Racket文档中了解到最佳优化发生when using Floats/Flonums。所以...是的,我已经尝试制作处理整数问题的浮点版本。与使用this problem使用整数的racket version一样,以及typed/racket one人工将整数转换为浮点数。我必须使用技巧:检查两个数字之间的相等性实际上意味着检查它们是否足够接近",就像在这个函数中检查x是否可以被y除以一样:
(: divide? (-> Flonum Flonum Boolean))
(define (divide? x y)
(let ([r (/ x y)])
(< (- r (floor r)) 1e-6)))
它有效(嗯......解决方案是正确的)我的速度提高了30%-40%。
这有多可以接受?人们真的在现实生活中这样做吗?如果没有,使用整数时优化类型/球拍解决方案的最佳方法是什么?或者在处理整数时是否应该完全放弃键入/球拍并保留浮点计算的问题?
答案 0 :(得分:4)
在大多数情况下,解决方案是使用更好的算法,而不是转换为Typed Racket。
由于Project Euler的大多数问题都涉及整数,所以这里有一些提示和技巧:
除法运算符/
需要计算分母和分子之间的最大公共除法,以便抵消公因子。如果您只想知道一个数字是否除以另一个数字,这会使/
成为一个糟糕的选择。使用(= (remainder n m) 0)
检查m
是否划分n
。另外:当你知道除法的余数为零时,使用quotient
rander而不是/
。
使用memoization避免重新计算。即使用向量来存储已计算的结果。示例:https://github.com/racket/math/blob/master/math-lib/math/private/number-theory/eulerian-number.rkt
首先实现一个朴素的算法。然后考虑如何减少案件数量。经验法则:如果你能将案件数量减少到1-10万,那么蛮力最有效。
减少查找搜索空间参数化的个案数量。示例:如果您需要找到毕达哥拉斯三元组:在数字m和n上循环,然后计算a = m^2 - n^2
,b = 2mn
和c = m^2 + n^2
。这比循环a,b和c更快,跳过那些^ 2 + b ^ 2 = c ^ 2不成立的三元组。
在math/number-theory
的来源中查找提示和技巧。
答案 1 :(得分:2)
由于我无法提供任何一般的提示,因此我没有想要成为一个真正的答案,因为我最近没有发布过“Amicable数字” 问题21“,我想也许在这里留下你的解决方案(遗憾的是没有很多Lisp解决方案在Euler上发布......)。
(define (divSum n)
(define (aux i sum)
(if (> (sqr i) n)
(if (= (sqr (sub1 i)) n) ; final check if n is a perfect square
(- sum (sub1 i))
sum)
(aux (add1 i) (if (= (modulo n i) 0)
(+ sum i (/ n i))
sum))))
(aux 2 1))
(define (amicableSum n)
(define (aux a sum)
(if (>= a n)
sum
(let ([b (divSum a)])
(aux (add1 a)
(if (and (> b a) (= (divSum b) a))
(+ sum a b)
sum)))))
(aux 2 0))
> (time (amicableSum 10000))
cpu time: 47 real time: 46 gc time: 0
在处理除数时,通常可以使用divSum
停止在n的平方根处。当你找到一个友好的对时,你也可以同时将两者加到总和中,这样可以节省你在我的代码中不必要的(divSum b)
计算。