2015-11-25 3 views
2

Доброе утро, все,Простые упражнения OCaml о списке

Я должен заниматься физическими упражнениями, но я застрял!

Ну, упражнение требует функции, которая дает список, не содержащий целых чисел, возвращает первое число с максимальным количеством вхождений.

Например:

  • режиме [1, 2, 5; 1; 2; 3; 4; 5; 5; 4: 5; 5] ==> 5
  • режим
  • [2; 1; 2; 1; 1; 2] ==> 2
  • режим [-1; 2; 1; 2; 5; -1; 5; 5; 2] ==> 2
  • режим [7] ==> 7

Важно: упражнения должны быть в функциональном программировании

Моя идея:

let rec occurences_counter xs i = match xs with 
           |[] -> failwith "Error" 
           |x :: xs when x = i -> 1 + occurences_counter xs i 
           |x :: xs -> occurences_counter xs i;; 

В этой функции я застрял:

let rec mode (l : int list) : int = match l with 
           |[] -> failwith "Error" 
           |[x] -> x 
           |x::y::l when occurences_counter l x >= occurences_counter l y -> x :: mode l 
           |x::y::l when occurences_counter l y > occurences_counter l x -> y :: mode l;; 

Заранее спасибо, я новичок в программировании и в StackOverflow Извините за мой английский

+0

может возникнуть проблема в вашем коде: x :: mode l , так как режим l возвращает int, а не список. Думаю, вы должны поставить режим (x :: l). то же исправление на другой строке. –

+1

Возможный дубликат [Min/Max и наиболее часто встречающийся элемент списка] (http://stackoverflow.com/questions/33529942/min-max-and-most-frequest-element-of-a-list) –

ответ

0

одно решение: сначала рассчитать список пар (число, вхождения). Подсказка: используйте List.assoc.

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

0

Вам необходимо обработать список входных данных при сохранении состояния, которое хранит количество вхождений каждого номера. В принципе, состояние может быть map, где ключи находятся в домене элементов списка, а значения находятся в домене натуральных чисел. Если вы будете использовать Map, алгоритм будет иметь сложность O(NlogN). Вы также можете использовать ассоциативный список (т. Е. Список типа ('key,'value) list) для реализации карты. Это приведет к квадратичной сложности. Другой подход заключается в использовании хэш-таблицы или массива длины, равной размеру входного домена. Оба будут давать вам линейную сложность.

После того, как вы собрали статистику (например, сопоставление от элемента к числу ее вхождений), вам нужно пройти через список победителей и выбрать тот, который был первым в списке.

В OCaml решение будет выглядеть следующим образом:

open Core_kernel.Std 

let mode xs : int = 
    List.fold xs ~init:Int.Map.empty ~f:(fun stat x -> 
     Map.change stat x (function 
      | None -> Some 1 
      | Some n -> Some (n+1))) |> 
    Map.fold ~init:Int.Map.empty ~f:(fun ~key:x ~data:n modes -> 
     Map.add_multi modes ~key:n ~data:x) |> 
    Map.max_elt |> function 
    | None -> invalid_arg "mode: empty list" 
    | Some (_,ms) -> List.find_exn xs ~f:(List.mem ms) 

Алгоритм следующий:

  1. Запуска через входную и вычислительной частоту каждого элемент
  2. Выполнить через статистику и вычислять спектр (т. е. отображение от частоты к элементам).
  3. Получите набор элементов с самой высокой частотой и найдите элемент в списке ввода, который находится в этом наборе.

Например, если мы возьмем образец [1;2;5;1;2;3;4;5;5;4;5;5],

  1. stats = {1 => 2; 2 => 2; 3 => 1; 4 => 2; 5 => 5}
  2. mods = {1 => [3]; 2 => [1;2]; 5 => [5]}

Вам необходимо установить core библиотеку, чтобы играть с ним. Используйте coretop, чтобы играть с этой функцией в верхнем углу. Или corebuild скомпилировать его, как это:

corebuild test.byte -- 

если исходный код хранится в test.ml

0

одно предложение:

ваш алгоритм может быть упрощена, если отсортировать список ранее. Это имеет сложность O (N log (N)). Затем измерьте самую длинную последовательность одинаковых чисел.

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

+0

сортировка не даст вам никаких преимуществ. Вам все равно нужно обработать весь список и собрать статистику. Тот факт, что они отсортированы, здесь не поможет. – ivg

+0

Сортировка отменяет часть о получении ** первого ** номера с максимальным вступлением (или требуется дополнительный шаг) – Sehnsucht

+0

@ivg при обработке списка вам нужно только сохранить в памяти длину самой длинной последовательности и аргумент, реализующий Это. – jimifiki

0

Это, вероятно, не самый красивый код, но вот с чем я пришел (F #). Сначала я преобразовываю каждый элемент в промежуточный формат. Этот формат содержит сам элемент, позицию его возникновения и сумму, в которой он произошел.

type T<'a> = { 
    Element: 'a 
    Position: int 
    Occurred: int 
} 

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

[1;3] 

будет первым преобразован в

[{Element=1;Position=0;Occurred=1}; {Element=3;Position=1;Occurred=1}] 

Добавляя два вместе, вы только можете добавить их с тем же «Элемент». Позиция с меньшим номером из обоих взята, и Произошло только что добавлено вместе. Так что, если вы, например, иметь

{Element=3;Position=1;Occurred=2} {Element=3;Position=3;Occurred=2} 

результат будет

{Element=3;Position=1;Occurred=4} 

Идея, что я имел в виду, был Monoid. Но в реальном Моноиде вы должны были придумать, что вы также можете добавить разные элементы вместе. Попробовав какой-то материал, я чувствую, что ограничение просто добавления того же Элемента куда проще. Я создал небольшой модуль с типом. Включая некоторые вспомогательные функции для создания, добавления и сравнения.

module Occurred = 
    type T<'a> = { 
     Element: 'a 
     Position: int 
     Occurred: int 
    } 

    let create x pos occ = {Element=x; Position=pos; Occurred=occ} 
    let sameElements x y = x.Element = y.Element 
    let add x y = 
     if not <| sameElements x y then failwith "Cannot add two different Occurred" 
     create x.Element (min x.Position y.Position) (x.Occurred + y.Occurred) 
    let compareOccurredPosition x y = 
     let occ = compare x.Occurred y.Occurred 
     let pos = compare x.Position y.Position 
     match occ,pos with 
     | 0,x -> x * -1 
     | x,_ -> x 

