球拍编程。我哪里错了?

时间:2013-03-29 08:29:29

标签: scheme racket primes prime-factoring

我想回答的问题:
13195的主要因素是5,7,13和29。 600851475143的最大主要因素是什么?

我哪里错了?我的巅峰?测试似乎是问题,但它在相对较小的数字上工作正常。不过素?测试给出了更大数字的错误答案。有没有更简单的方法来解决这个问题?

    (define b 3)

    (define z 0)

    (define divides?
      (lambda (a b)
        (= (remainder a b) 0)))

    (define (prime? n)
        (cond
          ((or (= n 1) (= n 0)) false)
          ((even? n) false)
          ((= n 2) true)
          ((= n b) true)
          ((divides? n b) false)
          (else (and (set! b (+ b 1)) (prime? n)))))

    ;Largest Prime Factor Test
    (define (LPF x)
      (cond
        ((divides? 600851475143 x)
         (cond
           ((prime? x)
            (cond
              ((> x z) (set! z x)))))))
      (if (< x 775146) (LPF (+ x 1)) (display z)))

2 个答案:

答案 0 :(得分:4)

以某种伪代码书写,你的意图似乎是

 pe3 = last [x | x <- [2 .. 775146], isPrime x, rem 600851475143 x == 0]

读取它:表示从范围2到775146绘制的x,如果isPrime x成立,如果600851475143 % x为0,则收集此类x;返回最大的。我们还在这里编写了没有括号的应用程序(g x),只是g x

计算余数比测试素数便宜,所以我们将交换这两个操作:

pe3 n = last [x | x <- [2 .. isqrt n], rem n x == 0, isPrime x] 

此算法可能适用于所涉及的特定数字,但遗憾的是它通常是不正确的 - 例如9000009,其整数平方根为3000,它将返回101.但 9901 是正确的答案(即9901是9000009的最大主要除数,而不是101)。

让我们首先关注找到最小素因子,而不是:

pe3a n = head ([x | x <- [2 .. isqrt n], rem n x == 0, isPrime x] ++ [n])

为什么( ... ++ [n])++表示列表的串联)?因为如果n本身是素数,则根本不会找到除数,并且第一个列表将返回空,[]。在这种情况下,答案必须是n本身。但如果没有,那么答案就是除数列表的第一个(即head)。当然,当我们找到它时,我们不需要继续寻找更多。只要一个就够了,如果我们需要的就是最小的那个。

好的,所以尝试它(在一个想象中的懒惰伪代码解释器),我们得到3作为它的第一个因素:

> pe3a 9000009
3

现在我们可以将这个数字除以3:

> div 9000009 3 
3000003

并继续使用3000003,而不是9000009.这意味着我们可以停在平方根,1732而不是3000 - 效率相当可观!此外,我们不需要从2开始 - 它已经过测试 - 我们可以从最后找到的因素开始:

pe3b (start, n) = (d, div n d)
  where
     d = head ([x | x <- [start .. isqrt n], rem n x == 0, isPrime x] ++ [n])

> pe3b (3, 3000003)
(3,1000001)

所以我们得到第二个3,然后再次将该数字除以找到的除数。

> pe3b (3, 1000001)
(101,9901)

发现的下一个素数除数是101,现在因子分解的数字是1000001/101 = 9901.我们再次从最后找到的除数101开始,因为所有较小的除数都已经尝试过了:

> pe3b (101, 9901)
(9901,1)

有趣。 isqrt(9901) == 99,列表[101 .. 99]为空,因此结果立即生成。 9901本身的第一个因素高于100,实际上高于1,因为所有以前的数字已经连续尝试过,并且被分开了。这意味着9901是一个素数,不需要测试它的素数。

事实上,通过相同的推理,这个算法找到的所有因素都可以通过构造得到保证,所有对isPrime的调用都是多余的。

还要注意在这里执行除法(剩余操作)的最大数字是101 - 3000.不仅我们的新算法是正确的,它还有更多高效!

现在您可以在Scheme中编写重复pe3b应用程序的算法并除以最后找到的因子。到达1时停止。


所以,在the same pseudocode

divStep (start, n) = (d, div n d) 
  where d = head ([x | x <- [start .. isqrt n], rem n x == 0] ++ [n])

pe3 n = fst . until ((== 1) . snd) divStep $ (2,n)  -- (1st,2nd) in a tuple

factorizing n = takeWhile ((> 1) . fst) . drop 1 . iterate divStep $ (2,n)

factors n = map fst . factorizing $ n

isPrime n = factors n == [n]      

.$视为“of”。 until pred step start 是函数重复应用的高阶模式,直到满足谓词为止(((== 1) . snd)表示结果的第二个分量等于1) 。它通常在Scheme中编码,名为let

要查看整个计算历史记录,iterate 步骤开始是收集所有中间结果(以及我们不需要的起始值)的另一种模式,因此我们{ {1}}它)。要仅查看因素本身,我们会使用drop获取每个结果的第一个组成部分。如果它是唯一的除数,大于1,则数是素数。测试:

map fst

答案 1 :(得分:0)

简单回答:

$ factor 600851475143
600851475143: 71 839 1471 6857

更严肃的回答:你的prime?功能确实被打破了;我甚至不确定它想做什么。 (另外,您的(= n 2)测试为时已晚,无法使用:(even? n)测试已胜过它。)

我的建议:实施Sieve of Eratosthenes。这是my implementation