Math Euler#211项目-效率问题

Math Euler#211项目-效率问题,math,common-lisp,Math,Common Lisp,我一直在慢慢地研究ProjectEuler问题列表,我已经找到了一个我知道如何解决的问题,但似乎我不能(考虑到我的解决方案的编写方式) 我正在使用CommonLisp来实现这一点,我的脚本已经运行了超过24小时(远远超过了他们一分钟的目标) 为了简洁起见,以下是我的解决方案(这是一个扰流器,但前提是你有一个极快的处理器): (defun square?(num) (如果(集成ERP(sqrt num))T) (defun因子(num) (让((l’())) (do((电流1(1+电流))) ((

我一直在慢慢地研究ProjectEuler问题列表,我已经找到了一个我知道如何解决的问题,但似乎我不能(考虑到我的解决方案的编写方式)

我正在使用CommonLisp来实现这一点,我的脚本已经运行了超过24小时(远远超过了他们一分钟的目标)

为了简洁起见,以下是我的解决方案(这是一个扰流器,但前提是你有一个极快的处理器):

(defun square?(num)
(如果(集成ERP(sqrt num))T)
(defun因子(num)
(让((l’()))
(do((电流1(1+电流)))
((>current(/num current)))
(如果(=0(模数当前))
(如果(=当前(/num当前))
(setf l(追加l(当前列表)))
(setf l(追加l(列出当前(/num current(()()()))))
(排序l#'<))
(第2(n)节)
(减少#'+(映射车(λ(x)(*x))(因子n)))
(定义和除数平方(限制)
(当(平方?(o_2 i))求和i时,i从1到极限的循环)
(defun euler-211()
(和除数平方64000000)
使用更小、更友好的测试参数解决问题所需的时间似乎比指数增长的时间要长。。。这是一个真正的问题

花了:

  • 0.007秒解算100
  • 0.107秒解算1000
  • 2.020秒解10000
  • 56.61秒解算100000
  • 1835.385秒为1000000解算
  • 超过24小时解决64000000个问题
我真的想弄清楚脚本的哪一部分导致它花了这么长时间。我在记忆factors函数时花了一些心思,但我不知道如何实际实现它

对于那些想看看问题本身的人来说

任何关于如何使这件事进行得更快的想法都将不胜感激


**很抱歉,如果这对任何人来说都是一个剧透,它不是注定的。。。。但是,如果您有足够的计算能力在相当长的时间内运行此程序,您将获得更大的能力。

对不起,我对LISP的理解不够透彻,无法阅读您的答案。但我的第一印象是,暴力解决方案的时间成本应该是:

开放式支架

sqrt(k)求k的除数(通过尝试除法),对每个除数求平方(每个因子的恒定时间),然后求和(每个因子的恒定时间)。这是σ2(k),我称之为x

加上

不确定一个好的整数平方根算法的复杂度是多少,但肯定不比sqrt(x)(哑试乘)差。x很可能比k大,所以我保留判断,但x显然是以k^3为界的,因为k最多有k个因子,每个因子本身不大于k,因此其平方不大于k^2。我已经很久没有拿到数学学位了,我不知道牛顿-拉斐逊的收敛速度有多快,但我怀疑它比sqrt(x)快,如果所有其他方法都失败的话,二元切分就是log(x)

闭式括号

乘以n(k的范围为1..n)

因此,如果你的算法在哑sqrt的情况下比O(n*sqrt(n^3))=O(n^(5/2)),或者在聪明的sqrt的情况下比O(n*(sqrt(n)+log(n^3))=O(n^3/2)差,我认为算法中出现了一些应该可以识别的错误。在这一点上,我被卡住了,因为我无法调试你的LISP

哦,我假设算术对于使用中的数字来说是常数时间。它很适合小到6400万的数字,并且它的立方体刚好适合一个64位无符号整数。但是即使您的LISP实现使算术比O(1)差,它也不应该比O(logn)差,所以它不会对复杂度有太大影响。当然不会使它成为超级多项式

这就是有人来告诉我我错了


哎呀,我刚看了你的实际计时数据。它们并不比指数更糟糕。忽略第一个和最后一个值(因为小时间无法准确测量,而且你还没有分别完成),n乘以10乘以时间不超过30 ish。30大约是10^1.5,这对于上面描述的暴力来说是正确的。

我想你可以用一个像素筛一样的东西来解决这个问题。不过这只是我的第一印象。

我认为你的算法不是最有效的。提示:你可能是明星他从错误的方向出发

编辑:我想补充一点,选择64000000作为上限可能是问题海报告诉你要想更好的办法

编辑:一些效率提示:

  • 而不是

我会通过对数字进行素数分解(例如:300=2^2*3^1*5^2)来解决这个问题,这是一个相对快速的过程,特别是如果你通过筛选来生成它的话。由此,通过迭代I=0..2;j=0..1;k=0..2,再进行2^I*3^j*5^k来生成因子相对简单

5 3 2 ----- 0 0 0 = 1 0 0 1 = 2 0 0 2 = 4 0 1 0 = 3 0 1 1 = 6 0 1 2 = 12 1 0 0 = 5 1 0 1 = 10 1 0 2 = 20 1 1 0 = 15 1 1 1 = 30 1 1 2 = 60 2 0 0 = 25 2 0 1 = 50 2 0 2 = 100 2 1 0 = 75 2 1 1 = 150 2 1 2 = 300 5 3 2 ----- 0 0 0 = 1 0 0 1 = 2 0 0 2 = 4 0 1 0 = 3 0 1 1 = 6 0 1 2 = 12 1 0 0 = 5 1 0 1 = 10 1 0 2 = 20 1 1 0 = 15 1 1 1 = 30 1 1 2 = 60 2 0 0 = 25 2 0 1 = 50 2 0 2 = 100 2 1 0 = 75 2 1 1 = 150 2 1 2 = 300
这可能不够快

这里有一个解决方案,要记住[Project]Euler的精神。[警告:扰流板。我已尝试将提示保持缓慢,以便您可以只阅读部分答案,如果需要,可以自己思考。]

当你面对一个与数字有关的问题时,一个好的策略(你可能已经从解决210个项目Euler问题中知道)是看一些小例子,找到一个模式,并证明它。[最后一部分可能是可选的,取决于您对数学的态度;-)]

但是,在这个问题中,看一些小例子——对于n=1,2,3,4,。。。可能不会给你任何提示。但在处理数论问题时,还有另一种意义上的“小例子”,你现在可能也知道了——素数是n的构造块 (setf l (append l (...))) (push (...) l) (do ((l ()) (current 1 (+ current 1))) ((> current (/ num current)) l) ...) 5 3 2 ----- 0 0 0 = 1 0 0 1 = 2 0 0 2 = 4 0 1 0 = 3 0 1 1 = 6 0 1 2 = 12 1 0 0 = 5 1 0 1 = 10 1 0 2 = 20 1 1 0 = 15 1 1 1 = 30 1 1 2 = 60 2 0 0 = 25 2 0 1 = 50 2 0 2 = 100 2 1 0 = 75 2 1 1 = 150 2 1 2 = 300
$ (factors 10) => (1 2 5 10)
$ (factors 10) => ((2 5) (1 10))
(defun o_2 (n)
"sum of squares of divisors"
  (reduce #'+ (mapcar (lambda (x) (* x x)) (reduce #'append (factors n)))))