DrRacket - 找到最长的递增子序列

DrRacket - find the longest increasing subsequence

我想创建用于查找 LIS 的代码。我的代码不能很好地工作。例如,如果输入是 '(1 3 5 10 9 6 7),输出应该是 '(1 3 5 6 7) 但我的程序 return '(1 3 5 10)。我究竟做错了什么?我有没有编码二叉树然后找到更高的高度?或者我可以用最简单的方式编写这个程序吗?

(define (return_sequence N lst)
  (cond
    [(empty? lst) '()]
    [(< N (first lst)) (cons                           
                           (first lst)
                           (return_sequence (first lst) (rest lst))
                           )]
    [else (return_sequence N (rest lst))]
  )
)

(define (find_longest lst1 lst2)
  (cond
    [(empty? lst1) lst2]
    [(empty? lst2) lst1]
    [(< (length lst1) (length lst2)) lst2]
    [else lst1]
  )
)

(define (LIS lst) 
  (cond
    [(empty? lst) '()]
    [else (find_longest
           (cons
            (first lst)
            (return_sequence (first lst) (rest lst)))
           (LIS (rest lst))
          )]
  )
)

在我们开始之前,先问一个问题。你认为 '(1 1) 是递增序​​列吗?现在,我假设你没有。

首先,注意我们可以将find-longest简化如下:

(define (find-longest lst1 lst2)
  (if (<= (length lst1)
          (length lst2))
      lst1
      lst2))

这非常低效,但我们暂时不用担心。它至少看起来比你的代码干净。

我假设您正在尝试将 (return_sequence N lst) 定义为 lst 的最长递增子序列,使得所述子序列的所有元素都大于 N。我建议你看看当你尝试 (return_sequence 1 '(4 2 3)) 时究竟会发生什么。我们应该期望结果是 '(2 3),但实际上是 '(4).

你需要仔细考虑你想要如何处理 (< N (first lst)) 的情况,并确保你没有犯一个愚蠢的错误(剧透 - 你正在犯一个愚蠢的错误)。

编辑:假设 (< N (first lst))。所有元素都大于 Nlst 的最长递增子序列包含或不包含 (first lst)。为了找到不存在的最长子序列,我们计算 (return_sequence N (rest lst))。为了找到最长的子序列,我们计算 (cons (first lst) (return_sequence (first lst) (rest lst))。相关的 cond 子句变为

[(< N (first lst)) (find_longest 
                      (cons (first lst) 
                            (return_sequence (first lst) (rest lst)))
                      (return_sequence N (rest lst)))]

这解决了您的问题。

另一方面,添加 minus-infinity 的值非常有帮助,条件是此值始终小于任何其他值。在那种情况下,我们可以做

(define (LIS lst)
   (return_sequence minus-infinity lst))

这样做需要一点小聪明,但这是可能的。

事实上,添加 minus_infinity 的一种巧妙方法是向 return_sequence 传递的不是数字 N,而是函数 (lambda (x) (< N x))。然后,您可以写 (less_than_N (first list)) 而不是行 (< N (first list))。然后,你可以 (define (minus_infinity x) #t).

也可以使用 gensym 创建一个新符号并将其设置为 minus_infinity。然后你想做一些相当厚颜无耻的事情,比如

(define minus_infinity (gensym))

(define (return_sequence N lst)
 (let ((< (lambda (a b) (or (equal? a minus_infinity) (< a b)))))
   cond ...)

(define (LIS lst)
  (return_sequence minus_infinity lst))

有趣的是我们没有递归定义 (<) - (lambda (a b) (or (equal? a minus_infinity) (< a b))) 中出现的 < 是原始的 <.

最后,您尝试的算法非常慢。在 O(n log n) where n = (length lst) 中可以解决此问题。您的解决方案将是一个指数时间的解决方案,但改进它是非常重要的。