Python不重复的相同间隔取数问题

0x00 问题引入

初中招生采取电脑随机派位方式进行录取,先由第一位家长代表抽取一位学生的报名序号作为录取第一人,再由第二位家长代表抽取每位被录取学生报名序号的间隔数,现某中学报名人数为885人,报名序号为: 0620001-0620885计划录取267人,现第一位家长代表抽取的号码为0620278第二位家长选取的间隔数字为865,请编写程序把所有被录取学生的报名序号输出至文件中。

格式如下:
0620278
0620258
0620239
0620221
0620204
0620188
0620173

0620684
0620152
0620503

0x01 问题分析

通过对问题进行分析,可以大致判断该题为不重复的相同间隔取数问题

原题中的数据量较为庞大,在进行问题分析时,可对问题进行简化

现有1,2,3,...,19,2020个数,要求从2开始取数,每隔4个取一次,共需要取10个数,请输出取出的10个数

由于需要不重复进行取数,因此每次取完数后,需要从原序列中删除该数,并继续取数。在遍历到序列末尾时,下一次将从角标0继续取数,因此循环变量需要对当前序列的长度进行求模运算,确保每次取到的数都在序列内,不会发生越界错误

用列表进行演算,前六次的结果如下表所示,取出的数分别为2、6、10、14、18、3

在这里插入图片描述

0x02 算法实现

alist = [i for i in range(1,886)]

index = 277 # 当前录取角标
for i in range(267):
    print('0620%03d'%alist[index])
    alist.pop(index)
    index = (index + 865 - 1) % len(alist)

运行结果如下图所示

在这里插入图片描述
完整代码如下

fp = open('result.txt', 'a+')

alist = [i for i in range(1,886)]

index = 277 # 当前录取角标
for i in range(267):
    fp.write('0620%03d\n'%alist[index])
    alist.pop(index)
    index = (index + 865 - 1) % len(alist)
fp.flush()
fp.close()

输出的文件如下图

在这里插入图片描述

0x03 思路拓展

上文提到的算法使用列表存储可选序号的范围,而对列表进行pop操作将耗费更多资源,在对上文算法进行分析后,不难发现pop元素的意义在于删除(跳过)已经选取的数。而如果仅使用循环,并对已经选取的数进行存储,依然可以达到跳过已经选取的数的目的

再次对0x01中提到的简化问题进行分析,第1-10步的选取过程如下图

在这里插入图片描述
每次选取仅为循环过程,前5步没有任何问题,从第6步开始,也就是从19开始往后数4个间隔,结果为2,但是由于2已经在之前取到过了,因此,第4个间隔步骤并不能算有效步骤,需要往后再次寻找,直到找到一个没有被选过的数3,因此第6步的结果为3

依照这个思路,可以将算法改写如下

alist = []
i, now = 0, 278
while i < 267:
    alist.append(now)
    i += 1
    # 后推
    j = 0
    while j < 865:
        now += 1
        if now in alist:
            j -= 1
        if now > 885:
            now = 1
        j += 1
print(alist)

运行后得到的数据与0x02中得到的数据一致,如下图

在这里插入图片描述

0x04 总结

该问题为一个典型的不重复的相同间隔取数问题,该问题可以通过列表实现,但由于每次成功取数后对列表删除元素需要耗费更多资源,因此可以对算法进行优化,仅使用循环并存储已经取过的数,在进行循环取数的过程中,若经过已经取过的数,则该次循环为无效循环,需要再次向后寻找直到找到未被取过的数作为有效取数

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值