【问题标题】:How to calculate a unique "mode" from an array?如何从数组中计算唯一的“模式”?
【发布时间】:2019-04-17 16:07:08
【问题描述】:

我的任务是查找给定数组的模式(未指定长度)。模式定义为最唯一出现的数字。因此,例如,数组 [1.0, 2.0, 3.0, 2.0] 的众数为 2.0。但是,如果该值没有唯一编号,例如 [1.0, 2.0, 2.0, 3.0, 3.0],则程序在我的程序中返回“无模式”或“Double.NaN”。

我编写了适用于 3/4 测试用例的代码,但总是搞砸有两种模式相同的情况。

public double mode() {

    double modeOne = data[0];
    double modeTwo = 0;
    int count = 0;
    int countOne = 0;
    int countTwo = 0;

    if(data.length == 1) { // special case: if array length is 1 the mode will always just be that value
        modeOne = data[0];
        return modeOne;
    } // end if

    for(int i = 0; i < data.length; i++) { // pulling out first value
        double value = data[i];
        for(int n = 0; n < data.length; n++) { // comparing first value to all other values
            if (data[n] == value) {
                count ++; // adding onto a count of how many of the same number there are
            }
        }
        if(modeOne == value || modeTwo == value) { // move on if the modes already have that value
            continue;
        }
        if(count > countOne) { // setting the max count
            countTwo = countOne;
            countOne = count;
            modeTwo = modeOne;
            modeOne = value;
        }
        else if(count > countTwo) { // setting second highest count
            countTwo = count;
            modeTwo = value;
        }
    } // end for
    if(countOne == 1) { // if all the modes are just one
        return Double.NaN;
    }
    if(countOne == countTwo) { // if there are two of the same modes
        return Double.NaN;
    }
    else {
        return modeOne;
    }
} //end MODE

对于这个测试用例:

double[] data = {1,2,2,3,3,4};
Stat stat1 = new Stat(data);
System.out.println("stat1 mode = " + stat1.mode());

我期望“NaN”,但它返回 4。但是,它适用于以下情况:

double[] data = {-5.3, 2.5, 88.9, 0, 0.0, 28, 16.5, 88.9, 109.5, -90, 88.9};
Stat stat1 = new Stat(data);
System.out.println("stat1 mode = " + stat1.mode());

预期的输出是 88.9,程序确实输出正确。

【问题讨论】:

  • 最独特是否意味着最频繁
  • 是 - 最常见但也不应该在该数组中存在具有相同模式的另一个值。如果这是有道理的。所以 [1.0,1.0,2.0,2.0,3.0] 没有模式,因为 1.0 和 2.0 出现的次数相同。
  • 您不能简单地扫描数组并将每个遇到的数字的频率保存在一个列表中。然后按频率递减排序。如果此列表有超过 1 个元素并且前 2 个元素具有相同的频率,则返回 NaN。否则返回列表中的第一个元素。
  • 一定要喜欢学校项目,这些项目为您提供了一个复杂的多功能工具来学习并告诉您使用它的唯一方法是作为锤子:p
  • 转学怎么样?

标签: java loops


【解决方案1】:

这是一个使用 Streaming API 的方法。但是,我将模式定义为一组而不是单个数字。

import org.junit.Test;

import java.util.Arrays;
import java.util.Map;
import java.util.OptionalLong;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;

public class ModeTest {

    private <T extends Number> Set<T> modes(T... input) {
        return modes(Arrays.stream(input));
    }

    /**
     * Calculate the modes of a numeric stream.  The modes are the values that occurs most often. If no number in the
     * stream is repeated, then all the numbers in the stream are modes.
     *
     * @param input stream of numbers
     * @param <T>   number type
     * @return modes.
     */
    private <T extends Number> Set<T> modes(Stream<T> input) {

        // transform the input to a map containing the counted entries
        final Set<Map.Entry<T, Long>> countedEntries = input
            .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
            .entrySet();

        // Figure out the max value
        final OptionalLong max = countedEntries
            .parallelStream()
            .mapToLong(Map.Entry::getValue)
            .max();

        // Handle the case where the stream was empty
        if (max.isEmpty()) {
            return Set.of();
        }

        return countedEntries
            .parallelStream()
            .filter(e -> e.getValue() == max.getAsLong())
            .map(Map.Entry::getKey)
            .collect(Collectors.toSet());

    }

