Самый маленький подсписок, который содержит все числа - PullRequest
0 голосов
/ 04 апреля 2019

Я пытаюсь написать программу в формате 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)

Но когда я звоню с этим small__sub (10,3, [1,3,1,3,1,3,3,2,2,1]); это не работает. Что я сделал не так ??

Пример: если input равен 1,3,1,3,1,3,3,2,2,1, программа должна распознать, что parto в списке, который содержит все числа и наименьшее, равно 1,3, 3,2 и 3,2,2,1, поэтому на выходе должно быть 4

1 Ответ

2 голосов
/ 05 апреля 2019

Эта проблема "наименьшего подсписка, который содержит все значения", кажется, повторяется в новых вопросах без успешного ответа.Это потому, что это не минимальный, полный и проверяемый пример .

Поскольку вы используете подход с «скользящим окном», индексируя переднюю и заднюю часть вашегона вход, список, требующий 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

То есть CountMap - это int IntMap.map, где Int представляет фиксированный тип ключа карты, равный int, а *Параметр 1022 * перед ним представляет тип значения карты, представляющий собой count от того, сколько раз это значение имело место.

При построении initialCountMap ниже вы используете CountMap.increment и когда вы используете подход «скользящего окна», вы используете CountMap.decrement для создания нового countMap, который вы можете проверить рекурсивно.

Если вы уменьшаете вхождение ниже 1, вы ищетев подсписке, который не содержит каждый элемент хотя бы один раз;мы исключаем любое решение, позволяя CountMap.decrement return 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 - хороший вопрос сам по себе.Как сделать чисто функциональное запоминание на не ленивом языке, еще лучше.

Еще одна оптимизация, которую вы можете сделать, это то, что если вы когда-нибудь найдете подсписок размером с число уникальных элементов, вам нужносмотреть не дальше.Это число случайно является количеством элементов в initialCountMap, и IntMap, вероятно, имеет функцию для его поиска.

...