检测阵列中的所有突然变化

时间:2016-09-06 05:10:38

标签: java arrays algorithm

如何在阵列中发现突然变化?例如,如果您有以下数组:

1,3,8,14,58,62,69
In this case, there is a jump from 14 to 58

OR

79,77,68,61,9,3,1
In this case, there is a drop from 61 to 9

在这两个例子中,都有小而大的跳跃。例如,在第二种情况下,从77到68有一个小的下降。但是,如果发现更大的跳跃/下降,则必须忽略它。我有以下算法,但我不确定这是否涵盖所有可能的情况:

ALGO
Iterate over array
Diff (i+1)-i
store first difference in a variable
if next diff is bigger than previous then overwrite 

对于以下示例,此算法不适用于以下情况:

1, 2, 4, 6, 34, 38, 41, 67, 69, 71

此阵列中有两个跳转。所以它应该像

那样安排
[1, 2, 4, 6], [34, 38, 41], [67, 69, 71]

3 个答案:

答案 0 :(得分:3)

最后,这是纯粹的统计数据。你有一个数据集;你正在寻找某种形式的outliers。从这个意义上说,你需要检测" 突然改变"不是很精确。

我想你应该回到这里;并深入研究你的问题背后的数学 - 提出明确的语义和#34;您实际问题的清晰定义(例如基于平均值,偏差等)。我上面给出的维基百科链接应该是该部分的一个很好的起点。

从那时起,为了实现Java实现,您可能会开始查看here

答案 1 :(得分:1)

我会考虑使用Moving Average,这涉及查看最后X个值的平均值。根据值的变化(Y1 - Y2)执行此操作。任何与平均值的大偏差都可视为一个重大转变。

但是,考虑到移动平均线的数据量有多小,可能会产生糟糕的结果。如此小的样本量,最好采用数组中所有值的平均值来代替:

double [] nums = new double[] {79,77,68,61,9,3,1};
double [] deltas = new double[nums.length-1];
double advDelta = 0;

for(int i=0;i<nums.length-1;i++) {
    deltas[i] = nums[i+1]-nums[i];
    advDelta += deltas[i] / deltas.length;
}

// search for deltas > average
for(int i=0;i<deltas.length;i++) {
    if(Math.abs(deltas[i]) > Math.abs(advDelta)) {
        System.out.println("Big jump between " + nums[i] + " " + nums[i+1]);
    }
}

答案 2 :(得分:1)

此问题没有绝对解决方案,您必须确定要应用解决方案的上下文的阈值。

没有算法可以为我们提供跳转规则。我们作为人类能够确定这些变化,因为我们现在能够一目了然地看到整个数据。但是如果数据集足够大,那么我们就很难说出要考虑哪些跳转。例如,如果连续数字之间的平均差异为10,则高于该值的任何差异将被视为跳跃。然而,在大型数据集中,可能存在差异,这些差异是一种尖峰或者从10开始出现新的正常差异,差异突然变为100.我们将不得不决定是否要根据差异平均值10来获得跳跃或者100。

如果我们对本地峰值感兴趣,那么就可以按照@ug_

的建议使用moving average

然而,移动平均线必须移动,这意味着我们维护一组具有固定设置大小的本地数字。在此我们计算差异的平均值,然后将它们与当地差异进行比较。

然而,这里我们再次面临确定本地集大小的问题。此阈值确定我们捕获的跳转的粒度。一个非常大的集合将倾向于忽略更接近的跳跃,而较小的集合将倾向于提供误报。

按照简单的解决方案,您可以尝试设置阈值。在这种情况下,本地集合大小为3,这是可以使用的最小值,因为它将为我们提供所需的最小差异计数为2.

public class TestJump {
    public static void main(String[] args) {
        int[] arr = {1, 2, 4, 6, 34, 38, 41, 67, 69, 71};
        //int[] arr = {1,4,8,13,19,39,60,84,109};

        double thresholdDeviation = 50; //percent jump to detect, set for your reuirement
        double thresholdDiff = 3; //Minimum difference between consecutive differences to avoid false positives like 1,2,4

        System.out.println("Started");

        for(int i = 1; i < arr.length - 1; i++) {
            double diffPrev = Math.abs(arr[i] - arr[i-1]);
            double diffNext = Math.abs(arr[i+1] - arr[i]);

            double deviation = Math.abs(diffNext - diffPrev) / diffPrev * 100;

            if(deviation > thresholdDeviation && Math.abs(diffNext - diffPrev) > thresholdDiff) {
                System.out.printf("Abrupt change @ %d: (%d, %d, %d)%n", i, arr[i-1], arr[i], arr[i+1]);
                i++;
            }
            //System.out.println(deviation + " : " + Math.abs(diffNext - diffPrev));
        }

        System.out.println("Finished");
    }
}

<强>输出

Started
Abrupt change @ 3: (4, 6, 34)
Abrupt change @ 6: (38, 41, 67)
Finished

如果您正在尝试解决一个更大的问题,而不仅仅是寻找医疗数据或图像中的尖峰等数据,那么您应该检查神经网络。