2

I've got to find the mode of an array. I am a bit embarrassed to admit that I've been stuck on this for a day. I think I've overthought it a bit - my method just gets longer and longer. The real issue that I keep running into is that when there isn't one mode (two numbers appear with the same frequency) I need to return Double.NaN.

Here's what I've tried:

private double[] data = {1, 1, 2, 2, 2, 3, 4, 5, 5, 5, 5, 5, 6, 7, 7, 8, 8, 8, 8, 8, 9, 9, 9};

if(data.length != 0){

        double maxValue = -1;
        int maxCount = 0;
        for(int i = 0; i < data.length; i++) {
            int count = 0;
            for(int j = 0; j < data.length; j++) {
                if(data[j] == data[i]) {
                    count++;
                }
            }

            if(count > maxCount) {
                maxValue = (int) data[i];
                maxCount = count;
            }
        }
        return maxValue;

    }else{

        return Double.NaN;

    }

This actually returns the mode, but it can't deal with two modes. Here's my most recent attempt, but it's only half complete:

private double[] data = {1, 1, 2, 2, 2, 3, 4, 5, 5, 5, 5, 5, 6, 7, 7, 8, 8, 8, 8, 8, 9, 9, 9};

public void mode(){

    int[] frequency = new int[data.length];

    double[] vals = new double[data.length];

    for(int i = 0; i < data.length; i++){
        frequency[i] = occursNumberOfTimes(data[i]);
    }

    boolean uniform = false;

    for(int g = 0; g < frequency.length && !uniform; g++){
        if(frequency[0] != frequency[g]){
            uniform = false;
        }

    int[] arr = new int[frequency.length-1];

    for(int j = 1; j < frequency.length; j++){

        if(frequency[j] > frequency[j-1]){

            int mod = 0;

            for(int k = 0; k < arr.length; k++){
                if(k == j){
                    mod += 1;
                    arr[k] = frequency[k + mod];
                }else{
                    arr[k] = frequency[k + mod];    
                }
            }
        }
    }
    frequency = arr;
    }
}

private int occursNumberOfTimes(double value){

    int count = 0;

    for(int i = 0; i < data.length; i++){
        if(data[i] == value){
            count++;
        }
    }
    return count;
}

I sorta got lost in the second try, I just can't sort out how to deal with multiple modes. I've written out my thoughts, but I just don't know how. I can't use anything from the Arrays class, which is why I'm lost.

9
  • "Can't use anything from the Arrays class" I assume this also includes anything from Collection includingSet and Map? Commented Nov 12, 2018 at 23:11
  • Where is data defined? What does it contain? May we see? Commented Nov 12, 2018 at 23:12
  • Assuming the answer to the above is "yes", then I think the best way to proceed is to break down the problem into smaller chunks, and implements something on its own like Map (you can look at that class and steal ideas) and then once you have your own version working the rest should be relatively easy. Commented Nov 12, 2018 at 23:12
  • Without using other collections, the easiest way might be to sort the array and then count the longest consecutive sequence of a single int value. Commented Nov 12, 2018 at 23:20
  • Just take your working implementation and modify it to track the top two most common elements. At the end of the loop, check to see if both those counts are the same -- and if they are return a NaN. Commented Nov 12, 2018 at 23:37

3 Answers 3

2

Does it have to be efficient? If not:

double maxValue = -1.0d;
int maxCount = 0;
for (int i = 0; i < data.length; ++i) {
    double currentValue = data[i];
    int currentCount = 1;
    for (int j = i + 1; j < data.length; ++j) {
        if (Math.abs(data[j] - currentValue) < epsilon) {
            ++currentCount;
        } 
    }
    if (currentCount > maxCount) {
        maxCount = currentCount;
        maxValue = currentValue;
    } else if (currentCount == maxCount) {
        maxValue = Double.NaN;
    }
}
System.out.println("mode: " + maxValue);
Sign up to request clarification or add additional context in comments.

3 Comments

