【问题标题】:My Quicksort doesn't work with negative numbers (Common Lisp)我的快速排序不适用于负数(Common Lisp)
【发布时间】:2013-08-30 09:02:25
【问题描述】:

该函数仅适用于正数。有时适用于负数,但大多数时候显示此错误“值 -1 不是 UNSIGNED-BYTE 类型”。

(defun OrdRapido (lista inf sup)
  (let ((pivote (nth sup lista)) (i (1- inf)) (j sup) (aux))
    (unless (>= inf sup)
        (loop (when (>= i j) (return))
          (loop (setf i (1+ i)) (when (>= (nth i lista) pivote) (return)))
          (loop (setf j (1- j)) (when (<= (nth j lista) pivote) (return)))
          (when (< i j)
            (setf aux (nth i lista))
            (setf (nth i lista) (nth j lista))
            (setf (nth j lista) aux)))  
        (setf aux (nth i lista))
        (setf (nth i lista) (nth sup lista))
        (setf (nth sup lista) aux) 
        (OrdRapido lista inf (1- i))
        (OrdRapido lista (1+ i) sup)) 
    lista)) 

例如:

(setq lista2 '(-5 3 7 6 2 1 -4 100 5 80 96 14 2 3 1 0 0 789 -1 3))
(OrdRapido lista2 0 (1- (length lista2)))

CL-USER> 
(-5 3 7 6 2 1 -4 100 5 80 96 14 2 3 1 0 0 789 -1 3)
CL-USER> 
(-5 -4 -1 0 0 1 1 2 2 3 3 3 5 6 7 14 80 96 100 789)

但是这个不行,我只加-到80

'(-5 3 7 6 2 1 -4 100 5 -80 96 14 2 3 1 0 0 789 -1 3)

谢谢

修正版(随机添加),我知道有更好的方法,这只是教授留下的练习

 (defun OrdRapido (lista inf sup)
  (let ((pivote (nth (random (1+ sup)) lista)) (i inf) (j sup) (aux))
    (unless (>= inf sup)
        (loop (when (> i j) (return))
          (loop (when (<= (nth i lista) pivote) (return)) (setf i (1+ i)))
          (loop (when (>= (nth j lista) pivote) (return)) (setf j (1- j)))
          (when (<= i j)
            (setf aux (nth i lista))
            (setf (nth i lista) (nth j lista))
            (setf (nth j lista) aux)
            (setf i (1+ i))
            (setf j (1- j))))  
        (OrdRapido lista inf j)
        (OrdRapido lista i sup)) 
    lista))

【问题讨论】:

  • 和负数无关,试试'(2 1)
  • 答案中显示的这些“其他方式”(即递归扁平树变体)是否“更好”,值得商榷。您的代码看起来像真正的快速排序,就地分区。您可以通过随机选择枢轴来改进它,以防止有序输入的急剧下降。
  • 感谢您的信息,我刚刚添加了随机枢轴 :)
  • 无论这是否是“真正的”快速排序...您的代码具有灾难性的渐近复杂性。您应该重写它以使用数组,或者使用列表完全不同。你在这里所做的超出了二次时间。
  • 基本上,可以这样想:如果您使用nth 并带有一个常量小于,例如 8,那么您很好。但如果不是,你做事的方式是错误的。但最好不要使用它/仅用于调试。

标签: types lisp common-lisp quicksort negative-number


【解决方案1】:

您正在尝试返回列表的第 -1 个元素,但该元素不起作用。 nth 返回列表的第 n 个元素,因此 (nth 0 '(1 2 3)) 将返回 1。但在您的代码中的某个时刻,它调用 (nth -1 (-5 -80 -4 -1 0 0 ... )) 和繁荣!

关于您的代码的其他说明:

  • 将结尾的 ) 放在单独的行上不是好的 lisp 样式,并且会使您的代码更难阅读。 Lisp 代码由列表组成,括号并不像其他语言的花括号。
  • 使用支持您的语言的编辑器。我推荐带有 Slim 的 Vim 或带有 slime 的 Emacs。如果你使用带有 slime 的 emacs this video may help you get started
  • 不要在你的名字中使用驼色外壳。所有符号在被实习时都以通用 lisp 大写,因此 'HelloThere'hellothere'HELLOTHERE完全相同 /强>

另外,请查看at the rosettacode page 以了解如何在 common-lisp(以及许多其他语言)中进行快速排序。

