包含所有数字的最小子列表

时间:2019-04-04 19:31:46

标签: sml smlnj

我正在尝试用sml编写一个程序,该程序占用列表的长度,将出现在列表中的最大数量,当然还有列表。然后,它计算包含所有数字的最小“子列表”的长度。

我尝试使用滑动窗口方法,具有两个索引,即前和尾。前端先扫描,当找到一个数字时,它将写入地图中已经看到该数字的次数。如果程序找到所有数字,则调用尾号。尾巴扫描列表,如果发现一个数字被查看的次数大于1,则将其删除。

到目前为止,我尝试过的代码如下:

structure Key=
 struct
  type ord_key=int
  val compare=Int.compare
 end


fun min x y = if x>y then y else x;


structure mymap = BinaryMapFn ( Key  );

fun smallest_sub(n,t,listall,map)=
let
 val k=0
 val front=0
 val tail=0

 val minimum= n; 

 val list1=listall;
 val list2=listall;

 fun increase(list1,front,k,ourmap)=
  let 
   val number= hd list1
   val elem=mymap.find(ourmap,number)
   val per=getOpt(elem,0)+1

   fun decrease(list2,tail,k,ourmap,minimum)=
    let 
     val number=hd list2
     val elem=mymap.find(ourmap,number)
     val per=getOpt(elem,0)-1
     val per1=getOpt(elem,0)
    in
     if k>t then
      if (per1=1) then decrease(tl list2,tail+1,k-1,mymap.insert(ourmap,number,per),min minimum (front-tail))
      else decrease(tl list2,tail+1,k,mymap.insert(ourmap,number,per),min minimum (front-tail))
     else increase (list1, front,k,ourmap)
    end

  in
   if t>k then
    if (elem<>NONE) then increase (tl list1,front+1,k,mymap.insert(ourmap,number,per))
    else increase(tl list1,front+1,k+1,mymap.insert(ourmap,number,per))
   else (if (n>front) then decrease(list2,tail,k,ourmap,minimum) else minimum)
  end


in
  increase(list1,front,k,map)
end


fun solve (n,t,acc)= smallest_sub(n,t,acc,mymap.empty)

但是当我用这个smallest_sub(10,3,[1,3,1,3,1,3,3,2,2,2,1])称呼它时;这是行不通的。我做错了什么?

示例:如果输入为1,3,1,3,1,3,3,2,2,1,则程序应识别出包含所有数字且最小的列表中的parto为1,3, 3,2和3,2,2,1,因此输出应为4

1 个答案:

答案 0 :(得分:2)

“包含所有值的最小子列表”的问题似乎再次出现 没有成功答案的新问题。这是因为它不是minimal, complete, and verifiable example

因为您使用“滑动窗口”方法,所以索引了前 在您输入的内容中,花费 O(n)时间来索引元素的列表并不理想。您 确实想在这里使用数组。如果您的输入函数必须有一个列表,则您 可以出于算法目的将其转换为数组。

我想在回答之前对代码进行清理,因为运行 您当前的手工代码有点难,因为它是如此的简洁。这是一个 如何提取给定是否给定的簿记的示例 子列表包含原始列表中每个元素的至少一个副本:

编辑::最初发布后,我更改了以下代码。

structure CountMap = struct
    structure IntMap = BinaryMapFn(struct
        type ord_key = int
        val compare = Int.compare
    end)

    fun count (m, x) =
        Option.getOpt (IntMap.find (m, x), 0)

    fun increment (m, x) =
        IntMap.insert (m, x, count (m, x) + 1)

    fun decrement (m, x) =
        let val c' = count (m, x)
        in if c' <= 1
           then NONE
           else SOME (IntMap.insert (m, x, c' - 1))
        end

    fun flip f (x, y) = f (y, x)
    val fromList = List.foldl (flip increment) IntMap.empty
end

也就是说,CountMapint IntMap.map,其中Int代表 地图的固定键类型,int,其前面的参数int 表示地图的值类型,是此次数的 count 价值发生了。

在构建下面的initialCountMap时,请使用CountMap.increment,然后 当您使用“滑动窗口”方法时,可以使用CountMap.decrement 产生一个新的countMap,您可以对其进行递归测试。

如果将出现的次数减少到1以下,那么您正在查看的子列表 至少每个元素都不包含一次;我们排除任何解决方案 让CountMap.decrement返回NONE

将所有这些机制抽象出来,算法本身就变得非常重要 更容易表达。首先,我想将列表转换为数组,以便 索引变为 O(1),因为我们将做很多索引工作。

fun smallest_sublist_length [] = 0
  | smallest_sublist_length (xs : int list) =
    let val arr = Array.fromList xs
        val initialCountMap = CountMap.fromList xs
        fun go countMap i j =
            let val xi = Array.sub (arr, i)
                val xj = Array.sub (arr, j)
                val decrementLeft = CountMap.decrement (countMap, xi)
                val decrementRight = CountMap.decrement (countMap, xj)
            in
                case (decrementLeft, decrementRight) of
                   (SOME leftCountMap, SOME rightCountMap) =>
                     Int.min (
                       go leftCountMap (i+1) j,
                       go rightCountMap i (j-1)
                     )
                 | (SOME leftCountMap, NONE) => go leftCountMap (i+1) j
                 | (NONE, SOME rightCountMap) => go rightCountMap i (j-1)
                 | (NONE, NONE) => j - i + 1
            end
    in
      go initialCountMap 0 (Array.length arr - 1)
    end

这似乎可行,但是...

执行Int.min (go left..., go right...)会产生 O(n ^ 2)堆栈的费用 记忆体(如果您不能排除其中之一是最佳的)。这是一个 动态编程的好用例,因为您的递归子问题有一个 常见子结构,即

go initialCountMap 0 10
 |- go leftCountMap 1 10
 |   |- ...
 |   `- go rightCountMap 1 9  <-.
 `- go rightCountMap 0 9        | possibly same sub-problem!
     |- go leftCountMap 1 9   <-'
     `- ...

所以也许有一种方法可以将递归子问题存储在内存阵列中,而不是 如果您知道此子问题的结果,请执行递归查找。如何 就SML而言,做备忘录本身就是一个很好的问题。纯粹怎么做 使用非惰性语言进行功能记忆是更好的选择。

您可以进行的另一种优化是,如果找到子列表, 大小独特元素的数量,您无需再寻找。这个号码 偶然是initialCountMapIntMap中元素的数量 可能有找到它的功能。