如何获得Common Lisp中最小数字和第二个最小数字之间的范围?

时间:2017-04-10 13:42:50

标签: range lisp common-lisp

我正在创建一个函数,它代表Common Lisp中最小数字和第二个最小数字之间的最大数字和范围。

此功能可以使最小数量和最大数量。 (我查了一下)。但是这个功能不能成为第二个最小的数字'在其名单中......

我应该考虑哪些方法来解决这个问题? 我认为我已经满足了实现这一功能所需的足够条件。

我需要你的大帮助。 这个结果应该是这样的:

  

(我的范围'(0 7 10 2 3 -1))=> (-1 10)

这是我的代码。

(defun my-range (list-of-numbers)  
    (let ((largest (first list-of-numbers))                 
          (smallest (first list-of-numbers))                  
          (secsmallest (first list-of-numbers)))              
      (dolist (element1 (rest list-of-numbers) largest)      
        (when (> element1 largest)
          (setf largest element1)))
      (dolist (element2 (rest list-of-numbers) smallest)
        (when (< element2 smallest)
          (setf smallest element2)))
      (dolist (element3 (remove smallest list-of-numbers) secsmallest)
        (when (< element3 secsmallest)
          (setf secsmallest element3))
        (return (list (- smallest secsmallest) largest)))))

3 个答案:

答案 0 :(得分:1)

要从列表中获取两个最小的元素,我们可以一次完成。我们维护了一个列表(最多)到目前为止我们看到的两个最小值。当我们扫描项目时,我们按顺序插入此列表,始终丢弃第三个最小值。当我们完成时,我们有两个最小值的列表。 (或者,如果列表只有那么多,则列出零值或一个值。)

有一种有效的算法可以从 n 的无序列表中选择 k 最小值:quickselect

如果我们不关心招致 o(n log n)的表现,我们可以对整个列表进行排序,并从结果中选择前两个值。

我们想要两个项目的情况可以按照以下方式进行硬编码:

(defun least-two (list)
  (cond
    ((null list) nil)
    ((null (cdr list)) (car list))
    (t (let ((a (car list))
             (b (cadr list)))
       (unless (< a b)
         (rotatef a b))
       (dolist (el (cddr list) (values a b))
         (cond
           ((< el a) (shiftf b a el))
           ((< el b) (setf b el))))))))

非常简单:我在SE浏览器编辑器中输入完全正如您所看到的那样,除了两个右括号外,它似乎工作正常。 :)

我冒昧地使用values来返回多个值而不是列表。如果您想要一个列表,那么必须将其更改为(list a b),并且主cond中的第二个案例也可以返回list而不是(car list)

答案 1 :(得分:1)

使用单个dolist进行简单的实施:

(defun my-range (list-of-numbers)
  ;; I don't know what should be returned if NIL is given
  (check-type list-of-numbers cons)
  (let* ((min (first list-of-numbers))
         (min2 nil)
         (max min))
    (dolist (num (rest list-of-numbers) (list (- min (or min2 max)) max))
      (if (<= num min)
          (setf min2 min
                min num)
          (if (<= max num)
              (setf max num)
              (setf min2 num))))))

(my-range '(0 7 8 2 3 -1))
;=> (-1 8)
(my-range '(-1 0 0 0 0))
;=> (-1 0)
(my-range '(-1 0 0 0 -1))
;=> (0 0)
(my-range '(0))
;=> (0 0)

答案 2 :(得分:1)

我只是留下效率,并首先编写一个有效的解决方案:

对列表的副本进行排序,使前面的元素最小,然后选择前两个数字。

(defun two-smallest-numbers (input-list)
   (subseq (sort (copy-list input-list) #'<) 0 2))

CL-USER 20 > (two-smallest-numbers '(83 3735 44562 483 83 2223 42232 3322))
(83 83)

或返回最小的不同数字:找到最小的数字,将其从列表中删除并再次找到最小的数字。返回两个数字。

CL-USER 22 > (defun two-smallest-different-numbers (input-list)
               (flet ((smallest-number (list)
                        (reduce #'min list)))
                 (let ((s0 (smallest-number input-list)))
                   (list s0 (smallest-number (remove s0 input-list))))))
TWO-SMALLEST-NUMBERS

CL-USER 23 > (two-smallest-different-numbers '(83 3735 44562 483 83 2223 42232 3322))
(83 483)