【问题标题】:Find the largest sorted subset [duplicate]找到最大的排序子集[重复]
【发布时间】:2018-04-11 21:24:41
【问题描述】:

我需要创建一个算法,从列表中提取一个最大可能的子集,其中所有元素都是有序的。该子集可以是非连续的,但必须保留原始列表中的顺序。例如:

Input:                   Possible Output:
[1,2,8,3,6,4,7,9,5]  ->  [1,2,3,6,7,9]

人们可能会将问题改写为“我至少必须删除哪些元素才能对剩余的列表进行排序”。

我不是在寻找实现,而只是在寻找简单算法的想法。 到目前为止,我最好的方法是为每个数字构建一个带有节点的树,并且它们的子节点都是列表中的所有较大数字。那么沿着树的最长路径应该等于排序的子集。但是,这似乎过于复杂。

上下文:这是为了检查学生在测试中的答案,他们必须按大小对项目进行排序。我想知道他们有多少是正确的,相对于彼此。

【问题讨论】:

标签: algorithm


【解决方案1】:

在 Scala 中使用标准列表函数(size、map、filter、dropWhile、reduce)实现递归:

def longestClimb (nx: List[Int]) : List[Int] = nx match {
  case Nil => Nil
  case _ => {
    val lix = nx.map (n => n :: longestClimb (nx.dropWhile (_ != n).filter (_ > n)))
    lix.reduce ((a, b) => if (a.size > b.size) a else b)
  }
}

调用:

scala> val nx = List (1, 2, 8, 3, 6, 4, 7, 9, 5)   
scala> longestClimb (nx)
res7: List[Int] = List(1, 2, 3, 4, 7, 9)

Prosa:对于空列表,结果为空列表,递归过程结束。

对于整个列表,尝试每个点作为起点。让我们以 6 值为例。因为 6 被评估为 nx.dropWhile (_ != 6) (即 6, 4, 7, 9, 5) 为 (_ > 6) 过滤的最长爬升,这将前一个样本减少到 (7, 9) 导致列表 (6, 7, 9)。

这几乎不是最长的列表,而是最长子列表的候选者,但由于只搜索了一个最大的列表,lix.reduce ((a, b) => if (a.size > b.size) a else b) 的偏差会产生另一个相同长度的列表,而lix.reduce ((a, b) => if (a.size >= b.size) a else b) 会导致等长列表 (1, 2, 3, 6, 7, 9)。

为了衡量我们使用这种方法能走多远,我使用了计时和迭代函数:

def timed (name: String) (f: => Any) = {
  val a = System.currentTimeMillis
  val res = f
  val z = System.currentTimeMillis
  val delta = z-a
  println (name + ": "  + (delta / 1000.0))
  res
}

val r = util.Random

def testRandomIncreasing (max: Int) : Unit = {
  (2 to max).map { i =>
    val cnt = Math.pow (2, i).toInt
    val l = (1 to cnt).toList
    val lr = r.shuffle (l)
    val s = f"2^${i}=${cnt}\t${lr}%s"
    val res = timed (s) (longestClimb (lr))
    println (res)
  }
}

结果非常有趣:

testRandomIncreasing (7)
2^2=4   List(2, 4, 3, 1): 0.0
List(2, 3)
2^3=8   List(2, 7, 5, 6, 8, 1, 4, 3): 0.0
List(2, 5, 6, 8)
2^4=16  List(15, 6, 10, 7, 1, 16, 9, 4, 13, 14, 5, 2, 8, 11, 3, 12): 0.0
List(1, 4, 5, 8, 11, 12)
2^5=32  List(1, 5, 30, 26, 27, 7, 20, 6, 29, 23, 31, 21, 22, ...10): 0.002
List(1, 5, 6, 13, 14, 15, 16, 18)
2^6=64  List(2, 57, 7, 45, 51, 49, 4, 16, 23, 21, 5, 3, 62, ... 55): 0.899
List(2, 4, 5, 9, 12, 14, 18, 19, 26, 31, 36, 41, 42, 54, 55)
2^7=128 List(16, 106, 65, 94, 84, 13, 57, 52, 117, 48, 38, ... 110): 42.195
List(13, 14, 33, 35, 37, 40, 53, 55, 58, 74, 75, 78, 97, 114, 116, 121, 123, 128)

有趣的是,从 64 到 128 值的最后一步,时间增加了大约 40 倍。以前用另一个随机种子测试,导致大约 2000 倍,2^ 用了大约 8 分钟REPL 中有 7 个值。 2^8 个元素的测试不得不中断,因为我不想在最坏的情况下等待 11 天才能得到结果。

【讨论】:

    猜你喜欢
    • 2015-09-27
    • 2023-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-03
    • 1970-01-01
    相关资源
    最近更新 更多