Python进阶:丢失的一笔订单

  • 2019 年 12 月 26 日
  • 笔记

2018年某天曾接到一个需求,要求给10个监考老师监考的10个科目来分配考场,要求每个老师的监考考场不能重复。见下图,不知道你感觉怎么样,我当时搞了几天没有找出随机生成的方法,丢失了一笔订单。

既然咱们已经学习了python,不妨用python试试。

需求分析:

1、生成一个10*1的数组;使用numpy.arange

2、随机排序;使用random.shuffle

3、如果一个10*1的数组到这里就完事了,可是题目要求的是10*10,没有现成的函数可以生成10*10,且行和列各不相等的矩阵。

4、考虑使用递归,下一次赋值时将已存在值去除(使用set集合)

5、每次赋值时要求随机取一个元素,使用random.sample

import numpy as npimport random  # 生成一个10*1 数组arr = np.arange(1, 11, 1)# 将该数组随机排序np.random.shuffle(arr)# 生成一个全部0值的10*10矩阵m = np.zeros((10, 10), int)# 将数组赋值给矩阵的第一行m[0, :] = arr
至此,第一行已经按照要求生成了,现在从m[1,0]开始给剩余的0赋值。定义一个计算函数calc(i, j) #i, j分别表示行列编号
def calc(i, j):    # 设置变量t,存储未出现过的数字,初始值为1~10的集合,第一次要填充    # m[1][0]位置,去除首行首列值就可以了,也就是此时i = 1, j = 0     t = set(np.arange(1, 11)) - set(m[0:1, 0])
# 从集合t中随机取一个元素赋值给m[1][0]x = random.sample(t, 1)
# 刚才赋值时用到的元素需要在集合t中剔除,否则会重复出现t.remove(int(x[0]))# 至此,第一次赋值就结束了,考虑继续赋值m[1][1],将列编号变量j+1,步骤相同# 数字1~10的集合,去除首行1列的值也就是去除m[0][1]t = set(np.arange(1, 11)) - set(m[0:1, 1])  # i=1,j=1# 同时,还需要去除1行0列的值,也就是m[1][0]t = t - set(m[1, 0:1]) # i=1,j=1
# 从集合t中随机取一个元素赋值给m[1][1]x = random.sample(t, 1)
# 同理,此时需要将刚才赋值时用到的元素在集合t中剔除t.remove(int(x[0]))# 至此,第二次赋值就结束了,考虑继续赋值m[1][2],将列编号变量j+1,步骤相同# 第二行赋值完了,赋值第三行。。。# 直到赋值到第9行第9列,否则一直循环运行。......

分析两次赋值的规律,抽象出calc函数如下:

def calc(i, j):    t = set(np.arange(1, 11)) - set(m[0:i, j])  # t是未出现数字的集合:数字1~10,去除首行至i-1 行,j列的已出现值    t = t - set(m[i, 0:j])  # 去除i行,j-1列的已出现值,经过行列去重,t是未出现数字的集合    while True:        if len(t) == 0:            return False        x = random.sample(t, 1)  # 集合t中随机截取一个长度为1的切片        m[i][j] = x[0]  # 将切片的元素值赋值给i行j列        t.remove(int(x[0]))  # 集合t去除刚才的赋值        if j < 9:            if calc(i, j + 1):                return True        elif i < 9:            if calc(i + 1, 0):                return True        else:            return True

我们来看看某次代码运行结果吧。(每运行一次结果不同)

小伙伴们可以试试,你是否可以得出10*10的矩阵,要求是行和列都不能相同。