С этой настройкой я теперь написал две дополнительные функции. Одна функция aggregate, которая сначала превращает каждый элемент в Occurred.T, группирует их по x.Element (результат - список). И затем он использует List.reduce во внутреннем списке, чтобы добавить Связанный с тем же Элементом вместе. Результатом является список, который содержит только один Occurred.T для каждого элемента с первой позицией и количеством отмеченных предметов.

let aggregate = 
    List.mapi (fun i x -> Occurred.create x i 1) 
    >> List.groupBy (fun occ -> occ.Element) 
    >> List.map (fun (x,occ) -> List.reduce Occurred.add occ) 

Вы можете использовать эту функцию агрегата для реализации различной логики агрегации. В вашем случае вам нужен только тот, который имеет самые высокие места и самую низкую позицию. Я написал еще одну функцию, которая сделала это.

let firstMostOccurred = 
    List.sortWith (fun x y -> (Occurred.compareOccurredPosition x y) * -1) >> List.head >> (fun x -> x.Element) 

1 примечание. Occurred.compareOccurredPosition написано, что оно сортирует все в порядке возрастания. Я думаю, что люди ожидают, что в этом порядке по умолчанию будет доходить до самого маленького элемента. Таким образом, по умолчанию первым элементом будет элемент с наименьшим вхождением и самой большой позицией. Путем умножения результата на -1 вы превращаете эту функцию в нисходящую функцию сортировки. Причина, почему я сделал это, это то, что я мог бы использовать List.head. Я также мог использовать List.last, чтобы получить последний элемент, но я чувствовал, что лучше не переходить весь список снова, чтобы получить последний элемент. Кроме того, вам не нужен Occurred.T, который вам нужен сам, поэтому я развожу Element, чтобы получить номер.

Вот все в действии

let ll = [ 
    [1;2;5;1;2;3;4;5;5;4;5;5] 
    [2;1;2;1;1;2] 
    [-1;2;1;2;5;-1;5;5;2] 
    [7] 
] 

ll 
|> List.map aggregate 
|> List.map firstMostOccurred 
|> List.iter (printfn "%d") 

Этот код теперь будет печатать

5 
2 
2 
7 

Это еще некоторые грубые края, как

  1. Occurred.add бросает исключение, если вы пытаетесь добавить Произошло с различными элементами
  2. List.head бросает исключение для пустых списков

И в обоих случаях код не написанными для обработки этих случаев или убедившись, что исключение не будет повышаться.

Смежные вопросы