    @Test
    public void oneMode() {
        final Double[] input = new Double[]{1.0, 1.1, 1.2, 2.0, 2.0, 3.0};
        assertEquals(modes(input), Set.of(2.0));
    }

    @Test
    public void multipleModes() {
        final Stream<Double> input = Stream.of(1.0, 1.1, 1.2, 2.0, 2.0, 3.0, 3.0);
        assertEquals(modes(input), Set.of(2.0, 3.0));
    }

    @Test
    public void allSingles() {
        final Stream<Double> input = Stream.of(1.0, 1.1, 1.2, 2.0, 3.0);
        assertEquals(modes(input), Set.of(1.0, 1.1, 1.2, 2.0, 3.0));
    }

    @Test
    public void largeRandomSet() {
        Integer[] randoms = new Integer[204800];
        for (int i = randoms.length - 1; i >= 0; --i) {
            randoms[i] = ThreadLocalRandom.current().nextInt(200);
        }
        assertFalse(modes(randoms).isEmpty());
    }

    @Test
    public void emptyStream() {
        final Stream<Double> input = Stream.of();
        assertEquals(modes(input), Set.of());
    }
}

【讨论】:

    【解决方案2】:

    没有Collection 等...纯硬编程:)

    public double mode(double[] data)
    {
        if(data.length==1)
            return data[0];
        double temp;
        double [] fr = new double [data.length];  //store frequency
        int visited = -1;  
    
        for(int i = 0; i < data.length; i++)
        {           
            int count = 1;  
            for(int j = i+1; j < data.length; j++)
            {  
                if(data[i] == data[j])
                {  
                    count++;   
                    fr[j] = visited;  
                }  
            }  
            if(fr[i] != visited)  
                fr[i] = count;  
        }  
    
    
        for (int i = 0; i < fr.length; i++)   // sort array in decreasing order
        {
            for (int j = i + 1; j < fr.length; j++) 
            {
                if (fr[i] < fr[j]) 
                {
                    temp = data[i];
                    data[i] = data[j];
                    data[j] = temp;
    
                    temp = fr[i];
                    fr[i] = fr[j];
                    fr[j] = temp;
                }
            }
        }
    
        if(fr[0] == fr[1])
            return Double.NaN;
        else
            return data[0];
    
    }
    

    【讨论】:

    • 这很有帮助!!您编写返回语句的方式更加简洁明了。我没有想到存储频率的想法,但它有很大帮助。谢谢!!
    • 现在,你可以换学校了哈哈哈……让我告诉你,学校到处都是一样的……是你必须找出问题所在……这就是你成为程序员的原因。 . 快乐编码:)
    • 虽然很难,但它确实帮助我真正巩固了编程基础的基本概念......再次感谢! :)
    【解决方案3】:

    因为我有心情接受一个小挑战,所以我确实使用 Map 编写了自己的解决方案来计算各个值。

    然后您检索可用的最高计数并再次遍历映射以确定多个条目是否具有相同的最高计数,如果是,您将返回 NaN。

    public static double calculateMode(double[] numbers) {
        Map<Double, Integer> lookupMap = new TreeMap<>();
    
        for (double number : numbers) {
            if (lookupMap.get(number) != null) {
                lookupMap.put(number, lookupMap.get(number) + 1);
            } else {
                lookupMap.put(number, 1);
            }
        }
    
        int max = -1;
        double maxKey = Double.NaN;
        for (Entry<Double, Integer> entry : lookupMap.entrySet()) {
            if (entry.getValue() > max) {
                max = entry.getValue();
                maxKey = entry.getKey();
            }
        }
    
        int foundMax = 0;
    
        for (Entry<Double, Integer> entry : lookupMap.entrySet()) {
            if (entry.getValue() == max) {
                foundMax++;
            }
        }
    
        if (foundMax > 1) {
            return Double.NaN;
        }
    
        return maxKey;
    
    }
    

    方法调用:

    public static void main(String[] args) {
        double[] data = {1, 2, 2, 3, 3, 4};
        double[] data2 = {-5.3, 2.5, 88.9, 0, 0.0, 28, 16.5, 88.9, 109.5, -90, 88.9};
        System.out.println("Expected NaN - and was: " + calculateMode(data));
        System.out.println("Expected 88.90 - and was: " + calculateMode(data2));
    }
    

    输出:

    Expected NaN - and was: NaN
    Expected 88.90 - and was: 88.9
    

    【讨论】:

    • 她无法使用 Collections API。
    • 她确认了吗?我以为只有数组函数会受到影响...
    • 我喜欢这种工作方式!干净多了。没有集合有没有办法做到这一点?
    • 是的......我会做你所做的,直到她再次发表评论
    • @maio290 不幸的是,是的。 :(
    【解决方案4】:

    所以我也感到了挑战,并在不使用Collections 的情况下获得了解决方案。
    不是一个很好的解决方案,但它似乎有效:

    public class TestMode
    {
      private static class NumberFrequency
      {
        double number;
        int    frequency;
      }
    
      public static double calculateMode(double[] numbers)
      {
        // Maybe array empty
        if ((numbers == null) || (numbers.length == 0))
          return Double.NaN;
    
        // Initialize array with frequencies
        NumberFrequency[] array;
        int               size = 0;
        array = new NumberFrequency[numbers.length];
    
        // Loop over numbers determining frequencies
        for (double number : numbers)
        {
          // Maybe encountered before
          int index;
          for (index = 0; index < size; index++)
          {
            if (array[index].number == number)
              break;
          }
    
          // Update array
          NumberFrequency elm;
          if (index == size)
          {
            elm = new NumberFrequency();
            elm.number = number;
            elm.frequency = 0;
            array[index] = elm;
            size++;
          }
          else
            elm = array[index];
          elm.frequency += 1;
    
        } // for all numbers
    
        // Initialize element with highest frequency
        int index_highest;
        int highest_freq;
        int nr_occurs;
        index_highest = 0;
        highest_freq = array[0].frequency;
        nr_occurs = 1;
    
        // Search 'better' element
        int counter;
        for (counter = 1; counter < size; counter++)
        {
          if (array[counter].frequency > highest_freq)
          {
            index_highest = counter;
            highest_freq = array[counter].frequency;
            nr_occurs = 1;
          }
          else if (array[counter].frequency == highest_freq)
            nr_occurs++;
        }
    
        // Return result
        if (nr_occurs == 1)
          return array[index_highest].number;
        else
          return Double.NaN;
    
      } // calculateMode
    
      public static void main(String[] args)
      {
        double[] data = {1, 2, 2, 3, 3, 4};
        double[] data2 = {-5.3, 2.5, 88.9, 0, 0.0, 28, 16.5, 88.9, 109.5, -90, 88.9};
        System.out.println("Expected NaN - and was: " + calculateMode(data));
        System.out.println("Expected 88.90 - and was: " + calculateMode(data2));  }
    
    } // class TestMode
    

    【讨论】:

    • 嗯,它是有目的的,但它非常复杂......你可以看到我的答案,尽可能简短,在 NO API PONLY HARD PROGRAMMING 的边界
    • 哦,我什至没有考虑空数组选项。遇到的 before 选项也提高了我喜欢的效率。非常感谢!! :)
    【解决方案5】:

    再添加一个替代方案,因为我也感到挑战:

    一般的想法是生成一个频率数组,对于上面给出的例子

     [1.0, 2.0, 2.0, 3.0, 3.0]
     [1,    2,   2,   2,   2]  
    

    表示相同索引的元素在输入中的次数,然后在频率数组中找到最大值,最后检查所有具有相同频率的值是否相等。

    public static double mode(double [] data) {  
       if(data == null || data.length < 1){
           return Double.NaN;
       }      
       int [] freq = new int [data.length]; 
       for(int i = 0; i<data.length; i++){
            for(int j = 0; j<data.length; j++){
                if(data[i]==data[j]){
                    freq[i]++;
                }
            }
        }
        int max = 0;
        double mode = data[0];
        for(int i = 0; i<freq.length; i++){
            if(freq[i]>max){
                max = freq[i];
                mode = data[i];
            }
        }
        for(int i = 0; i<freq.length; i++){
            if(freq[i] == max){
                if(mode != data[i]){
                   return Double.NaN;
                }
            }
        }
        return mode;
    } 
    

    【讨论】:

      猜你喜欢
      • 2013-02-04
      • 1970-01-01
      • 1970-01-01
      • 2014-04-16
      • 1970-01-01
      • 2018-04-01
      • 1970-01-01
      • 2019-05-16
      • 2021-02-17
      相关资源
      最近更新 更多