哈工大(威海)算法实验一:分治算法实验大作业

该博客介绍了如何使用分治算法对大学各学院成绩数据进行排序和分析。首先,通过归并排序实现全校单科成绩排序,接着利用分治法找出全校各科目的前k名学生,最后提出算法找出同时在多科目中排名前k的学生。代码示例展示了如何读取Excel文件、实现归并排序、分治法找最大值以及获取各项成绩都优秀的名单。
摘要由CSDN通过智能技术生成

哈工大(威海)算法实验一:分治算法实验大作业

题目

某一高等院校有汽车学院、材料学院、计算机学院、软件学院;每个学院的一年级第一学期都开英语、高数、线代课程。每个学院每学期的成绩已经分别登录在同一个Excel文件的不同Sheet(表)中,没有排序,人数分别为,N1,N2,N3,N4
(2020年朱东杰老师的算法课,实验一大作业,仅供参考)
(具体的python jupyter notebook文件和数据Excel文件已经打包上传资源到CSDN了,链接:哈工大(威海) 算法设计与分析 朱东杰老师 实验一(分治算法实验)

任务

1.按单科成绩排序算法
2.输出在全校英语成绩前k名的学生名单(学院、学号、姓名、英语成绩)
3.输出在全校英语、高数、线代成绩都是前k名的学生名单(学院、学号、姓名、英语、高数、线代成绩)

题解

任务一:使用归并排序。首先将各个表中的数据读入到一个数组中(笔者使用python的openpyxl库来实现Excel的读取,并使用list来存储每个学生的信息),之后实现divide函数和merge函数。
使用递归法实现归并排序:
1.将序列每相邻两个数字进行归并操作,形成floor(n/2)个序列,排序后每个序列包含两个元素
2.将上述序列再次归并,形成floor(n/4)个序列,每个序列包含四个元素
3.重复步骤2,直到所有元素排序完毕
merge函数 (将子序列合并)

def merge(arr, left, right, flag):
    mid = int((right - left)/2 + left)
    temp = []
    i = left
    j = mid + 1
    while i < mid + 1 and j < right + 1: # 合并两个有序的子序列
        if arr[i][flag] < arr[j][flag]:
            temp.append(arr[j])
            j = j + 1
        else:
            temp.append(arr[i])
            i = i + 1
    while i < mid + 1:
        temp.append(arr[i])
        i = i + 1
    while j < right + 1:
        temp.append(arr[j])
        j = j + 1
    index = 0
    for k in range(left, right + 1): # 使用合并后的有序序列更新原序列
        arr[k] = temp[index]
        index = index + 1

divide函数 (将原序列拆分,并进行递归,调用merge函数)

def divide(arr, left, right, flag): # flag = 2英语; flag = 3高数; flag = 4代数
    if right - left < 1:
        return 0
    else:
        mid = int((right - left) / 2 + left)
        divide(arr, left, mid, flag)
        divide(arr, mid + 1, right, flag)
        merge(arr, left, right, flag)

任务二:使用分治法求最大值的算法。
传统的求序列中最大值和最小值的算法是很直接对序列进行扫描,需要2n-2次比较操作,使用分治的思想,可以优化到比较3n/2-2次比较操作
在这里插入图片描述

如上图,不断划分序列,直到每一小段只有两个元素,一个为此小序列的最大值,一个为此小序列的最大值,之后再将两个有序的小序列合并,不断重复此过程,只需要比较合并的两个序列的最大值和最小值,并不断更新这两个值即可。具体伪代码可参考下图:
在这里插入图片描述

findMax函数

def findMax(arr, left, right, flag): #分治法求最大值
    max_temp1 = [10001, '无名氏', 0, 0, 0, '未知学院'] #初始化
    max_temp2 = [10002, '无名氏', 0, 0, 0, '未知学院'] #初始化
    if left == right: 
        return arr[left]
    elif left == right - 1: #获得最大值,并返回
        if arr[left][flag] > arr[right][flag]:
            return arr[left]
        else:
            return arr[right]
    else:
        mid = (left + right) // 2
        max_temp1 = findMax(arr, left, mid, flag) #继续分段
        max_temp2 = findMax(arr, mid + 1, right, flag)
        if max_temp1[flag] > max_temp2[flag]: #比较左右两端哪个的最大值更大
            return max_temp1
        else:
            return max_temp2

在有了这个可以求得最大值的算法后,想要求得序列的前k大值,只需要每次使用这个算法,求得当前序列的最大值,之后从序列中删除这个最大值即可。
findKMax函数

def findKMax(arr, ans, k, flag): #求前K大个值
    for i in range(0, k):
        tempAns = findMax(arr, 0, len(arr) - 1, flag)
        ans.append(tempAns)
        arr.remove(tempAns)

任务三:要获得各项成绩都是前k名的学生名单,那么可以用一个set存储某项科目成绩前k名的学生姓名,之后在获得下一项科目成绩前k名的学生姓名名单后,新建一个临时的set,如果此名单中的姓名出现在了前一个set中,将名字添加到这个临时的set中去,之后使用临时的set的值更新原来的set的值,重复此过程即可获得各项成绩都是前k名的学生姓名集合。然后遍历一遍整个成绩表,寻找集合中的学生姓名对应的成绩信息即可。
getAllKMax函数

def getAllKMax(arr, k): #各项成绩都是前k名
    nameSet = set()
    for i in range(2, 5):
        tempSet = set() # 临时的集合
        ans = []
        arr_temp = arr[:]
        findKMax(arr_temp, ans, k, i)
        for j in range(0, len(ans)):
            if i == 2:
                nameSet.add(ans[j][1])
            else:
                if ans[j][1] in nameSet:
                    tempSet.add(ans[j][1])
        if i != 2:
            nameSet = tempSet
    return nameSet

代码

from openpyxl import load_workbook
import copy

workbook = load_workbook('2020算法实验1:分治算法实验.xlsx')

sheet_car = workbook['汽车学院']
sheet_material = workbook['材料学院']
sheet_computer = workbook['计算机学院']
sheet_software = workbook['软件学院']

data_car_temp = sheet_car['C5':'G11']
data_material_temp = sheet_material['C5':'G13']
data_computer_temp = sheet_computer['C5':'G11']
data_software_temp = sheet_software['C5':'G11']

def dataProcess(data_temp, data, flag):
    for i in range(0, len(data_temp)):
        temp = []
        for j in range(0, 5):
            temp.append(data_temp[i][j].value)
        if flag == 1:
            temp.append("汽车学院")
        if flag == 2:
            temp.append("材料学院")
        if flag == 3:
            temp.append("计算机学院")
        if flag == 4:
            temp.append("软件学院")
        data.append(temp)

data_all = []
dataProcess(data_car_temp, data_all, 1)
dataProcess(data_material_temp, data_all, 2)
dataProcess(data_computer_temp, data_all, 3)
dataProcess(data_software_temp, data_all, 4)

def merge(arr, left, right, flag):
    mid = int((right - left)/2 + left)
    temp = []
    i = left
    j = mid + 1
    while i < mid + 1 and j < right + 1:
        if arr[i][flag] < arr[j][flag]:
            temp.append(arr[j])
            j = j + 1
        else:
            temp.append(arr[i])
            i = i + 1
    while i < mid + 1:
        temp.append(arr[i])
        i = i + 1
    while j < right + 1:
        temp.append(arr[j])
        j = j + 1
    index = 0
    for k in range(left, right + 1):
        arr[k] = temp[index]
        index = index + 1

def divide(arr, left, right, flag): # flag = 2英语; flag = 3高数; flag = 4代数
    if right - left < 1:
        return 0
    else:
        mid = int((right - left) / 2 + left)
        divide(arr, left, mid, flag)
        divide(arr, mid + 1, right, flag)
        merge(arr, left, right, flag)

# 打印降序排序后的单科成绩名单
divide(data_all, 0, len(data_all) - 1, 2)
print(data_all)

def findMax(arr, left, right, flag): #分治法求最大值
    max_temp1 = [10001, '无名氏', 0, 0, 0, '未知学院']
    max_temp2 = [10002, '无名氏', 0, 0, 0, '未知学院']
    if left == right:
        return arr[left]
    elif left == right - 1:
        if arr[left][flag] > arr[right][flag]:
            return arr[left]
        else:
            return arr[right]
    else:
        mid = (left + right) // 2
        max_temp1 = findMax(arr, left, mid, flag)
        max_temp2 = findMax(arr, mid + 1, right, flag)
        if max_temp1[flag] > max_temp2[flag]:
            return max_temp1
        else:
            return max_temp2

def findKMax(arr, ans, k, flag): #求前K大个值
    for i in range(0, k):
        tempAns = findMax(arr, 0, len(arr) - 1, flag)
        ans.append(tempAns)
        arr.remove(tempAns)

# 打印单项成绩是前k名的学生名单
data_temp = data_all[:]
ans = []
findKMax(data_temp, ans, 5, 2)
for i in range(len(ans)):
    print(ans[i][0],ans[i][1],ans[i][2],ans[i][5])

def getAllKMax(arr, k): #各项成绩都是前k名
    nameSet = set()
    for i in range(2, 5):
        tempSet = set()
        ans = []
        arr_temp = arr[:]
        findKMax(arr_temp, ans, k, i)
        for j in range(0, len(ans)):
            if i == 2:
                nameSet.add(ans[j][1])
            else:
                if ans[j][1] in nameSet:
                    tempSet.add(ans[j][1])
        if i != 2:
            nameSet = tempSet
    return nameSet

# 打印各项成绩都是前k名的学生名单
data_temp = data_all[:]
nameSet = getAllKMax(data_temp, 5)
for i in range(0, len(data_all)):
    if data_all[i][1] in nameSet:
        print(data_all[i])
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值