54

我正在尝试计算由文本字段接收的输入填充的数组的总数、平均值和中位数。我已经设法计算出总数和平均值,但我无法让中位数起作用。我认为在执行此操作之前需要对数组进行排序,但我不确定如何执行此操作。是这个问题,还是我没有找到另一个问题?这是我的代码:

import java.applet.Applet;
import java.awt.Graphics;
import java.awt.*;
import java.awt.event.*;

public class whileloopq extends Applet implements ActionListener
{
    Label label;
    TextField input;
    int num;
    int index;
    int[] numArray = new int[20];
    int sum;
    int total;
    double avg;
    int median;



    public void init ()
    {
        label = new Label("Enter numbers");
        input = new TextField(5);
        add(label);
        add(input);
        input.addActionListener(this);
        index = 0;
    }

    public void actionPerformed (ActionEvent ev)
    {
        int num = Integer.parseInt(input.getText());
        numArray[index] = num;
        index++;
        if (index == 20)
        input.setEnabled(false);
            input.setText("");
        sum = 0;
        for (int i = 0; i < numArray.length; i++)
        {
            sum += numArray[i];
        }
        total = sum;
        avg = total / index;

        median = numArray[numArray.length/2];



        repaint();

    }



    public void paint (Graphics graf)
    {



        graf.drawString("Total   = " + Integer.toString(total), 25, 85);
        graf.drawString("Average = " + Double.toString(avg), 25, 100);
        graf.drawString("Median = " + Integer.toString(median), 25, 115);



    }
}
4

16 回答 16

90

Java 中的 Arrays 类有一个静态排序函数,您可以使用Arrays.sort(numArray).

Arrays.sort(numArray);
double median;
if (numArray.length % 2 == 0)
    median = ((double)numArray[numArray.length/2] + (double)numArray[numArray.length/2 - 1])/2;
else
    median = (double) numArray[numArray.length/2];
于 2012-08-14T15:37:39.137 回答
40

对数组进行排序是不必要且低效的。有一个 QuickSort ( QuickSelect ) 算法的变体,它的平均运行时间为 O(n);如果你先排序,你会降到 O(n log n)。它实际上是在列表中找到第 n 个最小的项目;对于中位数,您只需使用 n = 列表长度的一半。我们称它为 quickNth (list, n)。

这个概念是要找到第 n 个最小的,选择一个“枢轴”值。(具体如何选择并不重要;如果您知道数据将是完全随机的,您可以选择列表中的第一项。)

将原始列表拆分为三个较小的列表:

  • 一个值小于枢轴的值。
  • 一个值等于枢轴。
  • 一个值大于枢轴的值。

然后你有三种情况:

  1. “较小的”列表有 >= n 项。在这种情况下,您知道第 n 个最小的在该列表中。返回 quickNth(smaller, n)。
  2. 较小的列表有 < n 项,但较小且相等的列表的长度总和 >= n 项。在这种情况下,第 n 个等于 "equal" 列表中的任何项目;你完成了。
  3. n 大于较小且相等的列表的长度之和。在这种情况下,您基本上可以跳过这两个,并相应地调整 n。返回 quickNth(更大,n - 长度(更小) - 长度(等于))。

完毕。

如果您不确定数据是否完全随机,则需要更加复杂地选择枢轴。取列表中第一个值、列表中最后一个值以及两者中间的值的中值效果很好。

如果您对枢轴的选择非常不走运,并且总是选择最小值或最大值作为枢轴,则这需要 O(n^2) 时间;那很糟。但是,如果你用一个像样的算法来选择你的支点,这也是不太可能的

示例代码:

import java.util.*;

public class Utility {
   /****************
   * @param coll an ArrayList of Comparable objects
   * @return the median of coll
   *****************/
   
   public static <T extends Number> double median(ArrayList<T> coll, Comparator<T> comp) {
      double result;
      int n = coll.size()/2;
      
      if (coll.size() % 2 == 0)  // even number of items; find the middle two and average them
         result = (nth(coll, n-1, comp).doubleValue() + nth(coll, n, comp).doubleValue()) / 2.0;
      else                      // odd number of items; return the one in the middle
         result = nth(coll, n, comp).doubleValue();
         
      return result;
   } // median(coll)
   
   

