Go 数组切割优雅解法

目录

标准数组切割

进阶版

Sync Map实现

Redis实现

扩展提问


  • 标准数组切割

// 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
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值