代码之家  ›  专栏  ›  技术社区  ›  Guido

用于检测集合中不同元素的高效算法

  •  15
  • Guido  · 技术社区  · 14 年前

    假设您有一组五个元素(a-E),其中包含测量属性的一些数值(每个元素的几个观察值,例如“心率”):

    A = {100, 110, 120, 130}
    B = {110, 100, 110, 120, 90}
    C = { 90, 110, 120, 100}
    D = {120, 100, 120, 110, 110, 120}
    E = {110, 120, 120, 110, 120}
    

    弗斯特 ,我必须检测平均水平是否存在显著差异。所以我只跑一条路 ANOVA 使用 Statistical package provided by Apache Commons Math

    第二 ,如果发现差异,我需要知道 unpaired t-tests

    C is different than B
    C is different than D
    

    但我需要一个通用算法来有效地确定,利用这些信息,哪些元素与其他元素不同(示例中的C,但可能不止一个)。

    撇开统计问题不谈,问题可以是(一般而言): 给定集合中每对元素的相等/不相等信息,如何确定与其他元素不同的元素

    这似乎是一个图论可以应用的问题。我正在使用 JAVA

    元素是人,测量值是完成任务所需的时间。我需要在某种欺诈检测系统中检测谁花费了太多或太少的时间来完成任务。

    4 回复  |  直到 11 年前
        1
  •  4
  •   Guido    14 年前

    以防有人对最终代码感兴趣,使用 Apache Commons Math 进行统计操作,以及 Trove 使用基元类型的集合。

    它寻找具有最高程度的元素(该想法基于@Pace和@Aniko的评论,谢谢)。

    import gnu.trove.iterator.TIntIntIterator;
    import gnu.trove.map.TIntIntMap;
    import gnu.trove.map.hash.TIntIntHashMap;
    import gnu.trove.procedure.TIntIntProcedure;
    import gnu.trove.set.TIntSet;
    import gnu.trove.set.hash.TIntHashSet;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import org.apache.commons.math.MathException;
    import org.apache.commons.math.stat.inference.OneWayAnova;
    import org.apache.commons.math.stat.inference.OneWayAnovaImpl;
    import org.apache.commons.math.stat.inference.TestUtils;
    
    
    public class TestMath {
        private static final double SIGNIFICANCE_LEVEL = 0.001; // 99.9%
    
        public static void main(String[] args) throws MathException {
            double[][] observations = {
               {150.0, 200.0, 180.0, 230.0, 220.0, 250.0, 230.0, 300.0, 190.0 },
               {200.0, 240.0, 220.0, 250.0, 210.0, 190.0, 240.0, 250.0, 190.0 },
               {100.0, 130.0, 150.0, 180.0, 140.0, 200.0, 110.0, 120.0, 150.0 },
               {200.0, 230.0, 150.0, 230.0, 240.0, 200.0, 210.0, 220.0, 210.0 },
               {200.0, 230.0, 150.0, 180.0, 140.0, 200.0, 110.0, 120.0, 150.0 }
            };
    
            final List<double[]> classes = new ArrayList<double[]>();
            for (int i=0; i<observations.length; i++) {
                classes.add(observations[i]);
            }
    
            OneWayAnova anova = new OneWayAnovaImpl();
    //      double fStatistic = anova.anovaFValue(classes); // F-value
    //      double pValue = anova.anovaPValue(classes);     // P-value
    
            boolean rejectNullHypothesis = anova.anovaTest(classes, SIGNIFICANCE_LEVEL);
            System.out.println("reject null hipothesis " + (100 - SIGNIFICANCE_LEVEL * 100) + "% = " + rejectNullHypothesis);
    
            // differences are found, so make t-tests
            if (rejectNullHypothesis) {
                TIntSet aux = new TIntHashSet();
                TIntIntMap fraud = new TIntIntHashMap();
    
                // i vs j unpaired t-tests - O(n^2)
                for (int i=0; i<observations.length; i++) {
                    for (int j=i+1; j<observations.length; j++) {
                        boolean different = TestUtils.tTest(observations[i], observations[j], SIGNIFICANCE_LEVEL);
                        if (different) {
                            if (!aux.add(i)) {
                                if (fraud.increment(i) == false) {
                                    fraud.put(i, 1);
                                }
                            }
                            if (!aux.add(j)) {
                                if (fraud.increment(j) == false) {
                                    fraud.put(j, 1);
                                }
                            }
                        }           
                    }
                }
    
                // TIntIntMap is sorted by value
                final int max = fraud.get(0);
                // Keep only those with a highest degree
                fraud.retainEntries(new TIntIntProcedure() {
                    @Override
                    public boolean execute(int a, int b) {
                        return b != max;
                    }
                });
    
                // If more than half of the elements are different
                // then they are not really different (?)
                if (fraud.size() > observations.length / 2) {
                    fraud.clear();
                }
    
                // output
                TIntIntIterator it = fraud.iterator();
                while (it.hasNext()) {
                    it.advance();
                    System.out.println("Element " + it.key() + " has significant differences");             
                }
            }
        }
    }
    
        2
  •  0
  •   Alex Feinman    14 年前

    你的编辑提供了很好的细节;谢谢

    基于这一点,我假设时间分布相当合理(正态分布,或者可能是伽马分布;这取决于你的时间接近于零的程度)的典型反应。拒绝此分布中的样本可能很简单,比如计算标准偏差并查看哪些样本与平均值的偏差超过n个标准差,或者复杂到获取排除异常值的子集,直到数据稳定到一个好的堆中为止(例如,平均值停止“大量”移动)。

    然后,根据这个新的统计数据,你需要检验一些假设。例如,我的怀疑是,对于作弊者来说,这一统计数据的标准偏差将高于那些速度始终高于其他人的人,但你需要数据来验证这一点。

    祝你好运!

        3
  •  0
  •   TheSteve0    14 年前

    您必须运行成对t检验(或任何您想要实现的成对检验),并在散列中增加计数,其中键是人,计数是不同的次数。

        4
  •  0
  •   Scott Smith    14 年前

    如果列表中的项目按数字顺序排序,则可以同时遍历两个列表,任何差异都可以轻松识别为插入或删除。例如

    List A    List B
      1         1       // Match, increment both pointers
      3         3       // Match, increment both pointers
      5         4       // '4' missing in list A. Increment B pointer only.
    
    List A    List B
      1         1       // Match, increment both pointers
      3         3       // Match, increment both pointers
      4         5       // '4' missing in list B (or added to A). Incr. A pointer only.