目录
-
标准数组切割
// SpiltArray
/**
target: 目标数组
num: 子数组长度
*/
func SpiltArray(target []int, num int) (map[int][]int, error) {
leng := len(target)
if leng < num {
return nil, fmt.Errorf("目标切割长度小于内部子元素长度:%d,源数据长度:%d", num, leng)
}
// 分批次切割
// 这里似乎有一个问题,如果是奇数,需要额外补齐,那么游戏场景,如果匹配机制的话,需要额外添加bot
var (
group int
result = make(map[int][]int)
)
if leng%num == 0 {
group = leng / num
} else {
group = leng/num + 1
}
var start, end, seq int
for i := 0; i < group; i++ {
start = i * num
end = (i + 1) * num
seq = i + 1
if i == group-1 {
result[seq] = target[start:]
} else {
result[seq] = target[start:end]
}
}
return result, nil
}
-
进阶版
Sync Map实现
如果数据量是以W级,如何做到快速切割?
在本机进行验证,百万数据,假设子数组长度为10,耗时在2.321584ms
以原子Map其实也只是在锁的基础上进行了优化,并不适合写多的场景,后续将会研究分片Map
// SpiltArray
/**
target: 目标数组
num: 子数组长度
*/
func SpiltArray(target []int, num int) (sync.Map, error) {
leng := len(target)
if leng < num {
return sync.Map{}, fmt.Errorf("目标切割长度小于内部子元素长度:%d,源数据长度:%d", num, leng)
}
var (
group int
result sync.Map
)
if leng%num == 0 {
group = leng / num
} else {
group = leng/num + 1
}
var (
wg sync.WaitGroup
start, end, seq int
)
for i := 0; i < group; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
start = i * num
end = (i + 1) * num
seq = i + 1
if i == group-1 {
result.Store(seq, target[start:])
} else {
result.Store(seq, target[start:end])
}
}(i)
}
wg.Wait()
return result, nil
}
Redis实现
在正式场景中,前端界面显示数据不可能一次性拿到百万数据进行显示,这个时候可以采取进一步优化措施:
将分组数据存储在redis中,只返回分组信息,例如序列号以及分组自定义属性,这个时候数据进一步精简,点进分组序列号返回内部子数组信息,耗时在1-10ms级别。
扩展提问
- 如果针对分组设置梯度,例如0-100为梯度,如何实现?
type SeperateGroup struct {
// gradient直接在DB层面继续处理
Start int
End int
Data []int
Room map[int][]int
}
// SpiltGradArray
/**
target: 目标数组
num: 子数组长度
*/
func SpiltGradArray(data []*SeperateGroup, num int) error {
for _, datum := range data {
// 直接针对data进行分房
sub, err := SpiltArray(datum.Data, num)
if err != nil {
return err
}
datum.Room = sub
}
return nil
}
- 如果在梯度的基础上,增加均匀度要求,如何实现?
均匀分布:在相同长度间隔的分布概率是等可能的。 -- 算法依然待优化
func SpiltGradArray(data []*SeparateGroup, num, gradient int) error {
for _, datum := range data {
// 需要针对start, end以及元素组成个数,拆分成多个buckets
// 比如直接以百级别数据作为子梯度分配
var (
seq int
buckets = make([][]int, 0)
subT = make(map[int][]int, 0)
)
for seq*gradient <= len(datum.Data) {
if (seq+1)*gradient >= len(datum.Data) {
buckets = append(buckets, datum.Data[seq*gradient:])
} else {
buckets = append(buckets, datum.Data[seq*gradient:(seq+1)*gradient])
}
seq++
}
seq = 0
// 直接从多个buckets随机取一个数据进入target number
for len(subT) == len(buckets) {
var temp = make([]int, 0)
for _, bucket := range buckets {
mathn := rand.Intn(gradient)
// 极端情况下,只有部分数据
if mathn < len(bucket) {
temp = append(temp, bucket[mathn])
}
}
subT[seq] = temp
seq++
}
}
return nil
}