题目描述
假设数组A包含的全是整数,其长度为n,数组中的元素可能是重复的。设计一个算法,找到一系列上标的集合,也就是:
I
=
i
0
,
i
1
,
…
,
i
k
I = i^0,i^1,{\ldots},i^k
I=i0,i1,…,ik
使得下面等式成立:
(
A
i
0
+
A
i
1
+
A
i
2
+
⋯
+
A
i
k
)
%
n
=
0
(A^{i^0} +A^{i^1} +A^{i^2}+\cdots+A^{i^k}) \%n =0
(Ai0+Ai1+Ai2+⋯+Aik)%n=0
算法描述
我们需要解决两个问题,第一:是需要确定这样的集合一定存在,第二:集合存在,如何找到构成这个集合的元素。
假设有n个盒子,编号分别为0,1,2,…,n-1。分别从数组中拿出元素对n求余,式子如下
(
A
0
)
%
n
=
t
0
(
A
0
+
A
1
)
%
n
=
t
1
(
A
0
+
A
1
+
A
2
)
%
n
=
t
2
…
…
(
A
0
+
A
1
+
A
2
+
⋯
+
A
n
)
%
n
=
t
n
(A^{0} ) \%n =t^0 \\ (A^{0} +A^{1} ) \%n =t^1 \\ (A^{0} +A^{1} +A^{2}) \%n =t^2 \\ …… \\ (A^{0} +A^{1} +A^{2}+\cdots+A^{n}) \%n =t^n
(A0)%n=t0(A0+A1)%n=t1(A0+A1+A2)%n=t2……(A0+A1+A2+⋯+An)%n=tn
如果上面式子中ti为0,那么该式子的所有元素为要求的集合,其上标为最后解。如果没有,那么会存在一下等式:
(
A
0
+
A
1
+
A
2
+
⋯
+
A
i
)
%
n
=
t
i
(
A
0
+
A
1
+
A
2
+
⋯
+
A
i
+
1
+
⋯
+
A
j
)
%
n
=
t
j
t
i
=
t
j
(A^{0} +A^{1} +A^{2}+\cdots+A^{i}) \%n =t^i \\ (A^{0} +A^{1} +A^{2}+\cdots+A^{i+1}+\cdots+A^{j}) \%n =t^j \\ t^i = t^j
(A0+A1+A2+⋯+Ai)%n=ti(A0+A1+A2+⋯+Ai+1+⋯+Aj)%n=tjti=tj
其中i<j;
所以有
(
A
i
+
1
+
⋯
+
A
j
)
%
n
=
0
(A^{i+1}+\cdots+A^{j}) \%n =0
(Ai+1+⋯+Aj)%n=0
代码实现
def findModuleSubSet(A):
'''
设置编号为0到n-1的盒子,并把他们设置为0,表示盒子里面没有球,如果boxes[i]不等于0
表示编号为i的盒子里面已经有球
'''
boxes = []
for i in range(len(A)):
boxes.append(0)
sum = 0
subSet = []
'''
依次取出元素相加后对数组长度求余,然后把余数当做盒子编号,将对应boxes数组中的元素设置为非0值
'''
for k in range(len(A)):
sum += A[k]
subSet.append(k)
t = sum % len(A)
#如果余数为0,那么我们找到了想要的子集
if t == 0:
return subSet
'''
检测对应编号的盒子是否为0,如果不是0说明我们找到了i,j,i < j
(A[0]+A[1]...A[i]) % n == (A[0]+A[1]+....A[j]) %n
于是(A[i+1]+...A[j])%n == 0
也就是元素A[i+1]...A[j]就是我们要找的子集
'''
if boxes[t] != 0:
preSum = 0
for i in range(k+1):
#找到满足条件的i,subSet[i+1:]就是满足条件的子集
preSum += A[i]
if (preSum % len(A) == t):
return subSet[i+1:]
#如果对应编号盒子是0,那么把boxes[k]设置为1,表明该盒子已经放入了一个球
boxes[t] = 1
return []
测试
import random
A = []
for i in range (9):
A.append(random.randint(10, 999))
print(A)
subSet = findModuleSubSet(A)
print(subSet)