【问题标题】:Racket Programming. Where am I going wrong?球拍编程。我哪里错了?
【发布时间】:2013-03-29 08:29:28
【问题描述】:

我要回答的问题:
13195 的质因数是 5、7、13 和 29。 600851475143这个数的最大质因数是多少?

我哪里出错了?我的素数? test 似乎是问题所在,但它在相对较小的数字上运行良好。然而素数?测试给出了较大数字的错误答案。有没有更简单的方法来解决这个问题?

    (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)))

【问题讨论】:

  • 可变变量让人难以推理,而这正是您的 prime? 函数出现问题的地方:b 在调用 prime? 之间没有被重置。但是你甚至不应该在这里使用全局可变变量。使用函数本地的临时变量。例如(define (prime? n) (define (iter b) &lt;body-of-your-function&gt;) (iter 3))。如果您使用的是set!,那么您必须考虑许多问题。如果可以的话,最好避免set!
  • 要说明您的prime? 损坏的原因,请执行以下顺序并观察结果:(prime? 7) (prime? 9)。您应该立即看到有一些时髦的东西。 :)

标签: scheme racket primes prime-factoring


【解决方案1】:

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

 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] 

该算法可能适用于所涉及的特定数字,但不幸的是它通常是不正确的 - 例如对于整数平方根为 3000 的 9000009,它将返回 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。这意味着我们可以在 它的平方根处停止,而不是在 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 step start 是另一种模式,它收集所有中间结果(以及我们不需要的起始值,所以我们 drop它)。为了仅查看因素本身,我们将每个结果的第一个组成部分用map fst 表示。一个数是素数,如果它是唯一的除数,大于 1,它本身。测试:

> pe3 9000009
9901
> factorizing 9000009
[(3,3000003),(3,1000001),(101,9901),(9901,1)]
> factors 9000009
[3,3,101,9901]

> pe3 600851475143
6857
> factorizing 600851475143
[(71,8462696833),(839,10086647),(1471,6857),(6857,1)]   -- 1471 is the largest
> factors 600851475143                                  --   factor tried, 
[71,839,1471,6857]                                      --   *not* 775146 !!

> factors 600851475143999917      -- isqrt 600851475143999917 == 775146099
[41,37309,392798360393]           -- isqrt 392798360393       ==    626736

【讨论】:

【解决方案2】:

简单回答:

$ factor 600851475143
600851475143: 71 839 1471 6857

更严肃的回答:你的prime?功能确实坏了;我什至不确定它想做什么。 (另外,你的 (= n 2) 测试太晚了,没有用处:(even? n) 测试胜过了它。)

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

【讨论】: