如何在Swift中找到数组中的多个峰和谷元素?

问题描述 投票:0回答:2

我正在开发一个潮汐指南应用程序。我有一个包含每小时从00:00开始7天的潮汐水平的数组。数组中有168个元素。 (24小时x 7天)。

作为人类,您可以按顺序查看每个Double,并识别数字何时开始增加或减少,从而可以找到高峰和低谷。

根据潮汐的工作原理,我需要将整个7天的行程安排在一开始。我计划在找到高峰和低谷并检查数组中它们的索引之后将其分类为每一天。如果它们的索引在0到23之间,则我们知道其第1天,依此类推。

let arr = [2.9, 2.8, 3.0, 3.5, 4.0, 4.4, 4.6, 4.4, 3.9, 3.1, 2.3, 1.4, 0.6, 0.3, 0.5, 1.1, 1.9, 2.8, 3.7, 4.3, 4.6, 4.5, 4.2, 3.8, 3.4, 3.1, 3.1, 3.3, 3.7, 4.1, 4.4, 4.4, 4.2, 3.5, 2.7, 1.9, 1.0, 0.4, 0.3, 0.6, 1.2, 2.1, 3.0, 3.9, 4.4, 4.7, 4.6, 4.3, 3.9, 3.5, 3.3, 3.3, 3.5, 3.8, 4.1, 4.3, 4.2, 3.8, 3.2, 2.4, 1.6, 0.9, 0.4, 0.4, 0.9, 1.4, 2.3, 3.2, 4.0, 4.5, 4.7, 4.6, 4.3, 3.9, 3.6, 3.4, 3.4, 3.5, 3.7, 3.9, 4.0, 3.9, 3.5, 2.9, 2.2, 1.5, 0.9, 0.6, 0.6, 1.1, 1.7, 2.5, 3.4, 4.1, 4.5, 4.6, 4.5, 4.3, 3.9, 3.6, 3.4, 3.3, 3.4, 3.6, 3.7, 3.7, 3.6, 3.2, 2.7, 2.1, 1.5, 1.0, 0.8, 1.0, 1.4, 2.0, 2.7, 3.5, 4.1, 4.5, 4.6, 4.5, 4.2, 3.9, 3.6, 3.3, 3.2, 3.3, 3.3, 3.4, 3.4, 3.3, 3.0, 2.6, 2.1, 1.7, 1.3, 1.2, 1.3, 1.7, 2.3, 2.9, 3.6, 4.1, 4.4, 4.5, 4.4, 4.1, 3.8, 3.4, 3.2, 3.0, 3.0, 3.1, 3.2, 3.2, 3.1, 2.9, 2.6, 2.3, 1.9, 1.6, 1.5, 1.6, 2.0, 2.5, 3.1, 3.6]

在此数组中,您可以看到第一个谷是索引1的2.8。然后,数字开始增加,直到索引6的4.6,然后开始减少。这是数据的直观表示:enter image description here

我也有两个空数组:

var highTides = [Int]()
var lowTides = [Int]()

那些数组将需要包含原始数组的峰和谷的索引。这是数组的外观:

var highTides = [6, 20, 30, 31, 45, 55, 70, 80, 95, 104, 105, 120, 129, 130, 145, 154, 155]
//Two indexes that are beside each other (eg 30 and 31) which have the same peak value of 4.4 must both be included in the `highTides` array

在“ arr”开始时,数字开始下降,但这并不意味着index [0]是一个峰值。 “ arr”的结尾也一样。 “ arr”的结尾开始上升,但这并不意味着nt [167]是一个峰值。

在您提供的任何帮助下,请包括一种忽略索引0和167的峰值或谷值的方法。(否则称为arr.first或arr.last)

如果您有任何其他问题,我会尽力解释我需要做的事情,请告诉我。谢谢:)

arrays swift graphing
2个回答
0
投票

从索引1到索引arr.count-2遍历数组(基本上忽略第一个和最后一个元素),并检查先前的p,当前的c和下一个n值。如果电流比上一个和下一个更高或相等,则为峰值;如果它等于或小于-则为山谷。

for idx in 1..<arr.count-1 {
    let p = arr[idx-1]
    let c = arr[idx]
    let n = arr[idx+1]

    if (c >= p && c >= n) {
        highTides.append(idx)
    }

    if (c <= p && c <= n) {
        lowTides.append(idx)
    }
}

0
投票

@@ NewDev的答案未考虑这样的图形区域:

[... 3.2, 3.3, 3.3, 3.4, ...]

(注意它如何开始上升,展平,然后再次上升。)

如果仅在迭代中检查当前值之前和之后的值,则在上面的示例中,第一个3.3将被视为峰值(因为3.3 >= 3.2 && 3.3 >= 3.3),即使它不是峰值。同样,第二个3.3将被视为一个山谷(因为3.3 <= 3.3 && 3.3 <= 3.4),即使它不是一个山谷。

这是我的解决方案,它通过跟踪最后一个峰和谷起始索引来解决此问题,并将峰和谷视为范围而不是单个索引:

extension Array where Element: Comparable {
    var rangesOfPeaksAndValleys: (peaks: [ClosedRange<Int>], valleys: [ClosedRange<Int>]) {
        guard !isEmpty else { return ([], []) }

        var peaks = [ClosedRange<Int>]()
        var valleys = [ClosedRange<Int>]()

        var previousValue = self[0]
        var lastPeakStartingIndex: Int?
        var lastValleyStartingIndex: Int?

        for (index, value) in enumerated() {
            if value > previousValue {
                if let lastValleyStartingIndexUnwrapped = lastValleyStartingIndex {
                    valleys.append(lastValleyStartingIndexUnwrapped...index - 1)
                    lastValleyStartingIndex = nil
                }

                lastPeakStartingIndex = index
            } else if value < previousValue {
                if let lastPeakStartingIndexUnwrapped = lastPeakStartingIndex {
                    peaks.append(lastPeakStartingIndexUnwrapped...index - 1)
                    lastPeakStartingIndex = nil
                }

                lastValleyStartingIndex = index
            }

            previousValue = value
        }

        return (peaks, valleys)
    }
}

用法:

let arr: [Double] = [2.9, 2.8, 3.0, 3.5, 4.0, 4.4, 4.6, 4.4, 3.9, 3.1, 2.3, 1.4, 0.6, 0.3, 0.5, 1.1, 1.9, 2.8, 3.7, 4.3, 4.6, 4.5, 4.2, 3.8, 3.4, 3.1, 3.1, 3.3, 3.7, 4.1, 4.4, 4.4, 4.2, 3.5, 2.7, 1.9, 1.0, 0.4, 0.3, 0.6, 1.2, 2.1, 3.0, 3.9, 4.4, 4.7, 4.6, 4.3, 3.9, 3.5, 3.3, 3.3, 3.5, 3.8, 4.1, 4.3, 4.2, 3.8, 3.2, 2.4, 1.6, 0.9, 0.4, 0.4, 0.9, 1.4, 2.3, 3.2, 4.0, 4.5, 4.7, 4.6, 4.3, 3.9, 3.6, 3.4, 3.4, 3.5, 3.7, 3.9, 4.0, 3.9, 3.5, 2.9, 2.2, 1.5, 0.9, 0.6, 0.6, 1.1, 1.7, 2.5, 3.4, 4.1, 4.5, 4.6, 4.5, 4.3, 3.9, 3.6, 3.4, 3.3, 3.4, 3.6, 3.7, 3.7, 3.6, 3.2, 2.7, 2.1, 1.5, 1.0, 0.8, 1.0, 1.4, 2.0, 2.7, 3.5, 4.1, 4.5, 4.6, 4.5, 4.2, 3.9, 3.6, 3.3, 3.2, 3.3, 3.3, 3.4, 3.4, 3.3, 3.0, 2.6, 2.1, 1.7, 1.3, 1.2, 1.3, 1.7, 2.3, 2.9, 3.6, 4.1, 4.4, 4.5, 4.4, 4.1, 3.8, 3.4, 3.2, 3.0, 3.0, 3.1, 3.2, 3.2, 3.1, 2.9, 2.6, 2.3, 1.9, 1.6, 1.5, 1.6, 2.0, 2.5, 3.1, 3.6]

let (peaks, valleys) = arr.rangesOfPeaksAndValleys

// Convert the above arrays of `ClosedRange`s to arrays of `Int`s:
let highTides = peaks.flatMap { Array($0) }
let lowTides = valleys.flatMap { Array($0) }

print(highTides) // [6, 20, 30, 31, 45, 55, 70, 80, 95, 104, 105, 120, 129, 130, 145, 154, 155]
print(lowTides)  // [1, 13, 25, 26, 38, 50, 51, 62, 63, 75, 76, 87, 88, 101, 112, 126, 137, 151, 152, 162]
© www.soinside.com 2019 - 2024. All rights reserved.