   /*****************
   * @param coll a collection of Comparable objects
   * @param n  the position of the desired object, using the ordering defined on the list elements
   * @return the nth smallest object
   *******************/
   
   public static <T> T nth(ArrayList<T> coll, int n, Comparator<T> comp) {
      T result, pivot;
      ArrayList<T> underPivot = new ArrayList<>(), overPivot = new ArrayList<>(), equalPivot = new ArrayList<>();
      
      // choosing a pivot is a whole topic in itself.
      // this implementation uses the simple strategy of grabbing something from the middle of the ArrayList.
      
      pivot = coll.get(n/2);
      
      // split coll into 3 lists based on comparison with the pivot
      
      for (T obj : coll) {
         int order = comp.compare(obj, pivot);
         
         if (order < 0)        // obj < pivot
            underPivot.add(obj);
         else if (order > 0)   // obj > pivot
            overPivot.add(obj);
         else                  // obj = pivot
            equalPivot.add(obj);
      } // for each obj in coll
      
      // recurse on the appropriate list
      
      if (n < underPivot.size())
         result = nth(underPivot, n, comp);
      else if (n < underPivot.size() + equalPivot.size()) // equal to pivot; just return it
         result = pivot;
      else  // everything in underPivot and equalPivot is too small.  Adjust n accordingly in the recursion.
         result = nth(overPivot, n - underPivot.size() - equalPivot.size(), comp);
         
      return result;
   } // nth(coll, n)
   
   
   
   public static void main (String[] args) {
      Comparator<Integer> comp = Comparator.naturalOrder();
      Random rnd = new Random();
      
      for (int size = 1; size <= 10; size++) {
         ArrayList<Integer> coll = new ArrayList<>(size);
         for (int i = 0; i < size; i++)
            coll.add(rnd.nextInt(100));
      
         System.out.println("Median of " + coll.toString() + " is " + median(coll, comp));
      } // for a range of possible input sizes
   } // main(args)
} // Utility
于 2015-03-03T00:56:33.420 回答
12

如果你想在这里使用任何外部库是Apache commons 数学库,你可以计算Median
有关更多方法和使用,请查看API 文档

import org.apache.commons.math3.*;
.....
......
........
//calculate median
public double getMedian(double[] values){
 Median median = new Median();
 double medianValue = median.evaluate(values);
 return medianValue;
}
.......

更新

在程序中计算

通常,使用此处给出的以下两个公式计算中位数

如果 n 是奇数,则中位数 (M) = 第 ((n + 1)/2) 项的值。
如果 n 是偶数,则中位数 (M) = [((n)/2)th item term + ((n)/2 + 1)th item term ]/2 的值

在您的程序中numArray,首先您需要使用Arrays#sort对数组进行排序

Arrays.sort(numArray);
int middle = numArray.length/2;
int medianValue = 0; //declare variable 
if (numArray.length%2 == 1) 
    medianValue = numArray[middle];
else
   medianValue = (numArray[middle-1] + numArray[middle]) / 2;
于 2013-11-08T06:36:55.310 回答
5
Arrays.sort(numArray);
return (numArray[size/2] + numArray[(size-1)/2]) / 2;
于 2017-11-11T21:02:18.327 回答
4
Arrays.sort(numArray);
int middle = ((numArray.length) / 2);
if(numArray.length % 2 == 0){
 int medianA = numArray[middle];
 int medianB = numArray[middle-1];
 median = (medianA + medianB) / 2;
} else{
 median = numArray[middle + 1];
}

编辑:我最初medianB设置为middle+1偶数长度数组,这是错误的,因为数组从 0 开始计数。我已经更新它以使用middle-1正确的,并且应该适用于偶数长度的数组。

于 2012-08-14T15:38:13.567 回答
4

您可以在https://www.youtube.com/watch?time_continue=23&v=VmogG01IjYc找到很好的解释

它使用 2 个堆的想法,即一个最大堆和平均堆。

