in pkg/degradation-detector/statistic/changeDetector.go [8:61]
func GetChangePointIndexes(data []int, minDistance int) []int {
n := len(data)
if n <= 2 {
return []int{}
}
if minDistance < 1 || minDistance > n {
panic("minDistance should be in range from 1 to len(data)")
}
penalty := 3 * math.Log(float64(n))
k := int(math.Min(float64(n), math.Ceil(4*math.Log(float64(n)))))
partialSums := getPartialSums(data, k)
cost := func(tau1, tau2 int) float64 {
return getSegmentCost(partialSums, tau1, tau2, k, n)
}
bestCost := make([]float64, n+1)
bestCost[0] = -penalty
for currentTau := minDistance; currentTau < 2*minDistance; currentTau++ {
bestCost[currentTau] = cost(0, currentTau)
}
previousChangePointIndex := make([]int, n+1)
previousTaus := []int{0, minDistance}
var costForPreviousTau []float64
for currentTau := 2 * minDistance; currentTau < n+1; currentTau++ {
costForPreviousTau = costForPreviousTau[:0]
for _, previousTau := range previousTaus {
costForPreviousTau = append(costForPreviousTau, bestCost[previousTau]+cost(previousTau, currentTau)+penalty)
}
bestPreviousTauIndex := whichMin(costForPreviousTau)
bestCost[currentTau] = costForPreviousTau[bestPreviousTauIndex]
previousChangePointIndex[currentTau] = previousTaus[bestPreviousTauIndex]
newPreviousTaus := make([]int, 0, len(previousTaus))
for i, cost2 := range costForPreviousTau {
if cost2 < bestCost[currentTau]+penalty {
newPreviousTaus = append(newPreviousTaus, previousTaus[i])
}
}
previousTaus = newPreviousTaus
previousTaus = append(previousTaus, currentTau-(minDistance-1))
}
var changePointIndexes []int
currentIndex := previousChangePointIndex[n]
for currentIndex != 0 {
changePointIndexes = append(changePointIndexes, currentIndex)
currentIndex = previousChangePointIndex[currentIndex]
}
slices.Reverse(changePointIndexes)
return changePointIndexes
}