24

I want to find the Mode in an Array. I know that I have to do nested loops to check each value and see how often the element in the array appears. Then I have to count the number of times the second element appears. The code below doesn't work, can anyone help me please.

for (int i = 0; i < x.length; i ++)
{
    x[i]++;
    int high = 0;
    for (int i = 0; i < x.length; i++)
    {
        if (x[i] > high)
        high = x[i];
    }
}
Bali C
  • 28,049
  • 34
  • 109
  • 147
sarah
  • 257
  • 1
  • 2
  • 3

4 Answers4

36

Using nested loops is not a good way to solve this problem. It will have a run time of O(n^2) - much worse than the optimal O(n).

You can do it with LINQ by grouping identical values and then finding the group with the largest count:

int mode = x.GroupBy(v => v)
            .OrderByDescending(g => g.Count())
            .First()
            .Key;

This is both simpler and faster. But note that (unlike LINQ to SQL) LINQ to Objects currently doesn't optimize the OrderByDescending when only the first result is needed. It fully sorts the entire result set which is an O(n log n) operation.

You might want this O(n) algorithm instead. It first iterates once through the groups to find the maximum count, and then once more to find the first corresponding key for that count:

var groups = x.GroupBy(v => v);
int maxCount = groups.Max(g => g.Count());
int mode = groups.First(g => g.Count() == maxCount).Key;

You could also use the MaxBy extension from MoreLINQ method to further improve the solution so that it only requires iterating through all elements once.

Community
  • 1
  • 1
Mark Byers
  • 719,658
  • 164
  • 1,497
  • 1,412
  • 2
    Definitely the most readable way to do it, although it assumes that the array only has a single mode value (or that only one of the values is required if there are several). – LukeH Nov 24 '11 at 17:17
  • 2
    why would you want a beginner to learn LINQ at the very start....Let her understand arrays and all....Then she will be better off using LINQ – Pankaj Upadhyay Nov 24 '11 at 17:21
  • I agree with @PankajUpadhyay. Better to get to grips with loops, array access etc before taking on LINQ. – spender Nov 24 '11 at 17:24
  • 10
    There are two types of programmer. There are those that believe it is best to first learn a lower level language like C or assembler to understand how the computer works in detail, before being allowed to learn a higher level language. And there are those who believe it is best to start learning how to use a higher level language and get basic programs working then later to learn the details. I think one of these groups in particularly is extremely loud about their opinions, but I don't think that either approach is necessarily right or wrong. Both are reasonable ways to learn, in my opinion. – Mark Byers Nov 24 '11 at 17:49
  • @MarkByers , I am not saying the solution is wrong in any way or there should be an age for developer to learn this or that. Actually looking at the question and the code OP showed, i was unable to understand why you gone the highway. – Pankaj Upadhyay Nov 24 '11 at 17:53
  • 2
    @PankajUpadhyay: The OP wanted to use nested loops but this is an O(n^2) solution and is an inappropriate method for solving this problem in my opinion. This is why I showed an alternative solution. If you want to post a nested loop solution, please do. The more solutions the OP gets, the more likely she will get one she can use. – Mark Byers Nov 24 '11 at 18:12
  • @MarkByers +1 for easy LINQ solution. – J.S. Orris May 01 '16 at 20:18
  • 1
    I will check this when I have time (if I remember) but, in your O(n) algorithm, won't the `Count()` calls iterate through the `IGrouping`s to find the count? So actually your worst case is O(n^2)? – Adam Goodwin Oct 27 '16 at 02:05
8

A non LINQ solution:

int[] x = new int[] { 1, 2, 1, 2, 4, 3, 2 };

Dictionary<int, int> counts = new Dictionary<int, int>();
foreach( int a in x ) {
    if ( counts.ContainsKey(a) )
        counts[a] = counts[a]+1
    else
        counts[a] = 1
}

int result = int.MinValue;
int max = int.MinValue;
foreach (int key in counts.Keys) {
    if (counts[key] > max) {
        max = counts[key];
        result = key;
    }
}

Console.WriteLine("The mode is: " + result);
Community
  • 1
  • 1
Petar Ivanov
  • 84,604
  • 7
  • 74
  • 90
4

As a beginner, this might not make too much sense, but it's worth providing a LINQ based solution.

x
.GroupBy(i => i) //place all identical values into groups
.OrderByDescending(g => g.Count()) //order groups by the size of the group desc
.Select(g => g.Key) //key of the group is representative of items in the group
.First() //first in the list is the most frequent (modal) value
spender
  • 106,080
  • 28
  • 202
  • 324
  • The LINQ version makes far more sense, imo, than the imperative equivalent would. – LukeH Nov 24 '11 at 17:20
  • Yes, I think it's easier to write correct code using LINQ... but unless versed in LINQ, it can be confusing to the learner programmer. – spender Nov 24 '11 at 17:22
1

Say, x array has items as below:

int[] x = { 1, 2, 6, 2, 3, 8, 2, 2, 3, 4, 5, 6, 4, 4, 4, 5, 39, 4, 5 };

a. Getting highest value:

int high = x.OrderByDescending(n => n).First();

b. Getting modal:

int mode = x.GroupBy(i => i)  //Grouping same items
            .OrderByDescending(g => g.Count()) //now getting frequency of a value
            .Select(g => g.Key) //selecting key of the group
            .FirstOrDefault();   //Finally, taking the most frequent value
Elias Hossain
  • 4,130
  • 1
  • 17
  • 32
  • A minor nit, using `FirstOrDefault` will erroneously state that `0` is the mode of the array if there are no values. – user7116 Sep 10 '12 at 15:10