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

时间:2010-02-24 13:49:04

标签: java algorithm collections statistics anova

想象一下,您有一组五个元素(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,比较每对元素(A与B,A与C .... D与E),以了解元素是否与另一元素不同。因此,此时我得到的元素列表信息与其他元素存在显着差异,例如:

C is different than B
C is different than D

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

不考虑统计问题,问题可能是(一般而言):“鉴于集合中每对元素的相等/不等的信息,你如何确定元素/与其他人有什么不同?“

似乎是一个可以应用图论的问题。我正在使用 Java 语言进行实现,如果有用的话。

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

4 个答案:

答案 0 :(得分:4)

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

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

我认为最终的算法是O(n ^ 2),欢迎提出建议。它应该适用于任何涉及一个cualitative和一个cuantitative变量的问题,假设观察的正常性。

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");             
            }
        }
    }
}

答案 1 :(得分:0)

您的编辑提供了很好的细节;感谢,

基于此,我会假设一个相当良好的时间分布(正常,或可能是伽马;取决于你的时间得到的接近零)对于典型的反应。拒绝这个分布中的样本可能就像计算标准偏差一样简单,看看哪些样本比平均值更多n stdevs,或者看起来很复杂,因为需要排除异常值的子集,直到你的数据陷入一个很好的堆(例如均值)停止'很多')。

现在,如果你认为一个试验猴子的人会和另一个人一起生活,你会有额外的皱纹。因此,你试图区分一个恰好快速(或慢速)的人和一个“作弊”的人。你可以做一些像计算每个分数的stdev等级的东西(我忘记了这个的正确名称:如果一个值是高于平均值的两个stdevs,得分是'2'),并将其用作你的统计数据。

然后,根据这个新的统计数据,您需要测试一些假设。例如,我怀疑这个统计数据的统计数据对于骗子来说会比那些比其他人速度更快的人更高 - 但是你需要数据来验证这一点。

祝你好运!

答案 2 :(得分:0)

你必须运行配对t检验(或者你想要实现的任何成对测试),并在哈希中计数增量,其中密钥是Person,而count是不同的数量。

我猜你也可以有一个包含人物对象的arrayList。人物对象可以存储他们的ID以及他们不同的时间计数。实现可比较,然后您可以按计数对arraylist进行排序。

答案 3 :(得分:0)

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

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.