“老李算法”:用python实现的随机抽号程序

一、前言

阅读这篇文章前,需要掌握的知识:

  • python的基础语法
  • python列表
  • 函数的定义和调用
  • 字典的定义及添加键值对
  • while循环和if语句
  • (如果没掌握也没关系,我会尽力解释清楚)

二、问题描述

某天上语文课时实在无聊,就研究起了语文老师点人回答问题的方法:

(语文老师姓李,且年龄比较大,我们称之为“老李”,因此这种点人的方法姑且被我称为“老李算法”)

  • 先随机抽取一个学号(记作学生A),然后随机选一个方向,如“5号的右边”。
  • 然后找到5号右边的学生B回答问题。
  • 询问学生B的学号(如16号),则从6号、26号和36号中选择一个学号的某个方向(如26号的后面),找到对应的学生C回答下一个问题。
  • 循环进行第三步,每次都取回答问题的学生的学号的末位并以此末位为个位,进行下一次抽取
  • 如果抽到的学生A在教室边缘位置,而随机的方向上恰好没有学生(如第一排的前面),则选择学生A本人回答问题。
  • (语文不太好,有点难以形容,举个简单的例子)

上图是一个简单的座位表,点人方式如下:

  • 36号的前面:8号
  • 18号的右边:18号
  • 38号的前面:33号
  • 以此类推

三、整体思路 

1.座位表

为了简化问题(能力有限),假设班里有40个同学,每十人一行坐成四行,则可以用列表的方式模拟出一个座位表。

#模拟座位表
stu_line0 = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
stu_line1 = [ 0,20,12, 1,11,19,34,15, 6,25,16, 0]
stu_line2 = [ 0, 3, 5,40,27, 2,14,29,22,39,33, 0]
stu_line3 = [ 0,24, 4,30,21, 8, 9,37,32,26,38, 0]
stu_line4 = [ 0,17,35,10,31,36,13,28, 7,23,18, 0]
stu_line5 = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
stu_group = [stu_line0,stu_line1,stu_line2,stu_line3,stu_line4,stu_line5]
  • 猜猜为什么要围一圈0?往后看就知道啦

2.准备工作

import random  #导入random模块用于生成随机数
student = {}  #定义一个空字典student
m = 0  #计数器

3.抽取第一个学号(学生A)和方向

def select_stu():
    """随机取值取向"""
    stu_num = random.randint(1,40)
    stu_dir = random.randint(1,4)
    if stu_dir ==1:
        s_direction = "左边"
    if stu_dir ==2:
        s_direction = "右边"
    if stu_dir ==3:
        s_direction = "前面"
    if stu_dir ==4:
        s_direction = "后面"
    student["number"] = stu_num
    student["direction"] = s_direction
    print("•",stu_num,"号的",s_direction)
  • 随机生成了一个1到40之间的整数,即学生A的学号,然后指定一个随机的方向,分别存储到字典student中并用print()输出。
  • print()里面的"•"是用来美化输出结果的,对程序无影响。

4.找到学生B

def stu_position():
    """定位找值"""
    if student["number"] in stu_line1:
        stu_line = stu_line1
        pos_x = 1
    if student["number"] in stu_line2:
        stu_line = stu_line2
        pos_x = 2
    if student["number"] in stu_line3:
        stu_line = stu_line3
        pos_x = 3
    if student["number"] in stu_line4:
        stu_line = stu_line4
        pos_x = 4
    student["x"] = pos_x
    student["y"] = stu_line.index(student["number"])
    """按向找值"""
    if student["direction"] == "左边":
        next_student = stu_line[student["y"] - 1]
    if student["direction"] == "右边":
        next_student = stu_line[student["y"] + 1]
    if student["direction"] == "前面":
        next_studen_line = stu_group[student["x"] - 1]
        next_student = next_studen_line[student["y"]]
    if student["direction"] == "后面":
        next_studen_line = stu_group[student["x"] + 1]
        next_student = next_studen_line[student["y"]]
    #防止边界取向
    if next_student == 0:
        next_student = student["number"]
    student["number"] = next_student
  • 确定学生A的位置:先用一大串if语句确定学生A在哪一个列表里,即学生A在哪一行,把这一行的列表关联到列表stu_line备用,把学生A在第几行记作pos_x(可以理解成x轴的坐标),然后存储到字典student中;在第16行,使用index()方法确定元素student["number"](即学生A的学号)的索引,记作pos_y(可以理解为y轴坐标),同样存储到字典student中。
  • 找到学生B:如果是左右的话,只要把学生A在列表stu_line里的索引加上或减去一即可;如果是前后的话,学生A的行数(student["x"])就是前面列表stu_group中的索引,将其加上或减去一,得到学生B所在的那一行,访问其中索引为y的值即为学生B的学号。(开始乱起来了,语文不行有点难描述清楚,凑合着看吧)
  • 这个时候前面那一圈0就有用了,如果按上面操作得到学生B的学号为0时,表明符合“问题描述”中第五点的情况,因此仍将学生A的学号赋给学生B,表明此时学生B即为学生A。

5.抽取下一位学生C

def next_stu():
    """抽取下一个值"""
    selected_list = list(str(student["number"]))
    end_number =selected_list[-1]
    a = int(end_number)
    next_list = [a,a+10,a+20,a+30]
    index = random.randint(0,3)
    next_number = next_list[index]
    while next_number == student["number"]:
        index = random.randint(0,3)
        next_number = next_list[index]
    """抽取下一个方向"""
    next_direction = random.randint(1,4)
    if next_direction == 1:
        s_direction = "左边"
    if next_direction == 2:
        s_direction = "右边"
    if next_direction == 3:
        s_direction = "前面"
    if next_direction == 4:
        s_direction = "后面"
    student["number"] = next_number
    student["direction"] = s_direction
    print("•",next_number,"号的",s_direction)
  • 将学生B的学号先用函数str()转换成字符串,然后用函数list()拆成列表,索引-1表示列表末位元素,然后转成整数方便后面的加法操作。
  • 根据前面得到的末位构建一个列表next_list,(其中的元素相当于“问题描述”中举例的6号,16号,26号和36号)然后用用一个while循环防止再次取到学生B的学号。
  • 然后抽取一个方向,和函数select_stu()结构类似,不做赘述。

​​​​​​6.实现循环抽号

stu_amount = int(input("Number of student:"))
if stu_amount < 1:
    print("学生人数应大于零!")
else:
    #抽取第一个学生
    select_stu()
    stu_position()
    print("  即",student["number"],"号")
    m += 1
    #抽取其余学生
    while m < stu_amount:
        next_stu()
        stu_position()
        print("  即",student["number"],"号")
        m += 1
        
  • 使用函数input()获取需要抽取的总学生数。
  • 第一个学生使用函数select_stu()抽号,并使用函数stu_position()找到,用print()输出结果。
  • 其他的学生使用函数next_stu()抽号,并使用函数stu_position()找到,用print()输出结果。


四、总结

整个程序的代码如下:

#模拟座位表
stu_line0 = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
stu_line1 = [ 0,20,12, 1,11,19,34,15, 6,25,16, 0]
stu_line2 = [ 0, 3, 5,40,27, 2,14,29,22,39,33, 0]
stu_line3 = [ 0,24, 4,30,21, 8, 9,37,32,26,38, 0]
stu_line4 = [ 0,17,35,10,31,36,13,28, 7,23,18, 0]
stu_line5 = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
stu_group = [stu_line0,stu_line1,stu_line2,stu_line3,stu_line4,stu_line5]

#准备
import random
student = {}
m = 0

#函数部分
def select_stu():
    """随机取值取向"""
    stu_num = random.randint(1,40)
    stu_dir = random.randint(1,4)
    if stu_dir ==1:
        s_direction = "左边"
    if stu_dir ==2:
        s_direction = "右边"
    if stu_dir ==3:
        s_direction = "前面"
    if stu_dir ==4:
        s_direction = "后面"
    student["number"] = stu_num
    student["direction"] = s_direction
    print("•",stu_num,"号的",s_direction)

def stu_position():
    """定位找值"""
    if student["number"] in stu_line1:
        stu_line = stu_line1
        pos_x = 1
    if student["number"] in stu_line2:
        stu_line = stu_line2
        pos_x = 2
    if student["number"] in stu_line3:
        stu_line = stu_line3
        pos_x = 3
    if student["number"] in stu_line4:
        stu_line = stu_line4
        pos_x = 4
    student["x"] = pos_x
    student["y"] = stu_line.index(student["number"])
    """按向找值"""
    if student["direction"] == "左边":
        next_student = stu_line[student["y"] - 1]
    if student["direction"] == "右边":
        next_student = stu_line[student["y"] + 1]
    if student["direction"] == "前面":
        next_studen_line = stu_group[student["x"] - 1]
        next_student = next_studen_line[student["y"]]
    if student["direction"] == "后面":
        next_studen_line = stu_group[student["x"] + 1]
        next_student = next_studen_line[student["y"]]
    #防止边界取向
    if next_student == 0:
        next_student = student["number"]
    student["number"] = next_student

def next_stu():
    """抽取下一个值"""
    selected_list = list(str(student["number"]))
    end_number =selected_list[-1]
    a = int(end_number)
    next_list = [a,a+10,a+20,a+30]
    index = random.randint(0,3)
    next_number = next_list[index]
    while next_number == student["number"]:
        index = random.randint(0,3)
        next_number = next_list[index]
    """抽取下一个方向"""
    next_direction = random.randint(1,4)
    if next_direction == 1:
        s_direction = "左边"
    if next_direction == 2:
        s_direction = "右边"
    if next_direction == 3:
        s_direction = "前面"
    if next_direction == 4:
        s_direction = "后面"
    student["number"] = next_number
    student["direction"] = s_direction
    print("•",next_number,"号的",s_direction)

#循环部分
stu_amount = int(input("Number of student:"))
if stu_amount < 1:
    print("学生人数应大于零!")
else:
    #抽取第一个学生
    select_stu()
    stu_position()
    print("  即",student["number"],"号")
    m += 1
    #抽取其余学生
    while m < stu_amount:
        next_stu()
        stu_position()
        print("  即",student["number"],"号")
        m += 1
        

冒着宕机的风险运行了一下,所幸没出什么大岔子,运行结果如下:

Number of student:10
• 34 号的 前面
  即 34 号
• 4 号的 右边
  即 30 号
• 30 号的 前面
  即 40 号
• 30 号的 右边
  即 21 号
• 1 号的 左边
  即 12 号
• 22 号的 前面
  即 6 号
• 16 号的 左边
  即 25 号
• 35 号的 左边
  即 17 号
• 37 号的 右边
  即 32 号
• 12 号的 后面
  即 5 号

对照座位表验证一遍,程序成功运行,没有逻辑错误。


五、结束语

感谢每一个坚持看到这里的小伙伴!

把自己的思路说出来并让人明白并不容易,而我也正在努力做到这一点。

本人正在读高二,用来学python的时间不多,难免有错误和偏颇之处,还请各位多多包涵,不吝指教。

如果觉得这篇文章用的话,请点一个小小的赞吧~

  • 10
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值