How to check if the result of a calculation is natural?

For another Euler struggle of the project (using SBCL 1.3.17) I want to check if a number <5 digit number is a pentagonal number. This can be easily checked if the result

(/ (+ 1 (sqrt (+ 1 (* 24 number)))) 6)

      

- natural number. Ignoring that a natural number is not an integer because it number

will only have positive values, I started using it intergerp

as a predicate, but it didn't work for all the numbers tested. So I came up with the following:

(defun is-pentagonal-p (number)
  "Returns T if NUMBER is a pentagonal number."
  (multiple-value-bind (n m)
      (floor (/ (+ 1 (sqrt (+ 1 (* 24 number)))) 6))
    (declare (ignore n))
    (when (zerop m) t)))

      

Which is great for simple examples i.e. with a low number, but again with an error for large numbers like 1533776805. After a while, I remembered my old Fortran days and ended up with:

(defun is-pentagonal-p (number)
  "Returns T if NUMBER is a pentagonal number."
  (multiple-value-bind (n m)
      (floor (/ (+ 1.0d0 (sqrt (+ 1.0d0 (* 24.0d0 number)))) 6.0d0))
    (declare (ignore n))
    (when (zerop m) t)))

      

which clearly reduce rounding errors and give correct results, but leave me feeling like I must have missed something much simpler and more lazy. Is it just paranoia?

+3


source to share


1 answer


Substance

If you are using CLISP you will get

(defun pentagonal-side (number)
  (/ (+ 1 (sqrt (+ 1 (* 24 number)))) 6))
(pentagonal-side 51)
==> 6
(pentagonal-side 1533)
==> 32.135838
(pentagonal-side 1533776805)
==> 31977

      

This is because ANSI CL allows you to sqrt

return rational decisions, and CLISP does. Thus, you can use integerp

:

(integerp (pentagonal-side 1533776805))
==> T

      

If your lisp ( SBCL ) always returns float, then you need to use enough precision , e.g .:

(pentagonal-side 1533776805d0) ; double-float
==> 31977.0d0
(pentagonal-side 1533776805f0) ; single-float
==> 31977.0
(pentagonal-side 1533776805s0) ; short-float
==> 31976.8s0

      

So, in your case, just pass the appropriate float

:

(zerop (mod (pentagonal-side 1533776805d0) 1))
==> T

      

Caveat

Seems to single-float

be enough, right?

(zerop (mod (pentagonal-side 1533776805f0) 1))
==> T

      

Nope!

(zerop (mod (pentagonal-side (1+ 1533776805f0)) 1))
==> T

      

Use "roundtrip"

It is not always easy to guess in advance which float type is appropriate. Moreover, it is quite possible that yours number

will be too big even for your lisp long-float

. (CLISP has arbitrary float precision , most lisps don't, and even then you have to decide which precision to use beforehand.)



This way it's easier to stick with integers: make sure the calculated one pentagonal-side

matches the original number via roundtrip:

(defun pentagonal-side-int (area)
  (/ (+ 1 (isqrt (+ 1 (* 24 area)))) 6))
(defun pentagonal-area (side)
  (/ (- (* 3 side side) side) 2))
(pentagonal-side-int 1533776805)
==> 31977
(pentagonal-area 31977)
==> 1533776805
(defun pentagonal-number-p (number)
  (let ((side (pentagonal-side-int number)))
    (and (integerp side)
         (= number (pentagonal-area side)))))
(pentagonal-number-p 1533776805)
==> T
(pentagonal-number-p 1533776804)
==> NIL
(pentagonal-number-p 1533776806)
==> NIL

      

Style

Names

Styles cannot be mixed. is-...

is C / Java style. ...-p

- lisp style. I suggest you stick with the latter for your lisp code.

Floating

No need to convert all your numbers to floats:

(defun pentagonal-side-double (number)
  (/ (+ 1 (sqrt (+ 1 (* 24 (float number 1d0))))) 6))

      

should do all your calculations use double-float

.

Return value

Use (zerop m)

instead (when (zerop m) t)

. Generally speaking, when

is is used in the "procedural context" when the return value is discarded. If you are using value, you should use if

, and is (if (zerop m) t nil)

exactly the same as (zerop m)

.

Several meanings

You should probably use plus nth-value

instead .multiple-value-bind

ignore

Standard functions

More readable to write (1+ ...)

than (+ 1 ...)

.

+4


source







All Articles