定义ISPRIME功能时遇到问题

时间:2016-09-16 01:01:32

标签: lisp common-lisp primality-test

我对LISP很新,正在解决一些初学者问题。我尝试定义一个ISPRIME函数,但似乎没有正常工作。这是我的代码:

 (defun ISPRIME (n &optional (d (- n 1))) 
      (if (= d 0)
           ( return-from ISPRIME t )) 
      (if (= (mod n d) 0)
           ( return-from ISPRIME nil )) 
      (ISPRIME n (- d 1)))

但是在运行我的代码时,我使用值5作为示例:

 (ISPRIME 5)
 Nil

5应该是素数。我怀疑所有内容都落入:(if(=(mod nd)0)语句,它不应该是.d应该继续减少直到达到0并返回true,但它不会。我似乎无法看看我的逻辑错误发生在哪里。

非常感谢任何和所有帮助!

3 个答案:

答案 0 :(得分:4)

你的代码中有一个错误:函数应该停在1而不是0,因为任何数字都可以被1整除。只需改变函数就这样:

(defun ISPRIME (n &optional (d (- n 1))) 
      (if (= d 1)                         ; <- change from 0 to 1
           ( return-from ISPRIME t )) 
      (if (= (mod n d) 0)
           ( return-from ISPRIME nil )) 
      (ISPRIME n (- d 1)))

但请注意,您的函数效率不高,因为return-from从函数的最后一次调用返回,而不是从“递归塔”返回,因此可以通过以这种方式消除它来重写函数,使用更紧凑的“条件”cond,而不是if,并将return-from替换为结果:

(defun isprime (n &optional (d (- n 1)))
  (cond ((= d 1) t)
        ((= (mod n d) 0) nil)
        (t (isprime n (- d 1)))))

这仍然是递归的,但对于Common Lisp更为惯用。当然,可以在更有效的迭代版本中转换此函数,并应用更多efficient algorithm。但是请注意,智能编译器会在迭代中自动转换此函数,因为函数是tail recursive

您还可以添加参数n大于1的初始检查,例如:

(defun isprime (n &optional (d (- n 1)))
  (cond ((<= n 1) nil)
        ((= d 1) t)
        ((= (mod n d) 0) nil)
        (t (isprime n (- d 1)))))

答案 1 :(得分:2)

由于您计算的是布尔值,by的实例可以替换为(if test T NIL),更一般地说,结果可能表示为单个布尔表达式。我倾向于发现它们更具可读性。以下是对接受的答案的一点修改作为例子:

test

为了完整性,并根据Will Ness的回答,这是一个LOOP版本:

(defun is-prime (n &optional (d (1- n)))
  (or (= d 1)
      (and (plusp d)
           (plusp (mod n d))
           (is-prime n (1- d)))))

等效的(defun is-prime (n) (loop for d = 2 then (+ d add) for add = 1 then 2 thereis (> (* d d) n) until (= (mod n d) 0))) 版本:

DO

答案 2 :(得分:2)

您不应该使用(尾部)递归,因为Common Lisp没有tail call optimization保证。请改为使用doloop,甚至prog使用go

在算法上, 总是 增加 顺序测试潜在的除数,从 2 <开始/ em>,当您超过 (sqrt n) 时停止:

(defun ISPRIME (n) 
  (prog ((d 2))                          ; defines implicit block named NIL
    LOOP
      (if (> (* d d) n)
           ( return-from ISPRIME t ))    ; (return T) also works
      (if (= (mod n d) 0)
           ( return-from ISPRIME nil ))  ; or just (return) 
      (if (= d 2)
        (incf d 1)
        (incf d 2))
      (go LOOP)))

(return)(return nil)相同,(return val)(return-from NIL val)相同。由于prog定义了一个名为NIL的隐式块,更短,更通用,因此可以在那里使用对return的调用。

这里有一个有趣的方法是使用一个可扩展的素数列表,它通过使用此isprime函数过滤增加的数字来创建,在另一个{{{}}中用作除数。 1}}函数只能通过这些素数来测试它的参数,而不是所有的几率,从而实现另一种算法性能增益。素数列表应根据需要进行扩展。素数只会达到被测数的平方根,素数本身只需要通过最高素数的平方根的数字来测试(所以,被测数的第四个根)。