class Heap {
private Queue<Integer> low = new PriorityQueue<>(Comparator.reverseOrder());
private Queue<Integer> high = new PriorityQueue<>();

public void add(int number) {
    Queue<Integer> target = low.size() <= high.size() ? low : high;
    target.add(number);
    balance();
}

private void balance() {
    while(!low.isEmpty() && !high.isEmpty() && low.peek() > high.peek()) {
        Integer lowHead= low.poll();
        Integer highHead = high.poll();
        low.add(highHead);
        high.add(lowHead);
    }
}

public double median() {
    if(low.isEmpty() && high.isEmpty()) {
        throw new IllegalStateException("Heap is empty");
    } else {
        return low.size() == high.size() ? (low.peek() + high.peek()) / 2.0 : low.peek();
    }
}

}

于 2018-05-28T16:30:28.147 回答
2

尝试先对数组进行排序。然后排序后,如果数组有偶数个元素,中间两个的平均值是中位数,如果它有奇数个,中间元素是中位数。

于 2012-08-14T15:31:54.953 回答
2

使用Arrays.sort然后取中间元素(如果n数组中的元素数量是奇数)或取两个中间元素的平均值(如果n是偶数)。

  public static long median(long[] l)
  {
    Arrays.sort(l);
    int middle = l.length / 2;
    if (l.length % 2 == 0)
    {
      long left = l[middle - 1];
      long right = l[middle];
      return (left + right) / 2;
    }
    else
    {
      return l[middle];
    }
  }

这里有些例子:

  @Test
  public void evenTest()
  {
    long[] l = {
        5, 6, 1, 3, 2
    };
    Assert.assertEquals((3 + 4) / 2, median(l));
  }

  @Test
  public oddTest()
  {
    long[] l = {
        5, 1, 3, 2, 4
    };
    Assert.assertEquals(3, median(l));
  }

如果您的输入是 a Collection,您可以使用Google Guava执行以下操作:

public static long median(Collection<Long> numbers)
{
  return median(Longs.toArray(numbers)); // requires import com.google.common.primitives.Longs;
}
于 2013-05-19T16:34:59.290 回答
2

我正在研究相同的统计问题。你认为的方法很好,它会奏效。(排序的答案已经给出)

但是,如果您对算法性能感兴趣,我认为有几种算法比仅对数组进行排序具有更好的性能,@bruce-feist 的回答指出了一个(QuickSelect)并且得到了很好的解释。

【Java实现:https ://discuss.leetcode.com/topic/14611/java-quick-select 】

但是这个算法有一个变体,叫做中位数的中位数,你可以在这个链接上找到一个很好的解释:http: //austinrochford.com/posts/2013-10-28-median-of-medians.html

Java 实现: - https://stackoverflow.com/a/27719796/957979

于 2016-11-19T22:03:03.817 回答
1

我昨天遇到了类似的问题。我用 Java 泛型编写了一个方法来计算每个数字集合的中值;您可以将我的方法应用于 Doubles、Integers、Floats 的集合并返回一个 double。请考虑我的方法会创建另一个集合,以免更改原始集合。我还提供了一个测试,玩得开心。;-)

public static <T extends Number & Comparable<T>> double median(Collection<T> numbers){
    if(numbers.isEmpty()){
        throw new IllegalArgumentException("Cannot compute median on empty collection of numbers");
    }
    List<T> numbersList = new ArrayList<>(numbers);
    Collections.sort(numbersList);
    int middle = numbersList.size()/2;
    if(numbersList.size() % 2 == 0){
        return 0.5 * (numbersList.get(middle).doubleValue() + numbersList.get(middle-1).doubleValue());
    } else {
        return numbersList.get(middle).doubleValue();
    }

}

JUnit 测试代码片段:

/**
 * Test of median method, of class Utils.
 */
@Test
public void testMedian() {
    System.out.println("median");
    Double expResult = 3.0;
    Double result = Utils.median(Arrays.asList(3.0,2.0,1.0,9.0,13.0));
    assertEquals(expResult, result);
    expResult = 3.5;
    result = Utils.median(Arrays.asList(3.0,2.0,1.0,9.0,4.0,13.0));
    assertEquals(expResult, result);
}

使用示例(考虑类名是 Utils):

List<Integer> intValues = ... //omitted init
Set<Float> floatValues = ... //omitted init
.....
double intListMedian = Utils.median(intValues);
double floatSetMedian = Utils.median(floatValues);

