【问题标题】:Detecting all abrupt changes in array检测数组中的所有突然变化
【发布时间】:2017-01-13 10:37:09
【问题描述】:

如何发现数组的突变?例如,如果您有以下数组:

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

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]

【问题讨论】:

  • 1、2、4、6、34、38、41、67、69、71会输出什么? [1, 2, 4, 6], [34, 38, 41], [67, 69, 71]or, 28(最大跳跃)??
  • 输出将是跳转/下降开始的索引/值。例如,[1, 2, 4, 6], [34, 38, 41], [67, 69, 71] 将有类似 6 和 41 的输出
  • 这听起来很像edge detection 问题,或一维模拟step detection
  • @friendlydog,我找不到该算法的任何 Java 实现。或者实现这个 alog 的简单步骤(不是复杂的数学符号)。
  • 你是如何定义大跳或大跌的?有阈值吗?

标签: java arrays algorithm


【解决方案1】:

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

我认为你应该退后一步;并更深入地了解问题背后的数学 - 为您的实际问题提出清晰的“语义”和清晰的定义(例如基于平均值、偏差等)。我在上面给出的维基百科链接应该是这部分的一个很好的起点。

从那里开始,要获得 Java 实现,您可能会开始查看 here

【讨论】:

    【解决方案2】:

    我会考虑使用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]);
        }
    }
    

    【讨论】:

    • 它似乎适用于我在问题中提供的所有示例。我也将等待其他人分析答案,并确保在任何隐藏的情况下都不会失败。
    • 这个算法只是试图找到上述平均差异。如果系列突然包含高差异,这将失败。喜欢1,4,8,13,19,39,60,84,109。差异是3,4,5,6,20,21,24,25,只有跳跃是在 19 到 39,但它会将每个下一个数字结果作为跳跃,因为它们的差异大于总平均值。您应该尝试实际的移动平均线。
    【解决方案3】:

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

    没有算法可以给我们跳跃的规则。作为人类,我们能够确定这些变化,因为我们现在能够一眼看到整个数据。但是如果数据集足够大,那么我们就很难说要考虑哪些跳跃。例如,如果连续数字之间的平均差异为 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
    

    如果您要解决的不仅仅是数组问题,例如在医学数据或图像中寻找峰值,那么您应该检查神经网络。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-05-31
      • 1970-01-01
      • 1970-01-01
      • 2020-07-05
      • 1970-01-01
      • 2023-04-11
      • 2015-05-26
      • 1970-01-01
      相关资源
      最近更新 更多