Wow! I just sat for a few hours writing my own solution, but this is way better. Thank you! I think I know what's going on by looking at it, but I don't think I could have ever come up with this. I was just thinking about it so differently - sort of overcomplicating it.
Hello Can I ask what the line if (Math.abs(data[j] - currentValue) < epsilon) { is doing?
Thanks Samuel. Fixed it: float point comparisons use a tolerance, or epsilon.
0

You could track the two most common elements as was suggested in the comments, but another approach is to keep a boolean flag that indicates if the current most common element is unique. Then:

  1. For each array element e, obtain its count as you're currently doing.
  2. If that count is greater than the current max, set the the most common element (seen so far) to e and set the "unique" flag to true.
  3. Otherwise, if that count is equal to the current max, set the "unique" flag to false.

At the end, just return the mode if "unique" is true, otherwise return NaN.

1 Comment

Okay. I just wrote a super long solution that does work but doesn't use that. I really don't think my solution is good, but I'm happy that it at least works. I think I'll try this approach soon just to understand it. Thanks!
0

Here's my long, dumb solution. It works! It's a very roundabout way of getting the mode, but I'm really happy it works. I used the advice I got from some comments and looked at it differently. It was a frustrating few hours, but here it is:

public double mode2(){



    if(data.length != 0){

        int[] counts = new int[data.length];
        double[] vals = new double[data.length];

        for(int l = 0; l < data.length; l++){

            counts[l] = 1;

        }

        for(int i = 0; i < data.length; i++){

            for(int j = 0; j < data.length; j++){

                if((data[i] == data[j]) && (i != j)){

                    vals[i] = data[i];
                    counts[i] += 1;

                }

            }

        }

        for(int i = 0; i < data.length; i++){

            for(int j = 0; j < data.length; j++){

                if((vals[i] == vals[j]) && (i != j)){

                    vals[i] = 0;
                    counts[i] = 0;

                }

            }
        }

        int counter = 0;

        for(int k = 0; k < data.length; k++){

            if(counts[k] != 0){

                counts[counter] = counts[k];
                vals[counter] = vals[k];
                counter++;

            }

        }


        int[] compactCounts = new int[counter];
        double[] compactVals = new double[counter];

        for(int k = 0; k < counter; k++){

            if(counts[k] != 0){

                compactCounts[k] = counts[k];
                compactVals[k] = vals[k];

            }else{

                break;

            }

        }

        for(int g = 1; g < compactVals.length; g++){

            if(compactCounts[g] > compactCounts[g-1]){

                compactCounts[g-1] = 0;
                compactVals[g-1] = 0;

            }

        }

        for(int g = 0; g < compactVals.length-1; g++){

            if(compactCounts[g] > compactCounts[g+1]){

                compactCounts[g+1] = 0;
                compactVals[g+1] = 0;

            }

        }

        int counterTwo = 0;

        for(int k = 0; k < compactCounts.length; k++){

            if(compactCounts[k] != 0){

                compactCounts[counterTwo] = compactCounts[k];
                compactVals[counterTwo] = vals[k];
                counterTwo++;

            }

        }



        int[] compactCountsTwo = new int[counterTwo];
        double[] compactValsTwo = new double[counterTwo];

        for(int k = 0; k < counterTwo; k++){

            if(counts[k] != 0){

                compactCountsTwo[k] = compactCounts[k];
                compactValsTwo[k] = compactVals[k];

            }else{

                break;

            }
        }

        //now populated compactTwos

        //We're now setting some lesser values to 0

        for(int g = 1; g < compactValsTwo.length; g++){

            if(compactCountsTwo[g] > compactCountsTwo[g-1]){

                compactCountsTwo[g-1] = 0;
                compactValsTwo[g-1] = 0;

            }

        }


        //now setting other lesser values to 0


        for(int g = 0; g < compactValsTwo.length-1; g++){
            if(compactCountsTwo[g] > compactCountsTwo[g+1]){
                compactCountsTwo[g+1] = 0;
                compactValsTwo[g+1] = 0;

            }

        }


        //calling methods to shorten our arrays by dropping indexes populated by zeroes

        compactValsTwo = doubleTruncator(compactValsTwo);
        compactCountsTwo = intTruncator(compactCountsTwo);


        //now setting some lesser values to 0

        for(int g = 1; g < compactValsTwo.length; g++){

            if(compactCountsTwo[g] > compactCountsTwo[g-1]){

                compactCountsTwo[g-1] = 0;
                compactValsTwo[g-1] = 0;

            }

        }


        //now setting other lesser values to 0


        for(int g = 0; g < compactValsTwo.length-1; g++){
            if(compactCountsTwo[g] > compactCountsTwo[g+1]){
                compactCountsTwo[g+1] = 0;
                compactValsTwo[g+1] = 0;

            }

        }


        //calling methods to shorten our arrays by dropping indexes populated by zeroes

        compactValsTwo = doubleTruncator(compactValsTwo);
        compactCountsTwo = intTruncator(compactCountsTwo);


        if(compactValsTwo.length > 1){

            return Double.NaN;

        }else{

            return compactValsTwo[0];

        }

        }else{

            System.out.println("ISSUE");
            return Double.NaN;          
    }   
}


public double[] doubleTruncator(double[] a){

    int counter = 0;

    for(int k = 0; k < a.length; k++){

        if(a[k] != 0){

            a[counter] = a[k];
            counter++;

        }

    }

    double[] b = new double[counter];

    for(int i= 0; i < counter; i++){

        if(a[i] != 0){

            b[i] = a[i];

        }else{

            break;

        }

    }
    return b;

}


public int[] intTruncator(int[] a){

    int counter = 0;

    for(int k = 0; k < a.length; k++){

        if(a[k] != 0){

            a[counter] = a[k];
            counter++;

        }

    }

    int[] b = new int[counter];

    for(int i= 0; i < counter; i++){

        if(a[i] != 0){

            b[i] = a[i];

        }else{

            break;

        }

    }
    return b;

}

Big thanks to everybody who helped. I know it's not great (certainly not as good as the answer from @Perdi Estaquel), but I'm happy that I managed to do it.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.