;; Here is a functional version for example.
(defun quicksort (list &aux (pivot (car list)) )
  (if (rest list)
      (concatenate 'list 
             (quicksort (remove-if-not #'(lambda (x) (< x pivot)) list))
             (remove-if-not #'(lambda (x) (= x pivot)) list)
             (quicksort (remove-if-not #'(lambda (x) (> x pivot)) list)))
      list))

如果您不习惯阅读 lambdas,那么这里的代码与上面相同,但将 lambdas 制成本地函数,因此代码读起来更像英语。

(defun quicksort (list &aux (pivot (car list)))
  (labels ((less-than-pivot (x) (< x pivot))
           (greater-than-pivot (x) (> x pivot))
           (equal-to-pivot (x) (= x pivot)))
    (if (rest list)
        (concatenate 'list 
                     (quicksort (remove-if-not #'less-than-pivot list))
                     (remove-if-not #'equal-to-pivot list)
                     (quicksort (remove-if-not #'greater-than-pivot list)))
        list)))

在我看来,第二个版本不太整洁。在这两个示例中,您都可以看到递归方法如何让您考虑如何只做一步,然后通过调用自身,您将解决方案应用于一步以获得整个问题的解决方案

【讨论】:

  • 非常感谢。我将停止将 ) 放在其他行中,我是 Lisp 的新手,我还不习惯它。我知道 Hello 和 HELLO 是一样的,我只是喜欢它。我知道有更好的方法可以用 lisp 进行快速排序,但由于我刚刚开始练习,我想实现“经典”方式。我在代码中发现了错误,我只是不知道错误是什么意思,再次感谢:)
  • 除了它不是一个“真正的”快速排序(即不进行原地数组分区,这是 Hoare 认为是他真正的发明 - 或者故事是这样的)。这个递归代码构建了一个三叉树,已经扁平化了。所以它是一个 treesort (构建一棵树,然后将其展平)。 (这当然不是我的想法;有 a reddit thread about it,基于 Haskell 等价物)。
  • 好吧,它会教我复制和粘贴而不检查。有时间我会解决这个问题
  • @Baggers 请不要删除任何内容。 :)
  • 好的,我将保持原样,到处都有很好的讨论,为什么这不是真正的快速排序,以及帮助人们实现真正快速排序的好链接。感谢大家的出色投入
【解决方案2】:

只是因为您最初的想法是使用loop 来执行此操作。以下是您可能已经完成的方法:

(defun quicksort (list)
  (if (cdr list)
      (loop :with pivot := (car list)
         :for element :in list
         :if (< element pivot) :collect element :into a
         :else :if (= element pivot) :collect element :into b
         :else :collect element :into c
         :finally (return (nconc (quicksort a) b (quicksort c)))) list)) 

【讨论】:

  • 再说一遍,这不是快速排序
  • @RainerJoswig 但是如果你完全按照描述进行操作 - 你不能用列表来做,因为你需要随机访问。那么争论的意义何在? IMO,这是您可以通过列表获得的最接近的列表。
  • 它不是快速排序,而是使用列表进行慢速排序。
  • 您的示例使用列表并声称它是快速排序,但事实并非如此。
  • @RainerJoswig 树排序并不是坏,:) 它仍然是 n log n 平均情况(甚至,最坏的情况,随机枢轴)。当然对于列表,mergesort 可能更快。
【解决方案3】:

除了编译器技巧,NTH 需要 O(i) 时间来访问列表的第 i 个元素,因为它必须遍历列表中的每个元素直到该点。这意味着仅使用 NTH 访问列表的每个元素将花费 O(n^2) 时间。由于快速排序应该在 O(nlgn) 时间内执行,因此需要稍作改动。

通过在遍历列表时抓住剩余的尾部并通过该尾部而不是使用 NTH 访问,您可以对列表进行快速排序。由于 lisp 列表是单链接的,因此还必须仅向前遍历列表。

(defun simple-list-partition (list end)
  "Reorder list until end such that all values less than the first value in the
  list are placed left of it and all greater placed to its right; return the new
  index of the first value and the tail of the list starting with that value as
  multiple values."
  (loop for walk-cons on (cdr list)
        for walk-value = (car walk-cons)
        for walk-index from 1
        with pivot-value = (car list)
        with rotate-cons = list
        with rotate-index = 0
        while (<= walk-index end)
        when (< walk-value pivot-value)
        do (progn (setq rotate-cons (cdr rotate-cons)
                        rotate-index (+ 1 rotate-index))
                  (rotatef (car rotate-cons) (car walk-cons)))
        finally (progn (rotatef (car list) (car rotate-cons))
                       (return (values rotate-index rotate-cons)))))

(defun quicksort (list)
  "Quicksort for lists."
  (labels ((quicksort% (l  max)
             (when (and (plusp max) (cdr l))
               (multiple-value-bind (index tail)
                   (simple-list-partition l max)
                 (quicksort% l (1- index))
                 (quicksort% (cdr tail) (- max index 1))))))
    (quicksort% list (1- (length list)))
    list))

为简单起见,上述内容并不能防止在预排序或相等元素的列表上性能不佳。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-15
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多