注意:我的方法适用于集合,您可以将数字数组转换为此处指出的数字列表

于 2016-05-28T11:35:56.867 回答
1

当列表仅包含一个元素(list.size == 1)时,没有人注意。您的所有答案都会因索引超出范围异常而崩溃,因为整数除法返回零(1 / 2 = 0)。正确答案(在 Kotlin 中):

MEDIAN("MEDIAN") {

        override fun calculate(values: List<BigDecimal>): BigDecimal? {
            if (values.size == 1) {
                return values.first()
            }
            if (values.size > 1) {
                val valuesSorted = values.sorted()
                val mid = valuesSorted.size / 2
                return if (valuesSorted.size % 2 != 0) {
                    valuesSorted[mid]
                } else {
                    AVERAGE.calculate(listOf(valuesSorted[mid - 1], valuesSorted[mid]))
                }
            }
            return null
        }
    },
于 2018-11-12T13:50:02.253 回答
1

正如@Bruce-Feist 提到的,对于大量元素,如果您关心性能,我会避免任何涉及排序的解决方案。与其他答案中建议的方法不同的方法是 Hoare 的算法,用于查找 n 项的第 k 个最小元素。该算法在 O(n) 中运行。

public int findKthSmallest(int[] array, int k)
{
    if (array.length < 10)
    {
        Arrays.sort(array);
        return array[k];
    }
    int start = 0;
    int end = array.length - 1;
    int x, temp;
    int i, j;
    while (start < end)
    {
        x = array[k];
        i = start;
        j = end;
        do
        {
            while (array[i] < x)
                i++;
            while (x < array[j])
                j--;
            if (i <= j)
            {
                temp = array[i];
                array[i] = array[j];
                array[j] = temp;
                i++;
                j--;
            }
        } while (i <= j);
        if (j < k)
            start = i;
        if (k < i)
            end = j;
    }
    return array[k];
}

并找到中位数:

public int median(int[] array)
{
    int length = array.length;
    if ((length & 1) == 0) // even
        return (findKthSmallest(array, array.length / 2) + findKthSmallest(array, array.length / 2 + 1)) / 2;
    else // odd
        return findKthSmallest(array, array.length / 2);
}
于 2019-06-03T07:21:37.783 回答
1
public static int median(int[] arr) {
int median = 0;
java.util.Arrays.sort(arr);
        
for (int i=0;i<arr.length;i++) {
            
    if (arr.length % 2 == 1) {
        median = Math.round(arr[arr.length/2]);
    } else {
        median = (arr[(arr.length/2)] + arr[(arr.length/2)-1])/2;
    }
}
return median;

}

于 2021-01-20T15:49:32.860 回答
0

查看 Arrays.sort 方法:

http://docs.oracle.com/javase/6/docs/api/java/util/Arrays.html

您还应该真正将查找中位数抽象到它自己的方法中,然后将值返回给调用方法。这将使测试您的代码更加容易。

于 2012-08-14T15:32:36.200 回答
0
public int[] data={31, 29, 47, 48, 23, 30, 21
        , 40, 23, 39, 47, 47, 42, 44, 23, 26, 44, 32, 20, 40};

public double median()
    {
        Arrays.sort(this.data);
        double result=0;
        int size=this.data.length;


        if(size%2==1)
        {
            result=data[((size-1)/2)+1];
            System.out.println(" uneven size : "+result);
        }
        else
        { 
            int middle_pair_first_index =(size-1)/2;
            result=(data[middle_pair_first_index+1]+data[middle_pair_first_index])/2;
            System.out.println(" Even size : "+result);
        }

        return result;
    }
于 2016-01-25T16:33:21.603 回答
0
package arrays;

public class Arraymidleelement {
    
    
    static public double middleArrayElement(int []  arr)
    {
     double  mid;
        if(arr.length%2==0)
        {
            mid=((double)arr[arr.length/2]+(double)arr[arr.length/2-1])/2;
            return mid;
        }
    
        
        return arr[arr.length/2];
        
        
        
    }
    public static void main(String[] args) {
        int arr[]= {1,2,3,4,5,6};
        
        System.out.println( middleArrayElement(arr));
        
    }
    

}
于 2021-09-17T16:49:37.920 回答