ClickHouse集群缩容,为保证数据不丢失,计划将需要缩容的节点上的数据,迁移到其他节点上,保证迁移到每个机器上的数据量尽量均衡。数据的迁移已partition为单位,已知每个partition的数据量。
将一个包含m个整数的数组分成n个数组,每个数组的和尽量接近
这个问题是典型的动态规划的问题,理论上是无法找到最优解的,但是本次只是为了解决实际生产中的问题,而不是要AC,所以我们只需要找到一个相对合理的算法,使得partition的分配相对均衡就好了。
输入:int数组,分组数divisionNum
我们举一个栗子:
数组为:500, 18, 28, 2, 27, 35, 22, 10, 6, 5, 3, 2, 1;分为4组
排序为:500, 35, 28, 27, 22, 18, 10, 6, 5, 3, 2, 2, 1
计算平均值 avg = 164.75
遍历数组:
结果:
arr 0 is : 500, sum = 500
arr 1 is : 35 18, sum = 53
arr 2 is : 28 22 3, sum = 53
arr 3 is : 27 10 6 5 2 2 1, sum = 53
// 将数组分成n个数组,每个数组的和尽量接近
func GetAvgArr(numberList []int64, arrNum int) [][]int64 {
avgArrays := make([][]int64, 0)
if len(numberList) == 0 || len(numberList) < arrNum {
return avgArrays
}
// 1. 计算平均值
var sum, mean float64
numberListFloat64 := make([]float64, 0)
for _, num := range numberList {
numberListFloat64 = append(numberListFloat64, float64(num))
sum += float64(num)
}
mean = sum / float64(arrNum)
// 2. 倒序排序
sort.Sort(sort.Reverse(sort.Float64Slice(numberListFloat64)))
for cnt := 0; cnt < arrNum; cnt++ {
//fmt.Println("mean = ", mean)
var arr []float64
if cnt == arrNum-1 {
// 最后一组,返回数组剩余所有数
avgArrays = append(avgArrays, transFloatToIntList(numberListFloat64))
break
}
// 如果最大的数max>=mean,这个数单独一个组
if len(numberListFloat64) > 0 && numberListFloat64[0] >= mean {
arr = []float64{float64(numberListFloat64[0])}
avgArrays = append(avgArrays, transFloatToIntList(arr))
sum = sum - numberListFloat64[0]
// 重新计算剩下partition的平均值
mean = sum / float64(arrNum-len(avgArrays))
} else {
// 否则寻找一组数据
arr, _ = getList(numberListFloat64, mean, math.Pow(mean, 2))
avgArrays = append(avgArrays, transFloatToIntList(arr))
}
// 将已经形成一组的数据从原数组中移除,准备寻找下一组数据
numberListFloat64 = removeFromFloatList(numberListFloat64, arr)
}
return avgArrays
}
// 将[]float64转为[]int64
func transFloatToIntList(floatList []float64) []int64 {
res := make([]int64, 0)
for _, item := range floatList {
res = append(res, int64(item))
}
return res
}
// 将在removeNums中出现过的数字从originalList中移除
func removeFromFloatList(originalList []float64, removeNums []float64) []float64 {
res := make([]float64, 0)
from := 0
for _, remove := range removeNums {
for i := from; i < len(originalList); i++ {
if originalList[i] == remove {
res = append(res, originalList[from:i]...)
from = i + 1
break
}
}
}
res = append(res, originalList[from:]...)
return res
}
func getList(arr []float64, delta, distance float64) ([]float64, float64) {
res := make([]float64, 0)
if len(arr) == 0 {
return res, -1
}
for i := 0; i < len(arr)-1; i++ {
switch true {
case delta == arr[i]:
res = append(res, arr[i])
return res, 0
case delta < arr[i]:
continue
case delta > arr[i]:
if i == 0 {
res = append(res, arr[i])
delta = delta - arr[i]
distance = math.Pow(delta, 2)
tmp, d := getList(arr[i+1:], delta, distance)
res = append(res, tmp...)
return res, d
} else {
dis1 := math.Pow(arr[i-1]-delta, 2)
dis2 := math.Pow(delta-arr[i], 2)
if dis1 > dis2 {
res = append(res, arr[i])
delta = delta - arr[i]
tmp, d := getList(arr[i+1:], delta, dis2)
res = append(res, tmp...)
return res, d
} else {
tmp, d := getList(arr[i:], delta, dis2)
if dis1 > d {
res = append(res, tmp...)
return res, d
}
res = append(res, arr[i-1])
return res, dis1
}
}
}
}
dis := math.Pow(delta-arr[len(arr)-1], 2)
if dis < distance {
return arr[len(arr)-1:], dis
}
return make([]float64, 0), -1
}
测试
func main() {
partitionList := []int64{500, 18, 28, 2, 27, 35, 22, 10, 6, 5, 3, 2, 1}
fmt.Println("test partitionList is : ", partitionList)
fmt.Println("result is :")
arrays := stage.GetAvgArr(partitionList, 4)
for i, arr := range arrays {
fmt.Printf("arr %d is : %d, ", i, arr)
var sumValue int64
for _, value := range arr {
sumValue += value
}
fmt.Println("sum = ", sumValue)
}
}
几组测试结果:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。