Good Morning everyone,
I must do an exercise of Programming, but i'm stuck!
Well, the exercise requires a function that given a list not empty of integers, return the first number with maximum number of occurrences.
For example:
Important : the exercise must be in functional programming
My idea is:
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;;
In this function i'm stuck:
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;;
Thanks in advance, i'm newbie in programming and in stackoverflow Sorry for my english
one solution : calculate first a list of couples (number , occurences). hint : use List.assoc.
Then, loop over that list of couple to find the max occurrence and then return the number.
You need to process you input list while maintaining a state, that stores the number of occurrences of each number. Basically, the state can be a map
, where keys are in the domain of list elements, and values are in domain of natural numbers. If you will use Map
the algorithm would be of O(NlogN)
complexity. You can also use associative list (ie, a list of type ('key,'value) list
) to implement map. This will lead to quadratic complexity. Another approach is to use hash table or an array of the length equal to the size of the input domain. Both will give you a linear complexity.
After you collected the statistics, (ie, a mapping from element to the number of its occurrences) you need to go through the set of winners, and choose the one, that was first on the list.
In OCaml the solution would look like this:
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)
The algorithm is the following:
For example, if we take sample [1;2;5;1;2;3;4;5;5;4;5;5]
,
stats = {1 => 2; 2 => 2; 3 => 1; 4 => 2; 5 => 5}
mods = {1 => [3]; 2 => [1;2]; 5 => [5]}
You need to install core
library to play with it. Use coretop
to play with this function in the toplevel. Or corebuild
to compile it, like this:
corebuild test.byte --
if the source code is stored in test.ml
One suggestion:
your algorithm could be simplified if you sort the list before. This has O(N log(N)) complexity. Then measure the longest sequence of identical numbers.
This is a good strategy because you delegate the hard part of the work to a well known algorithm.
It is probably not the most beautiful code, but here is with what i came up (F#). At first i transform every element to an intermediate format. This format contains the element itself, the position of it occurrence and the amount it occurred.
type T<'a> = {
Element: 'a
Position: int
Occurred: int
}
The idea is that those Records can be added. So you can first transform every element, and then add them together. So a list like
[1;3]
will be first transformed to
[{Element=1;Position=0;Occurred=1}; {Element=3;Position=1;Occurred=1}]
By adding two together you only can add those with the same "Element". The Position with the lower number from both is taken, and Occurred is just added together. So if you for example have
{Element=3;Position=1;Occurred=2} {Element=3;Position=3;Occurred=2}
the result will be
{Element=3;Position=1;Occurred=4}
The idea that i had in mind was a Monoid. But in a real Monoid you had to come up that you also could add different Elements together. By trying some stuff out i feel that the restriction of just adding the same Element where way more easier. I created a small Module with the type. Including some helper functions for creating, adding and comparing.
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
With this setup i now wrote two additional function. One aggregate
function that first turns every element into a Occurred.T
, group them by x.Element
(the result is a list of list). And then it uses List.reduce
on the inner list to add the Occurred with the same Element together. The result is a List that Contains only a single Occurred.T
for every Element with the first Position and the amount of Occurred items.
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)
You could use that aggregate function to now implement different aggregation logic. In your case you only wanted the one with the highest Occurrences and the lowest position. I wrote another function that did that.
let firstMostOccurred =
List.sortWith (fun x y -> (Occurred.compareOccurredPosition x y) * -1) >> List.head >> (fun x -> x.Element)
One note. Occurred.compareOccurredPosition
is written that it sorts everything in ascending order. I think people expecting it in this order to go to the smallest to the biggest element by default. So by default the first element would be the element with the lowest occurrence and the biggest Position. By multiplying the result of it with -1
you turn that function into a descending sorting function. The reason why i did that is that i could use List.head
. I also could use List.last
to get the last element, but i felt that it would be better not to go through the whole list again just to get the last element. On top of it, you didn't wanted an Occurred.T
you wanted the element itself, so i unwrap the Element
to get the number.
Here is everything in action
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")
This code will now print
5
2
2
7
It has still some rough edges like
Occurred.add
throws an exception if you try to add Occurred with different Elements List.head
throws an exception for empty lists And in both cases no code is written to handle those cases or making sure an exception will not raise.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.