20210619_数字元素均匀分配
前言
在工作中,有位同事提出了一个场景问题,当时没有只想到前两种方法,事后发现还有2种方法,所以进行笔记
时间线
2021年6月18日 问题提出,解决
2021年6月19日 笔记
问题
从1到n存在若干数字元素,如何将这些数字元素均匀分配到k个集合中。
例如从1到32,将元素分到8个集合中,则8个集合中的第1个集合是
[1,9,17,25]
,第2个集合是[2,10,18,26]
解决思路
思考
可以使用数据结构实现问题场景模拟,也可以使用数学方法
方法1:使用队列
最开始时想到的便是使用队列模拟实现
在一个集合中创建x个空子集合,遍历1到n,每一次都将集合弹出头部子集合,插入当前元素后,再将这个子集合从集合的尾部插入。
遍历所有元素后,即完成。
4个元素分为2组进行模拟,有如下结果:
遍历元素(从1到4) | 结果 |
---|---|
开始 | [[],[]] |
1 | [[],[1]] |
2 | [[1],[2]] |
3 | [[2],[1,3]] |
4 | [[1,3],[2,4]] |
结束 | [[1,3],[2,4]] |
方法2:使用栈
问题中,还有一些信息是可以使用到的,如下:
- 有n个元素,分为k组,在整除的条件下,每一组会有n//k个元素
- 第1组的子集合中的第1个元素是1,第2个元素是1+k,依次类推,共有n//k个元素,表达式可以如下:
x = 1 + ( n − 1 ) k x = 1+(n-1)k x=1+(n−1)k
- 如果知道第1组的结果后,可以求出后面几组的结果,需要通过k-1次求解可以得到结果
通过以上结论,使用栈很容易实现。
同上,使用4个元素分为2组进行模拟:
过程
-
开始时,结果集合为[],
-
求出第1个子集合,可以得到 [[1,3]]
-
获取集合尾部的第一个子集合,根据子集合中的结果,逐个加1,可得到另一个子集合,再从集合尾部插入
-
重复上一步骤k-1次后结束
方法3:等差数列
前面两个都是使用的场景模拟,利用了大部分条件,如多少个元素,分为多少组,怎么分配才能保证场景实现等等,
但没有具体到数学公式中。而问题中,有两个重要变量,即第几个子集合,在子集合中的第几个位置。
以上两个变量是前提
通过观察,可以发现,
从集合角度考虑,集合中有k个子集合。第k个子集合中的第一个元素是k,如第1个子集合的第一个元素是1,第2个子集合的第1个元素是2,依次类推
从子集合角度考虑,子集合中有n//k个元素,每一个子集合是一个等差数列
而根据等差数列表达式有:
a
n
=
a
1
+
(
n
−
1
)
d
a_n = a_1 + (n-1)d
an=a1+(n−1)d
其中a1表示子集合中的第1个元素,d为k,而n表示子集合中第几个元素,
使用i和j分别表示第几个子集合,在子集合中的第几个位置,会有:
x
=
i
+
(
j
−
1
)
∗
k
x = i + (j-1)*k
x=i+(j−1)∗k
使用4个元素分为2组进行模拟,则第2个子集合中的第2个元素,为x=2+(2-1)*2=4
方法4:求余
使用队列和栈需要大量的弹出插入操作,而等差数列的实现需要双重for循环,虽然两层for循环的次数为n。
而集合中的定位是可以通过求余获得的。集合中的第1个子集合中的元素与k相除,会余下1
第2个子集合中的元素与k相除,会余下2,依次类推
到了最后一个子集合,由于刚好是第k组,会整除。
所以使用求余是可行的。
代码实现
class Solution:
def splitOfQueue(self,k,n):
if n%k != 0:
return
res = [[] for i in range(k)]
for i in range(n):
topList = res.pop(0)
topList.append(i+1)
res.append(topList)
return res
def splitOfStack(self,k,n):
if n%k != 0:
return
nums = n // k
res = [[1+i*k for i in range(nums)]]
for i in range(1,k):
ele = [ele+1 for ele in res[-1]]
res.append(ele)
return res
def splitOfAS(self,k,n):
if n%k != 0:
return
nums = n // k
res = []
for i in range(k):
ele = []
for j in range(nums):
ele.append(i+1+j*k)
res.append(ele)
return res
def splitOfComp(self,k,n):
if n%k != 0:
return
res = [[] for i in range(k)]
for i in range(n):
res[i%k].append(i+1)
return res
if __name__ == "__main__":
sol = Solution()
res = sol.splitOfStack(8,32)
res = sol.splitOfAS(8,32)
res = sol.splitOfComp(8,32)
res = sol.splitOfQueue(8,32)