Python——lesson

Python——lesson

学习的笔记,不定时更新

基本常识

  1. py中一切皆为对象;因此当我们创建一个变量时,就是给出了一个指向数据空间的对象,即变量是指向对象的引用。同理,一个对象可以起名为不同的变量(引用)

    1. 对象包括,id(识别用首地址),type(数据类型), value(值)

    2. 三者都有对应的函数,如id(), type()

    3. 赋值的意义因此就变成了不改变内容位置的,’贴标签‘

    4. 基于三,py存在垃圾回收机制,即为对于没有标签的空间需要回收。(于变量被赋值时往往发生)

  2. ,py解释器的主要作用----用于动态类型检查(既不是编译,也不是管理文件)

  3. 文件默认扩展名.py

  4. 外部运行文件,如app.py时,直接python app.py

  5. 版本检查 python -v || python -version

  6. pip用于管理python的包

  7. 虚拟环境在python中的意义时管理依赖(这点不同于c++的平台剥离)

基本语法函数

print于input()

print(*object, sep='', end='\n', file=sys.stdout, flush=False)
#本质在于对象输出,每个对象输出即为  对象,对象
#即为,行末自动换行,区分默认sep为',', 
#通过修改对应的对象来实现,如end=' '即可取消换行。用空格代替
#fils用于控制是否要输出到文件
#flush用于判定是否刷新缓冲区

print(1, 2, 3, 4, sep=',')#默认为,切分,输出时就没有,

#全角与半角
# 补充代码,第一行接收一个人名的输入,后面两行中依次输出“xxx 你好!”和“xxx ,你好!”
user_name=input()
print(user_name, '你好!')
print(user_name, ',你好!', end=' ')

#格式化输出
#模板
print('{}输出{}继续输出'.format{第一个对象,第二个对象})
print(f '{user_name}你好')#f定义为格式化输出,

user_name=input()
user_ch=input()
print('{}是{}'.format(user_name, user_ch) )#而且能去除空格

user_name=input()
div=input()
print(user_name,'你好!', sep=div)


print("%d / %d = %f" % (a, b, a/b));#末尾的分号可以表示本句结束


#3,循环控制
# 在下面空行输入一个符号
end_mark=input()
for poet in ['李白', '王维', '王勃', '白居易', '杜甫']:
    # 在下一行对齐此位置写输出语句,加参数使输出时不换行,每个输出后用第2行输入的符号结尾
    print(poet, end=end_mark)#这里的屁股还有个','
----- 
str=input('提示语句')#显然这里也可以用。
'''
多行注释
PE8
my_transform

大驼峰--多用于类声明
HelloWorld
小驼峰
helloWorld

冲突避免
class_

PE8注释
'''

##十六进制输出
a=int(number)
print(hex(a))
对于input为字符串对象的认识不清:
在用input处理各种输入的时候,务必需要注意的是对于其类型的小心,尤其是当需要其参与运算时;
score_f=input();
score_x=eval(input());
score=int()
dic={'A':4.0, 'A-':3.7, 'B+':3.3, 'B':3.0, 'B-':2.7, 'C+':2.3, 'C':2.0, 'C-':1.5, 'D':1.3, 'D-':1.0, 'F':0}
base=score_x
while(True):

    #print(score_x, dic[score_f])
    score+=int(score_x) * dic[score_f]
    score_f=input();
    #print(score_f)
    if(score_f != "-1"):
        score_x=input();
        base+=int(score_x)
    else:
        break;

print(f'{round(score/base, 2):.2f}')#这里为严格两位输出。
总体来说大概有几个知识
  1. 基本输出,输出(一般返回str,减少类型讨论),包括常量,变量

  2. 模式输出,包括换行,空格,分割替换

  3. 循环控制

一些可迭代对象:

string:一些常用的组合对象:

In [8]: import string

In [9]: dir(string)

In [10]: string.ascii_letters
Out[10]: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'

In [11]: string.ascii_lowercase
Out[11]: 'abcdefghijklmnopqrstuvwxyz'

In [12]: string.ascii_uppercase
Out[12]: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'

In [13]: string.digits
Out[13]: '0123456789'

In [14]: string.punctuation
Out[14]: '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'

In [15]: string.whitespace
Out[15]: '\t\n\x0b\x0c\r '

set:集合

#生成
a = set()#生成空集
a = set(iterable) 

s='avcd'
a=set(s)#{'a', 'v', 'c', 'd'}根据迭代对象的基本要素分割


#增加;
a.add(iter)
c=a.copy(a)

#不定向删除
a.clear()#全清楚,保留集合

a.pop()#随机弹出,如果没有对象了,且没有给默认值,那就报错
#定向
a.remove(item);#没对象报错,因为通用
a.discard(item);#没对象不报错
#|并, &交, -差, ^对称


#比较复杂的结合文件读取的实例
#这里表现了set()不能实现对set(list(list))的转化,因为不可哈希。

import numpy as np
# 设置常量,对应各列数据的语义,方便索引  
HIGH = 0
LOW = 1
OPEN = 2
CLOSE = 3
VOLUME = 4

def statistics_of_all(code_list):
    """  
    @参数 code_list:股票代码列表,列表类型  
    接收股票数据文件名列表,逐个统计各股票数据文件涨跌幅、总成交量、最高价和最低价。  
    涨跌幅计算公式为:(最新记录收盘价-最早记录收盘价) / 最早记录收盘价 * 100  
    为方便处理,读入数据时,略过日期列。  
    """
    statistics_of_stock = []#这种读取数据的方法确实创行,且动用了列名称。
    for code in code_list:
        data_of_code = np.genfromtxt('datas/' + code, dtype=None,
                                     usecols=[1, 2, 3, 4, 5, 6], delimiter=',',
                                     skip_header=1)
        # 计算当前股票涨跌幅、总成交量、最高价和最低价  
        uplift_or_fall = round((data_of_code[:, CLOSE][-1] - data_of_code[:, CLOSE][0]) / data_of_code[:, CLOSE][0] * 100, 2)
        volumes = round(sum(data_of_code[:, VOLUME]), 2)
        statistics_of_stock.append([code[:6], uplift_or_fall, volumes])
    return statistics_of_stock  # 每支股票涨跌幅、总成交量、最高价和最低价

def top_10_uplift(statistics_of_stock):
    """  
    @参数 statistics_of_stock:每支股票涨跌幅、总成交量、最高价和最低价统计信息,列表类型  
    按涨幅降序排序,涨幅相同时按股票代码降序排序,取排名前10的股票,  
    返回排名前10的股票代码,返回值为列表类型。  
    """
    # 补充你的代码
    saveList=sorted(statistics_of_stock,key=lambda x:(x[1], x[0]), reverse=True)
    list1=list();
    for i in saveList:
        list1.append(i[0])
    return list1[:10]
def top_10_volumes(statistics_of_stock):
    """  
    @参数 statistics_of_stock:每支股票涨跌幅、总成交量、最高价和最低价统计信息,列表类型  
    按成交量降序排序,成交量相同时,按股票代码降序排序,取成交量前10的股票代码,返回成交量  
    最大的10支股票代码列表。  
    """
    # 补充你的代码
    saveList=sorted(statistics_of_stock,key=lambda x:(x[2], x[0]), reverse=True)
    list1=list();
    for i in saveList:
        list1.append(i[0])
    return list1[:10]

def uplift_and_volumes(top_uplift, top_volumes):
    """
    @参数 top_high,最高价在前10名的股票代码,字符串
    @参数 top_volumes,成交量在前10名的股票代码,字符串
    返回一个列表,其元素依序为以下4个:
    涨幅和成交量均在前10名的股票,按股票代码升序,列表
    涨幅或成交量在前10名的股票,按股票代码升序,列表
    涨幅前10名,但成交量未进前10名的股票,按股票代码升序,列表
    涨幅和成交量不同时在前10名的股票,按股票代码升序,列表
    """
    # 补充你的代码
    listAll=list(list())

    set_top=set(top_uplift)
    set_volumes=set(top_volumes);
   #

    listAll.append(list(sorted(set_top & set_volumes)));
    listAll.append(list(sorted(set_top | set_volumes)));
    listAll.append(list(sorted(set_top - set_volumes)));
    listAll.append(list(sorted(set_top ^ set_volumes)));
    return listAll

def operation():
    """接收一个字符串为参数,根据参数值调用不同函数完成任务"""
    statistics_of_list = statistics_of_all(stock_lst)  # 对获取的股票数据进行统计  
    uplift_set = top_10_uplift(statistics_of_list)  # 涨幅前10名集合
    volumes_set = top_10_volumes(statistics_of_list)  # 成交量前10名集合
    u_and_v = uplift_and_volumes(uplift_set, volumes_set)
    opt = input()
    if opt == '涨幅与成交量':
        print('涨幅和成交量均在前10名的股票:')
        print(u_and_v[0])  # 涨幅和成交量均在前10名的股票
        print('涨幅或成交量在前10名的股票:')
        print(u_and_v[1])  # 涨幅或成交量在前10名的股票
        print('涨幅前10名,但成交量未进前10名的股票:')
        print(u_and_v[2])  # 涨幅前10名,但成交量未进前10名的股票
        print('涨幅和成交量不同时在前10名的股票:')
        print(u_and_v[3])  # 涨幅和成交量均在前10名的股票
    else:
        print('输入错误')
if __name__ == '__main__':
    filename = 'datas/沪市股票top300.csv'              # 股票名称与代码文件
    stock_lst = ['600000.csv', '600004.csv', '600006.csv',
                 '600007.csv', '600008.csv', '600009.csv',
                 '600010.csv', '600011.csv', '600012.csv',
                 '600015.csv', '600016.csv', '600018.csv',
                 '600019.csv', '600020.csv', '600026.csv',
                 '600028.csv', '600029.csv', '600030.csv',
                 '600031.csv', '600033.csv', '600036.csv']
    operation()

dict:字典

dict1={'key':'value', }

dict1={};
dirt={}
for i in zip(list1, list2):

    #zip将两个队中的的对应项形成元组
    dirt[i[0]]=i[1]


#增加
dictname[newKey]=newValue

dict1.update(dict2)#extend



#删

dictname.clear()

dictname.pop(key)

dictname.popItem()#return (,)

del dictname[key]



#查

what=dictName[key]

dictName.get(key,default)

list_=dictName.keys()

        dictName.values()

        dictName.items()

#遍历
dict1 = {'赵小明': '13299887777', '特明朗': '814666888', '普希京': '522888666', '吴小京': '13999887777'}
name = input()
num = input()

if name in dict1:
    print("您输入的姓名在通讯录中已存在")
else:
    dict1[name]=num;
    for i in dict1.items():
        print(f'{i[0]}:{i[1]}')#这里才知道,返回的i是tuple对象
应用:密码转换
def morse_code_encryption(txt):
    """接收明文字符串为参数,返回用摩斯密码加密后的字符串。"""
    # 补充你的代码
    ls = [".-","-...","-.-.","-..",".","..-.","--.","....","..",".---","-.-",".-..","--","-.","---",".--.","--.-",".-.","...","-","..-","...-",".--","-..-","-.--","--.."]
    ts="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    dirt={}

    for i in zip(ls, ts):
        dirt[i[1]]=i[0]
    #print(dirt)
    ans=str()
    for i in txt.upper():
        if i in dirt:
            ans=ans+dirt[i]
        else:
            ans=ans+i;
    return ans;

if __name__ == '__main__':
    plaintext = input().lower()              # 输入一个字符串并转为小写
    print(morse_code_encryption(plaintext))  # 调用函数,并输出返回值
易错:在多队列的组合字典中:
n = eval(input())
name=input()
nameList=name.split(',')
numList=input().split(sep=',')

dic=dict();
for i in zip(nameList, numList):#############
    dic[i[0]]=i[1]


oplist=['add', 'print', 'del', 'update', 'value', 'key', 'clear'];
for i in range(n):
    op=input().split()
    idx=oplist.index(op[0])
    if idx==0:
        dic[op[1]]=op[2];
    elif idx==1:
        print(dic);
    elif idx==2:
        if op[1] in dic:
            del dic[op[1]]
        else:
            print("键不存在")
    elif idx==3:
        dic[op[1]]=op[2];
    elif idx==4:
        print(list(dic.values()))
    elif idx==5:
        print(list(dic.keys()))
    else:
        dic.clear()

list:队列

  队列,我认为时py中最最最方便也最最最能体现py性质的对象了。
string
print(''.join(sorted(list(set(string)))))#超级嵌套


s=input()#


s_list=s.strip().split(',')
s_set=set(s_list)
s_new=list(s_set)
print(sorted(s_new, key=s_list.index))#这个确实让我震惊了,还可以这样?


##list查找
tag='a'
list1=list()
if tag in list1:
   jjj

list1.count(tag)

idx=list1.index(tag, st, ed)

在input的时候,如果后面接的是用空格分割,可以直接当数组用

n = eval(input())
list1=input().strip().split()##
MySet=set(list1)


for i in range(n):
    op=input()
    oj=op.split();


    if(oj[0] == 'add'):
        MySet.add(oj[1])
    if(oj[0] == 'print'):
        tlist=list(MySet)
        tlist.sort()
        print(tlist);
    if(oj[0] == 'del'):
        MySet.discard(oj[1])
    if(oj[0] == 'update'):
        tlist=oj[1:]
        #print(tlist,'aaaa')
        MySet=MySet | set(tlist)
    if(oj[0]=='clear'):
        MySet.clear()

拷贝:在运用迭代对象中的一些问题:

n= eval(input())
dic = dict()
ldc=list()
for i in range(n):
    tg=input()
    lg=tg.split()
    dic['name']=lg[0];
    dic['age']=int(lg[1]);
    ldc.append(dic.copy());#这里,如果没有用拷贝的话,list里面所有的字典都会一样被最后一个覆盖

print(sorted(ldc, key=lambda x:x['age']))
print(sorted(ldc, key=lambda x:x['name']))

collections类下的各种对象

元组类:

一种比较特殊的对象,如下

import collections

def CreatePoint():
    # ********** Begin ********** #
    Point = collections.namedtuple('Point', 'x, y')
    p=Point(x=0, y=0)
    return p;
    # ********** End ********** #
def IncX(p):
    # ********** Begin ********** #
    p=p._replace(x=p.x+1)##尤其注意,这里的下划线,以及替换方式,不能x=x+1
                            ##同时,注意返回新的对象的问题。
    return p
    # ********** End ********** #
def IncY(p):
    # ********** Begin ********** #
    p=p._replace(y=p.y+1)
    return p;
    # ********** End ********** #
def PrintPoint(p):
    print("当前位置:x = %d,y = %d" % p)

Counter字典对象

这个用于记录对象的出现次数;

import collections
def Func():
    '''
    将偶数行的数据加到计数器中;
    将奇数行的数据从计数器中减去。
    '''
    c = collections.Counter()#本质是生成字典记录每个对象的次数,但可以容纳报错
    for i in range(6):
        data = input()
        # ********** Begin ********** #
        if(i%2==0):
            c.update(data)
        else:
            c.subtract(data)
        # ********** End ********** #
    print(c.most_common())

deque双向队列

import collections
def Func():
    d = collections.deque()##定义双向队列
    n = int(input())
    # ********** Begin ********** #
    #双向队列更类似于list,有pop和appent,默认是右边增减
    #显然,只用一边就是纯粹的栈,还是双向栈,或者也可以用当队列,非常方便
    #extend 123
    if(n>0):
        d.append(0)
        mark=1;
        for i in range(1,n):
            if mark==1:
                d.appendleft(i);
            else:
                d.append(i);
            mark*=-1;

    # ********** End ********** #
    print(d)

'''
d1 = collections.deque()
d1.extend("123")  # di  1 2 3
print(d1)
d1 = collections.deque()
d1.extendleft("123") # d1  3 2 1
print(d1)
'''

OrderedDict有序字典

基本与字典相同,但是保留插入顺序,因此可以排序。例如菜单可以按照价格排序。

其创建也是通过对可迭代对象的迭代实现的

基于元组的字典建立

data = [('a',1),('b',3),('c',2)]
od = collections.OrderedDict(sorted(data,key=lambda s:s[0]))#按数据中key值的大小排序
print(od)
od = collections.OrderedDict(sorted(data,key=lambda s:s[1]))#按数据中value值的大小排序
print(od) 

#不能直接比较的时候,需要利用lambda实现控制对象


#移动
dt = collections.OrderedDict()
dt['a'] = 0
dt['b'] = 1
dt['c'] = 2
dt.move_to_end('b',last = False) #将`b`键值对移动到最前方
print(dt)# 注意,这里是直接通过key来实现了键的移动。换句话说,字典默认键比较
dt.move_to_end('b',last = True) #将`b`键值对移动到最后方
print(dt)



##import collections
def Func():
    pairs = []
    n = int(input())
    for s in range(n):
        k = input()
    # ********** Begin ********** #
        pairs.append((k,s));#奇怪的是这个字典的子对象是元组组层的数组
    od = collections.OrderedDict(sorted(pairs, key=lambda x:x[0]))
    # ********** End ********** #
    print(od)

defaultdict默认字典

本质依旧是通过字典函数生成字典,唯一的区别在于,其在无参数的时候生成的默认值是不同的,
dd = collections.defaultdict(int) #使用int作为工厂函数
print(dd['a']) #访问不存在的key:'a'
dd = collections.defaultdict(tuple) #使用tuple作为工厂函数
print(dd['a']) #访问不存在的key:'a'
dd = collections.defaultdict(str) #使用str作为工厂函数
print(dd['a']) #访问不存在的key:'a'
class Test:
    def __init__(self,name): #只有一个构造函数,而且它有一个参数
        print("init")
dd = collections.defaultdict(Test) #使用自定义类型Test作为工厂函数
print(dd['a']) #运行到这里就会出现异常,原因是Test类没有无参的构造函数 

#这里其实可以看出,int tuple, str也是一种类,其初始函数如上。

文件阅读

import string
# 读文件,返回字符串  
def read_file(file):  
    # 补充你的代码
    with open(file, 'r', encoding='utf-8') as f:
        return f.read();

#读文件,返回行列表


with open('info.csv', 'r', encoding='utf-8') as f:
    ls=f.readlines();

car=input();
if car=='A':
    for i in ls:
        print(i);
elif car=='D':
    print(ls);
else:
    print('ERROR')

# 返回大写字母、小写字母、数字、空格和其他字符的数量  
def classify_char(txt):  
    # 补充你的代码
    up, lw, dig, spa, oth=0, 0, 0, 0, 0;
    for ch in txt:
        if(ch.islower()):
            lw+=1;
        elif ch.isupper():
            up+=1;
        elif ch.isnumeric():
            dig+=1;
        elif ch.isspace():
            spa+=1;
        else:
            oth+=1;
    return up, lw, dig, spa, oth


#返回列表,尤其是对于csv文件:
with open('./university.csv','r',encoding='utf-8') as Uname:
    ls = Uname.readlines()#####

s=input()
for i in ls:
    if s in i:
        print(ls[0].replace('\n', ''))
        print(i.replace('\n', ''))
        break;


if __name__ == '__main__':  
    filename = 'mayun.txt'  # 读取的文件名  
    text = read_file(filename)  # text为字符串  
    print(*classify_char(text))  

更高级的映射表(加密等):

import string
# 读文件,返回字符串  
def read_file(file):  
    with open(file, 'r', encoding='utf-8') as f:  
        return f.read()

# 
def offset_cal(day):  
    """用字符串中字符ASCII值的和对26取模为偏移量  """
    sum_of_ch = 0  
    for c in day:  
        sum_of_ch = sum_of_ch + ord(c)  
    offset = sum_of_ch % 26  
    return offset

def kaisa(txt, number):  
    # 补充你的代码
    lower = string.ascii_lowercase;
    upper = string.ascii_uppercase;
    before = string.ascii_letters;
    after = lower[number:] + lower[:number]+ upper[number:] + upper[:number]
    table = ''.maketrans(before, after);########
    return txt.translate(table)############

if __name__ == '__main__':  
    filename = 'mayun.txt'  # 读取的文件名  
    text = read_file(filename)  # text为字符串  
    secret_word = input()  
    offset_number = offset_cal(secret_word)  
    print(kaisa(text, offset_number))  

读取json文件:

import json  

#load
# 打开文件并读取内容  
with open('data.json', 'r', encoding='utf-8') as file:  
    # 使用json.load()方法解析JSON数据  
    data = json.load(file)  

# 打印解析后的Python对象  
print(data)#本质是变成了字典?
print(data['name'])  # 提取name字段的值  
print(data['age'])   # 提取age字段的值



#loads:与load()方法不同,loads()方法用于将JSON格式的字符串解析为Python对象。
#如果你已经将JSON文件的内容读取为一个字符串,那么可以使用这个方法。

#pandas----相较于是为列表字典的load, pandas允许按照列搜索通过其特有的对象
import pandas as pd  

# 使用pandas的read_json()方法读取JSON文件  
df = pd.read_json('data.json')  

# 打印DataFrame对象  
print(df)  

# 提取特定列的值  
names = df['name']  
ages = df['age']  

print(names)  
print(ages)

读取CSV文件:

最容易犯的错误是,在对表格内数据进行处理时,由于没有跳过第一行,导致数据经常会无法分割或者函数使用失败等问题。
"""
任务描述
admit2.csv
本题附件包含500名国际高校的研究生申请人的相关信息和预测的录取概率数据。
下表为文件中字段及对应含义:

Serial No	GRE Score	TOEFL Score	University Rating	SOP	LOR	CGPA	Research	Chance of Admit
编号1-500	GRE分数	托福分数	本科大学排名分	个人陈述分数	推荐信分数	本科绩点	研究经历(1/0)	录取概率(0-1之间)
研究经历:1代表有,0代表无

录取概率:0-1之间的小数,如0.73代表73%
请按照下列要求对文件中数据进行统计和分析,并严格按照下面所示格式输出结果。
(描述中示例仅为格式示例,数据与测试用例无关)

输入一个数据n

1:如果n为'1',抽取数据中录取概率大于等于80%的记录,计算其中大学排名评分大于等于4分的百分比,程序结束。

1
Top University in >=80%:11.11%
2:如果n为'Research',分别统计和输出录取概率大于等于90%的学生和录取概率小于等于70%的学生中,有研究经历的学生占比,程序结束。(百分比保留两位小数)

Research
Research in >=90%:91.03%
Research in <=70%:22.10%
3:如果n为'2',输出录取概率大于等于80%的学生中TOEFL分数的平均分,最高分和最低分,程序结束。(保留两位小数)

2
TOEFL Average Score:300.12
TOEFL Max Score:323.00
TOEFL Min Score:299.00
4:如果n为'3',输出录取概率大于等于80%的学生中绩点的平均分,最高分和最低分,程序结束。(保留三位小数)

3
CGPA Average Score:4.333
CGPA Max Score:4.910
CGPA Min Score:4.134
5:如果非以上输入,则输出'ERROR',程序结束。"
tlist=list()
with open('step9/admi"""
t2.csv', 'r', encoding='utf-8') as f:
    for i in f:
        tlist.append(i.strip().split(','))

op = input()
#print(tlist)
if op=='1':
    tempL=list()
    for i in tlist[1:]:
        if eval(i[-1])>=0.8:
            tempL.append(i);
   # print(tempL)
    cur=0
    for j in tempL:
        if eval(j[1]) >=4:
           # print(j)
            cur+=1;
    print(f"Top University in >=80%:{round(cur/len(tempL), 4)*100}%")
elif op=='2':
    tempL=list()
    for i in tlist[1:]:
        if eval(i[-1])>=0.8:
            tempL.append(i);
    total=0;
    maxn=0;
    minn=10000;
    for i in tempL:
        now=eval(i[3])
        total+=now;
        if(now>maxn):
            maxn=now
        elif now<minn:
            minn=now;
    print(f"TOEFL Average Score:{round(total/len(tempL), 2)}")
    print(f"TOEFL Max Score:{maxn:.2f}")
    print(f"TOEFL Min Score:{minn:.2f}")
elif op=='Research':
    tempL=list()
    for i in tlist[1:]:
        if eval(i[-1])>=0.9:
            tempL.append(i);
    cur=0;
    for i in tempL:
        if eval(i[-4])==1:
            cur+=1;
    print(f"Research in >=90%:{round(cur/len(tempL)*100, 2):.2f}%")
    
    tempL.clear()
    for i in tlist[1:]:
        if eval(i[-1])<=0.7:
            tempL.append(i);
    cur=0;
    for i in tempL:
        if eval(i[-4])==1:
            cur+=1;
    print(f"Research in <=70%:{round(cur/len(tempL)*100, 2):.2f}%")
elif op=='3':
    tempL=list()
    for i in tlist[1:]:
        if eval(i[-1])>=0.8:
            tempL.append(i);
    total=0;
    maxn=float(0);
    minn=float(10000);
    l=list()
    for i in tempL:
        now=eval(i[4])
        total+=now;
        l.append(float(i[4]))
    print(f"CGPA Average Score:{round(total/len(tempL),3):.3f}")
    print(f"CGPA Max Score:{max(l):.3f}")    
    print(f"CGPA Min Score:{min(l):.3f}")
else:
    print("ERROR")   


爬虫基础

url原理

爬虫的基本流程

1.发起请求
通过HTTP库向目标站点发起请求,也就是发送一个Request,请求可以包含额外的header等信息,等待服务器响应

2.获取响应内容
如果服务器能正常响应,会得到一个Response,Response的内容便是所要获取的页面内容,类型可能是HTML,Json字符串,二进制数据(图片或者视频)等类型

3.解析内容
得到的内容可能是HTML,可以用正则表达式,页面解析库进行解析,可能是Json,可以直接转换为Json对象解析,可能是二进制数据,可以做保存或者进一步的处理

4.保存数据
保存形式多样,可以存为文本,也可以保存到数据库,或者保存特定格式的文件

当然,这需要基于一定的工具库来实现,其中最基础的库就是request。

# coding=utf-8
import requests
def getHtml(url):
    try:
        r = requests.get(url,timeout=30)
        r.raise_for_status()  #如果状态不是200,引发异常
        r.encoding = 'utf-8'   #使用utf-8
        return r.text       
url = 'http://www.baidu.com'
print(getHtml(url))

数学建模

方程求解

非线性方程组求解scipy.linalg.solve

非齐次线性方程组求解(系数矩阵求解)AX=B
import numpy as np
for scipy.linalg import solve;

a=np.array([[3,1,-2],[1,-1,4],[2,0,3]])
b=np.array([5, -1, 2, 5])
x=solve(a, b)
print(x);

#很显然,关键在于scipy中的solve函数,前系数矩阵,后常数标量。解法正宗的三角法


#特别要注意的是,系数对应的x是对应的同位在构建的时候,因为返回的解向量也是对应位置的

代数方程构建与求解 solve()

一般来说,代数方程不能出现在代码中,但是通过sympy能实现对变量的定义,然后通过py内置的solve就能解决方程问题。
from sympy import *
x= symbols('x')
print(solve(x*2-4,x)) #这里的solve应该与上面的solve不同,是py的内置函数

#这里尤其需要注意,sympy内嵌在py,貌似是不用导包的
#第一个参数为要解的方程,要求右端等于 0 ,第二个参数为要解的未知数.这意味着要规范化方程
#为显示状态,这意味着隐式方程(如圆)不能求解。


###二元一次方程
from sympy import *
x,y= symbols('x,y')
print(solve([2*x-y-3,3*x+y-7],[x,y]))##三个要点,定义变量,规范化方程,传入参数
# 如果返回I,表示虚数;

### 参数方程
import sympy as syp;
x,a=syp.symbols("x,a");
res=solve(x**2+a**2, x);##表示解对象即为x,换句话说就是要去掉x表示
print(res);
for temp in res:#遍历所有解形式
    print(temp.subs([(a, 2)]))#temp.subs(注意s)表示替换解内的对象,
                              # 替换列表为[(a, 2)...],其中a为替换对象,
                              #2为对象值。共有一个对象

###
from sympy import *
a1,b1,c1,d1,a2,b2,c2,d2=symbols('a1,b1,c1,d1,a2,b2,c2,d2')
x0,x1,x2,y0,y1,y2=symbols('x0,x1,x2,y0,y1,y2')
result=solve([a1+b1*x0+c1*x0*x0+d1*x0*x0*x0-y0,
    a1+b1*x1+c1*x1*x1+d1*x1*x1*x1-y1,
    a2+b2*x1+c2*x1*x1+d2*x1*x1*x1-y1,
    a2+b2*x2+c2*x2*x2+d2*x2*x2*x2-y2,
    b1+2*c1*x1+3*d1*x1*x1-b2-2*c2*x1-3*d2*x1*x1,
    c1+3*d1*x1-c2-3*d2*x1,
    c1+3*d1*x0,
    c2+3*d2*x2],[a1,b1,c1,d1,a2,b2,c2,d2])
#设置参数
cs=[(x0,6),(x1,9),(x2,12),(y0,0),(y1,3),(y2,0)]
# print(result[a1])
# print(result[b1])
# print(result[c1])
# print(result[d1])
# print(result[a2])
# print(result[b2])
# print(result[c2])
# print(result[d2])
a1=result[a1].subs(cs)
b1=result[b1].subs(cs)
c1=result[c1].subs(cs)
d1=result[d1].subs(cs)
a2=result[a2].subs(cs)
b2=result[b2].subs(cs)
c2=result[c2].subs(cs)
d2=result[d2].subs(cs)
print(a1)
print(b1)
print(c1)
print(d1)
print(a2)
print(b2)
print(c2)
print(d2)

常微分方程sympy.dsolve

所谓常微分方程,是指某变量偏导所等价的含变量方程以及对等的常熟

dx/dt = x*\*2+x+1 = 1类似如此,显然需要积分求解。

未知函数是一元函数的微分方程称作常微分方程,由一个或多个常微分方程组成的方程组叫做常微分方程组。 具体在这题就是,x都是唯一由t构成的函数。

如,$x1+x2-x3=4=dx1/dt$ 显然解不了,需要联立方程组。

预览大图

小结重点:

微分方程至少又几个要求

1,微分表达式(显性的)

2,对应变量的特定解。

原理,本质是是想要求解得出一个x?(t)的一个表达式,具体来说就是一条约束曲线。

1每一个微分表达式实现的是对变量的空间维度还原,只有还原三个变量的空间形式,才能求解(也就是三个微分表达式)

2,每一个特定变量解,就是在确定在对变量的空间还原后,到底是哪个具体的线(显然的,对于一次的线,只需要一个点就能标定),其线的方向由其积分表达式所约束。

这件事告诉我们,在思考微分方程时,一方面要考虑变量的数量,确定方程量。另一方面需要注意方程的特定解。(这里我们大胆猜测,如果时二级求导,两个一级求导的方程,显然那个二级求导的变量需要更多的点来锚定,因为二阶-》一阶时首先就需要确定其一阶空间形式,不然时不能还原的。所有必定需要增加锚定点,估计是一阶导的锚定点)

import sympy as sp;
#解析解
t = sp.symbols("t");
x1, x2, x3=sp.symbols("x1, x2, x3", cls=sp.Function);#设定变量为函数对象,使得其可以被求导
eq = [x1(t).diff(t)-2*x1(t)+3*x2(t)-3*x3(t),
      x2(t).diff(t)-4*x1(t)+5*x2(t)-3*x3(t),
      x3(t).diff(t)-4*x1(t)+4*x2(t)-2*x3(t)]
con ={x1(0):1, x2(0):2, x3(0):3}#字典带入,有点特色
s=sp.dsolve(eq, ics=con)#d表示微分类, eq表示微分方程组(右边为0),ics为函数取值字典
print(s)



##另一种解法,数值解求法。
import scipy.integrate as spi;
import numpy as np;
dy=lambda y, x:-2*y+x**2+2*x #这里y, x 位置不能变,原因在于后面的odeint
x=np.arange(1, 10.5, 0.5)#表示每隔0.5取一个点  这里需要注意的是,必须是np内的arr矩阵才能放到odeint 
sol=spi.odeint(dy, 2, x)#默认开头第一个x对应的情况下,即为:y(1)=2, 
                    #这里的位置有意思在于,与上面lambda是对应的
print("x={}\n对应数值解为y={}".format(x, sol.T))# 这里的表现在odeint返回了一组解矩阵


#这个从另一个方面更深刻的解释了odeint,odeint本质是默认了因变量为导数
from scipy.integrate import odeint
import numpy as np
import matplotlib.pyplot as plt 
import math;
def dy_dt(y,t):
########## begin ##########
    return -2*y+x**2+2*x
##########  end  ##########
y0=[1]
t = np.arange(-10,10,0.01)
y=odeint(dy_dt,y0,t)

plt.plot(t, y)
plt.title("picture")
plt.show()

二阶常系数齐次微分方程

即为内涵f(x)的函数,二阶导与0的关系式子;f(x)′′+ω**2 \* f(x)=0,显然,原函数结果是一个需要两个系数c1, c2的含ω的的f(x)函数
方法1, 解析解,symbol的dsolve求单二阶方程
import sympy as sy
#符号定义与函数定义
x = sy.symbols("x")
omega = sy.symbols("w");
f=sy.Function("f")

#方程构建
equation = f(x).diff(x,2)+omega**2 *f(x)
print(sy.dsolve(equation, f(x)))#返回值实现,方程与求解对象。
sy.pprint(sy.dsolve(equation, f(x)))##sy自带

'''
Eq(f(x), C1*exp(-I*w*x) + C2*exp(I*w*x))
           -ⅈ⋅w⋅x       ⅈ⋅w⋅x
f(x) = C₁⋅ℯ       + C₂⋅ℯ  
''' 
方法2,解析解,sympy的solve求解一阶方程组(多元)

类似的方法,两个方程族,一阶,每个方程关系需要一个点锚定,需要每个函数一个初值点。不然就只能得到函数族了。

import sympy as sy
t= sy.symbols("t")
x= sy.Function("x")
y= sy.Function("y")

equation= x(t).diff(t, 1) - 3*x(t) +2*y(t)
equation2=y(t).diff(t, 1) - 2*x(t)+ y(t)
print(sy.dsolve([equation, equation2], [x(t), y(t)]))
sy.pprint(sy.solve([equation, equation2], [x(t), y(t)]))

'''
[Eq(x(t), 2*C1*t*exp(t) + (C1 + 2*C2)*exp(t)), Eq(y(t), 2*C1*t*exp(t) + 2*C2*exp(t))]
⎧        d            d                   d            d       ⎫
⎨x(t): - ──(x(t)) + 2⋅──(y(t)), y(t): - 2⋅──(x(t)) + 3⋅──(y(t))⎬
⎩        dt           dt                  dt           dt    

这一题没有给常数解
'''


import sympy as sp
t = sp.symbols('t')
x1,x2,x3 = sp.symbols('x1,x2,x3', cls=sp.Function)
eq = [x1(t).diff(t)-2*x1(t)+3*x2(t)-3*x3(t),
      x2(t).diff(t)-4*x1(t)+5*x2(t)-3*x3(t),
      x3(t).diff(t)-4*x1(t)+4*x2(t)-2*x3(t),
      ]
con = {x1(0):1, x2(0):2, x3(0):3}
########## begin ##########
s = sp.dsolve(eq, [x1(t), x2(t), x3(t)], ics=con)
##########  end  ##########

print(s)

其中,第二个有系数,题目如下:

特别需要注意的,时需要对方程进行标准化。

预览大图

方法3, 数值解,scipy中的odeint求方程组
  • 如果是二阶方程,需要化成两个一阶方程组成的方程组,如果是一阶则跳过该步骤;

  • 把每个得到的方程组化成 y′=f(x,y)y′=f(x,y)y′=f(x,y) 形式,这里的x和y是相对每个方程而言的,后面会详细讲;

  • 因为要求的是数值解,所以需要确定好参数值和初始条件,在odeint中默认输入y = 0 y=0y=0的时因变量的值;

  • 确定自变量范围并解方程,绘制图像;

如:解微分方程 y′′+b∗y′+c∗siny=0,y′′+b∗y′+c∗siny=0,y′′+b∗y+c∗siny=0,其中 b,c 是参数,对 t 求导;(显然,这是一个二阶导的函数,存在的映射关系即为y=f(x)//f(t)

所以,称这里的方程F(x,y)也可以理解为F(x , f(x))

对 t 求导;

按照上面的思路,首先化成两个一阶方程:

y′(t)=z(t) (1)

z′(t)=−b∗z(t)−c∗sin(y(t)) (2)

这里用t表示自变量(x), 并将二阶方程,化为了一个一阶二元微分方程, 也就是存在两个两个一变量, y, z, —>t,其中y,z存在导数关系。因此方程而可以这样写

z=-bz - c * sin(y), 是一个二元一阶方程。到此,化简完成。

??? 这里的问题在于函数参数设计的问题。

import matplotlib.pyplot as plt
import scipy.integrate as sp
import numpy as np
#设定参数b,c初值,其实这个方程是物理学中跟钟摆重力角和摩擦力有关的一个微分方程
b = 0.25;
c = 5.0;

def function(t, y):#zi
    yt, zt = y #表示因变量,目标结果对象,默认在下面的数组中因变量是被求导的结果等价
                #这里也限定了位置。
    return [zt, -b *zt - c*np.sin(yt)]
t=np.linspace(0, 10, 100)#数值解求解范围
init_x = [np.pi - 0.1, 0]#规定初值,传入向量,分别是每个方程y=0时t的取值  初态
                         #这里时因为每个方程的y(t)映射相同,所以解相同,只用一个就能确定方程
result = sp.odeint(function, init_x, t, tfirst=True);#function组, 解集, 取值集, 
                                                     ##解方程,注意第一个参数只填函数名不要写括号,tfirst是指是否把求导自变量作为函数第一个参数
plt.rcParams['font.sans-serif'] = 'SimHei'
plt.rcParams['axes.unicode_minus'] = False
plt.xlabel('t')
plt.ylabel('f(t)')
plt.plot(t, result[:, 0], label='f:yt')#原函数数值解
plt.plot(t, result[:, 1], label='f:zt')#一阶导
plt.legend()
plt.show()

'''
1首先是我们的自定义函数,这个函数要作为 odeint 的必要参数,我们在调用 odeint 的时候,odeint 会自动调用我们定义好的这个函数;

2自定义函数的返回值是一个数组向量,第一项表示原函数(方程的解)一阶导的表示(这里我们用另一个符号zt表示),第二项就表示原函数二阶导表示(即上面的方程(2)),以此类推…

3由于我们是求数值解,所以我们需要为每个方程规定一个初值,向量的第一项是第一个方程的初值,以此类推。在这里 odeint 默认初值为 0 ,可以通过 h0 参数修改一下,详见'
'''' 

向量函数

理论理解

要理解这个概念,需要先从函数开始。

y=f(x), 本质上是在表示,对于x轴上的每一个点,存在一个y, 使得x->y的单射成立;

类似的, 如果扩展这样的关系,也就是某个对象由多个要素共同映射

w=f(x, y, z),即为三个要素共同决定w。

显然,我们可以任意推广这样的映射关系。

最后,我们甚至能够得到,多个对象对多个对象的映射

f(x, y, z), g(x, y, z), 那么,乃至是f g , 被x, y, z决定(这里强调我们不知道f是究竟被多少变量决定, g同理), 因此,就存在这样的猜测

对于任何一种映射关系,其反应的一系列导数的组合在这样一组关系中是唯一的。也就是说f的任意阶导,与g的任意阶导如果相同,那就表明同一个函数,否者肯定可以区分。因此定义雅可比行列式

预览大图

设 Ω 是 R^2 中的区域,函数 f(x,y),g(x,y) 在 Ω 内可微,(f(x,y),g(x,y)) 称为向量函数(vector function),它的雅可比矩阵是如上

这个是基于一阶导就能够区分的一种应用。

而对于需要多阶数来区分的函数,则:设 Ω 是 R^n 中的区域,n 元函数 f1(x),…,fm(x) 在 Ω 内可微, f1(x),…,fm(x) 称为向量函数(vector function),亦称为 Ω 属于 R^n 到 R^m 的映射,记为 f:Ω->R^m。它的雅可比矩阵是

预览大图

而如果将所有分量看成一个整体t的向量 即为t = (x1, x2, x3, x4……)这样,就重新变成了一个基本函数,就可以存在微分,积分,导数的概念,即:

预览大图

其积分也可以表征为:

预览大图

代码表示numpy.array

比较特别的是,这个包里面的向量默认为行向量。且1维行向量可以直接乘以1维行向量(当作行×列处理)
转置:
import numpy as np
v1 = np.array([1, 2, 3, 4, 5])  ##将列表元素转换为向量的元素

## 推荐使用.reshape方法。转换为5x1的二维数组
v1.reshape(5, 1)
Out[44]: 
array([[1],
       [2],
       [3],
       [4],
       [5]])

##如果一开始是二维数组,那就方便很多。直接.T
v2 = np.array([[1, 2, 3, 4, 5]])
v2
Out[34]: array([[1, 2, 3, 4, 5]])
v2.T
Out[35]: 
array([[1],
       [2],
       [3],
       [4],
       [5]])
运算:

加减法:同型矩阵对应项相加

点乘:np.dot(v1, v2), 返回一个数组而不是数字

v1=np.array([1, 2, 3, 4, 5])
v2=np.array([1,1, 1, 1, 1])
np.dot(v1, v2)
out[42]:数字
np.dot(v1, v2.T)
Out[43]: array([[10]]) 

叉乘:np.cross(v1, v2)

v5 = np.array([1, 2])
v6 = np.array([3, 4])
np.cross(v5, v6)
Out[44]: array(-2) ##两个向量的夹角大于180度,围成的面积为负数
三维行向量的外积。
v7 = np.array([1, 2, 3])
v8 = np.array([3, 4, 5])
np.cross(v7, v8)
Out[46]: array([-2,  4, -2])

注意点

一方面,要先通过np.array将数组转化为可以计算的矩阵,

另一方面,留意矩阵本身十分可解

AI 算法区

搜索算法

盲目搜索:

B/DFS搜索
如所学过的算法一样,通过基础的队列来实现。
#dfs
'''
迷宫的定义:
A能走到B和C
B能走到D和E
C能走到F
F能走到G和H
D,E,G,H是死胡同
'''
graph = {
            'A': ['B', 'C'],
            'B': ['D', 'E'],
            'C': ['F'],
            'F': ['G', 'H'],
            'D': [],
            'E': [],
            'G': [],
            'H': []
         }
def dfs(graph, start, visited=None):
    '''
    深度优先搜索,从A走到H
    :param graph: 待搜索的迷宫
    :param start: 开始搜索的起点
    :param visited:  已经搜索过的地点集合
    '''
    if visited is None:
        visited = set()
    visited.add(start)
    print(start, end='')
    # 当前地点为H时结束搜索
    if start == 'H':
        return
    # 看看当前位置有哪些路可以走,如果能走并且之前没有走过就走
    for v in graph[start]:
        if v not in visited:
            dfs(graph, v, visited)
# 从迷
宫的A走到H,并按搜索的先后顺序打印地点
dfs(graph, 'A') 
################################################################
####DFS
'''
迷宫的定义:
A能走到B和C
B能走到D和E
C能走到F
F能走到G和H
D,E,G,H是死胡同
'''
graph = {
            'A': ['B', 'C'],
            'B': ['D', 'E'],
            'C': ['F'],
            'F': ['G', 'H'],
            'D': [],
            'E': [],
            'G': [],
            'H': []
         }
def bfs(graph, start):
    '''
    广度优先搜索,从A走到H
    :param graph: 待搜索的迷宫
    :param start: 开始搜索的起点
    '''
    # queue为队列,当队列为空或者当前地点为H时搜索结束
    visited, queue = set(), [start]
    while queue:
        # 从队列中出队,即当前所处的地点
        vertex = queue.pop(0)
        if vertex not in visited:
            visited.add(vertex)
            print(vertex, end='')
            # 当前地点是`H`的话就结束搜索
            if vertex == 'H':
                return
            # 将当前所处地点所能走到的地点放入队列
            for v in graph[vertex]:
                if v not in visited:
                    queue.extend(v)
# 从迷宫的A走到H,并按搜索的先后顺序打印地点
bfs(graph, 'A')
八皇后:DFS:
def make(mark):
    '''
    标记皇后的位置,例如mark[0] = 2, 表示第1行皇后放在第3列的位置
    :param mark: 皇后的位置信息
    :return: 拼接好的结果
    '''
    #初始化数组
    r = [['X' for _ in range(len(mark))] for _ in range(len(mark))]
    #将每一行中皇后的位置用‘Q’代替
    for i in mark:
        r[i][mark[i]] = 'Q'
    #枚举,将原来散的元素连接成字符串
    for k, v in enumerate(r):
        r[k] = ''.join(v)
    return r
def FourQueens(mark, cur, ret):
    '''
    深度优先搜索的方式求解四皇后问题
    :param mark:表示皇后的位置信息,例如[0,1,3,2]表示棋盘的第1行第1列,第2行第2列,第3行第4列,第4行第3列放置了皇后。例如[1, None, None, None]表示第1行第2列放置了皇后,其他行没有放置皇后。初始值为[None,None,None,None]
    :param cur:表示当前准备在第几行放置皇后,例如`cur=1`时,表示准备在第`2`行放置皇后。初始值为0
    :param ret:表示存放皇后摆放结果的列表,类型为列表。初始值为[]
    :return:无
    '''
    if cur == len(mark):
        #********* Begin *********#
        # 如果当前行是最后一行,记录一个解,并返回结束此次搜索
        ret.append(make(mark))
        return ;
        #********* End *********#
    #试探处理,将当前行的皇后应该在的位置遍历每一列,如果满足条件,递归调用处理下一行
    for i in range(len(mark)):
        mark[cur], down = i, True
        for j in range(cur):
            # 当想在当前位置放皇后会与其他皇后冲突时不放置皇后
            if mark[j] == i or abs(i-mark[j]) == cur - j:#????
                down = False
                break
        if down:
            # 准备在下一行找能放置换后的位置
            FourQueens(mark, cur+1, ret)

启发搜索:

1.通过启发函数构建比较策略,每次选择可以实现最优结果的最优策略。

基本结构:

两表:开启列表,用于搜索当前可能前往的地方

        关闭列表,去除已经去过的地方,收集开启列表淘汰的对象

父点:每个点要有父节点,也就是其被加入开启列表时的起始点。----但是如果有更优的抵达方式,需要更改父节点为更有点

回溯:通过父节点回溯路径。

路径搜索演示下:

预览大图 途中,每个方块有一个启发函数计算其价值F=G+H。即为F(权衡代价,默认保持可能的最低)= G(左下角,表示抵达代价,横竖10,斜着14) + H(估计代价,这里应该是用曼哈顿距离估计了水平的情况)

图生成:
def GenerateMap(m, n):#正是其通过对图的详细定义,使得其可以直接用于定义点
    map = list()
    for j in range(m):
        nodeRow = list()
        map.append(nodeRow)
        for i in range(n):
            node = Node()
            node.y = j
            node.x = i
            node.unable = False
            node.distanceFromDes = -1  # 距离终点的距离
            node.distanceFromOri = -1  # 距离起点的距离
            node.allDistance = -1
            node.added = False
            node.closed = False
            node.parent = None
            nodeRow.append(node)
    return map
算法实现
from a_star_utils import Node##这个估计是自定义了
def A_star(map, mapSize, start, end):
    '''
    A*算法,从start走到end
    :param map:地图
    :param mapSize:地图大小,例如[10,10]表示地图长10宽10
    :param start:表示出发地,类型为列表,如[1,2]表示出发地为地图中的第1行第2列的方块
    :param end:表示目的地,类型为列表,如[1,2]表示目的地为地图中的第1行第2列的方块
    :return:从出发地到目的地的路径
    '''
    openedList = []
    #********* Begin *********#
    # 获得出发地方块的信息,并将信息保存为node变量
    node =map[start[0]][start[1]];##这里独特的用了图的点的构造方法来实现
    #********* End *********#
    node.distanceFromOri = 0
    node.allDistance = 0
    #********* Begin *********#
    # 将当前方块存到开启列表中
    openedList=list()
    openedList.append(node)
    node.added = True
    #********* End *********#
    while len(openedList) != 0:
        node = openedList.pop(0)
        node.closed = True
        if node.y == end[0] and node.x == end[1]:
            finalListNeedReverse = []
            while node != None:
                finalListNeedReverse.append(node)
                node = node.parent
            finalListNeedReverse.reverse()
            return finalListNeedReverse
        neighboursList = []
        y = node.y
        x = node.x
        parentDistanceFromOri = node.distanceFromOri
        for needNodey in (y + 1, y, y - 1):
            if needNodey < 0 or needNodey >= mapSize[0]:
                continue
            for needNodex in (x + 1, x, x - 1):
                if needNodex < 0 or needNodex >= mapSize[1]:
                    continue
                needNode = map[needNodey][needNodex]
                if needNode.unable == True or needNode.closed == True or needNode.added == True:
                    continue
                yOffset = needNodey - y
                xOffset = needNodex - x
                allOffset = yOffset + xOffset
                if allOffset == 1 or allOffset == -1:
                    distanceFromOri = parentDistanceFromOri + 1
                else:
                    distanceFromOri = parentDistanceFromOri + 1.4
                if needNode in neighboursList:
                    # 若新的G值比老的G值低,则更新成老的G值
                    if distanceFromOri < needNode.distanceFromOri:
                        needNode.distanceFromOri = distanceFromOri
                else:
                    needNode.distanceFromOri = distanceFromOri
                    neighboursList.append(needNode)
        for needNode in neighboursList:
            needNode.parent = node
            # 更新F值
            needNode.allDistance = needNode.distanceFromOri + needNode.distanceFromDes
            needNode.added = True
            openedList.append(needNode)
        openedList.sort(key=lambda x: x.allDistance)
    return None
要说伪代码的话,就写的能看懂许多:
# 构造开启列表,开启列表为openedList
openedList = list()
# 将起点的G和F设置成0,H已经设置过了 map表示地图节点,originIndex表示出发地
node = map[oriIndex[0]][oriIndex[1]]
node.distanceFromOri = 0
node.allDistance = 0
# 将起点存到开启列表中
openedList.append(node)
node.added = True
# 循环检查开启列表
while len(openedList) != 0:
    # 将开启列表中第一个方块删除
    node = openedList.pop(0)
    # 方块的closed状态设置成True,相当于加入到关闭列表
    node.closed = True
    # 如果走到了终点就获取路径
    if node.y == desIndex[0] and node.x == desIndex[1]:
        finalListNeedReverse = list()
        while node != None:
            finalListNeedReverse.append(node)
            node = node.parent
            finalListNeedReverse.reverse()
            return finalListNeedReverse

    # neighboursList存放的是当前方块周围的方块
    neighboursList = list()
    y = node.y
    x = node.x
    parentDistanceFromOri = node.distanceFromOri

    # 检查当前方块周围的方块
    for needNodey in (y + 1, y, y - 1):
        if needNodey < 0 or needNodey >= mapSize[0]:
            continue
        for needNodex in (x + 1, x, x - 1):
            if needNodex < 0 or needNodex >= mapSize[1]:
                continue
            needNode = map[needNodey][needNodex]
            # 不考虑不可达、在关闭列表中以及已经在开启列表中的方块
            if needNode.unable == True or needNode.closed == True or needNode.added == True:
                continue#这句意思是,点不是禁用点,没有被经过,也没有被加入open(防止多余加入,使得距离增加
            yOffset = needNodey - y
            xOffset = needNodex - x
            allOffset = yOffset + xOffset#总相对偏移曼哈顿距离
            # 计算可达并没有被添加到开启列表中的方块的G值
            if allOffset == 1 or allOffset == -1:
                distanceFromOri = parentDistanceFromOri + 1
            else:
                distanceFromOri = parentDistanceFromOri + 1.4

            # 更新最小的G值
            if needNode in neighboursList:#开始时,临近表是空的。
                if distanceFromOri < needNode.distanceFromOri:
                    needNode.distanceFromOri = distanceFromOri
            else:#疑惑在于为什么上面要加表。按理来说,一个点的临近点不会重复
                needNode.distanceFromOri = distanceFromOri
                neighboursList.append(needNode)
    # 设置neighboursList中的方块的父方块,F值等
    for needNode in neighboursList:
        needNode.parent = node
        needNode.allDistance = needNode.distanceFromOri + needNode.distanceFromDes

        needNode.added = True
        openedList.append(needNode)
    # 将方块根据F值从小到大排序,这样每次只要获取列表中的一个方块就能得到F值最小的方块
    openedList.sort(key=lambda x: x.allDistance)  
A*算法解决8数码移动问题
这个问题解释了我很久的疑惑,即如何确定一个能将结果导向目标结果的目标函数,答案是能使得当前状态与目标状态相同时能返回一个可比较的唯一值的函数:
class Solution:
    def salvePuzzle(self, init, targ):
        '''
        求解8数码问题
        :param init:初始状态,123045678
        :param targ: 目标状态, 012345678
        :return:
        clf -由udlr组成的移动路径字符串串
        '''
        #空格0在各个位置上可以移动到的其余位置
        dirs={0:[1,3], 1:[0, 2, 4],  2:[1, 5],
              3:[0, 4, 6], 4:[1, 3, 5, 7], 5:[2, 4, 8],
              6:[3, 7], 7:[4, 6, 8], 8:[5, 7]}
        #原因是因为只有0可以移动,因此可以遍历位置状态出来
        #可移动的对应方向
        drec ={0:['r', 'd'], 1:['l', 'r', 'd'], 2:['l', 'd'],
               3:['u', 'r', 'd'], 4:['u', 'l', 'r', 'd'], 5:['u', 'l', 'd'],
               6:['u', 'r'], 7:['u', 'l', 'r'], 8:['u', 'l']}
        que=[]#宽搜索队列,存态
        map_fn={}#总评估代价;
        map_gn={} #到这个状态最少的步骤(层次)
        map_path={}#记录当前状态的上一个状态,以及到此的转移方式

        #统一用字典来存了
        map_fn[init] = 1+self.calcDistH(init, targ);#这里也能看出self是用在类里面
        map_gn[init] = 1;
        map_path[init] = (init, 'o');
        que.append(init);

        while(len(que)>0):
            cur_map = min(map_fn, key=map_fn.get);#最小代价得出,显然是一个 状态:代价的字典
            del map_fn[cur_map]#删除键值
            que.remove(cur_map)#移除队列中的对应列
            #这里其实可以看出来,是想队列和待解评估合并用当一个,能弄出最小代价的队列

            if cur_map == targ:
                break#已经到达目标

            idx_zero = cur_map.index("0")#确定0索引位置,明确遍历方式
            for id_, idx_next in enumerate(dirs[idx_zero]):#这里能看出枚举本质就是穷举可能组合
                new_map = self.moveMap(cur_map, idx_zero, idx_next);
                if new_map not in map_path:#这里同时去除了后面的重复加入
                    map_fn[new_map] = map_gn[cur_map] +1 +self.calcDistH(new_map, targ);
                    map_gn[new_map] = map_gn[cur_map] +1
                    map_path[new_map] = (cur_map, drec[idx_zero][id_]);
                    que.append(new_map);

        #此时已经遍历完成了, 如果结果必然有解, 就如下
        path = ''
        cur_map = targ;
        while map_path[cur_map][1]!='o':#不断向上找,一直到根节点标志
            (cur_map, direction) = map_path[cur_map];#从目标对象开始往回找
            path += direction;
        path = path[::-1];#反转
        return path;

    def calDishH(self, src_map, dest_map):#这里对索引的处理更是独特
        '''
        启发函数h(n)
        :param src_map: 当前状态八数码
        :param dest_map: 目标状态八数码
        :return:
        clf, 当前状态到目标状态的启发值
        '''

        dist =0;
        a = src_map.index('0')#直接得到0索引
        for i in range(0, 9):
            if i!=a:
                dist +=abs(i-dest_map.index(src_map[i]))

        return dist;

    def moveMap(self, cur_map, i, j):##凸显了py的性质,之前都想着用循环解决
        '''
        基于0的位置变化,得到变化后的八数码
        :param cur_map: 原数码
        :param i: 原来0的索引位置
        :param j: 变化后0的索引位置
        :return:
        clf- 新的八数码状态
        '''

        if i>j:
            i, j = j, i;
        new_map = cur_map[:i] + cur_map[j] + cur_map[i+1:j] + cur_map[i] + cur_map[j+1:]

        return new_map;

一般注意

for poem in ['a', 'b', 'c']
    print(poem)#表示poem为指针,通过缩进表示分层


##特别的,同区块只要缩进相同(如括号数量或者tab相同,那就是同一个语句块另外,句尾可以
##通过添加分号;来实现一行多个语句.

关键字保留

import keyword
print(keyword.kwlist)#可以输出所有的关键字信息,不确定是否包括一切其他库的内容

注意点

  1. 运算前虽然不用基于变量空间声明(类似于c),但是需要归类变量的类型。例如在进行double 的运算时 要float(input)等

  2. 输出时如果需要格式化,需要用f标记或者.format()

  3. 对于单位类数值,优先化解单位后运算(归一化或者化标);
    map_fn[new_map] = map_gn[cur_map] +1 +self.calcDistH(new_map, targ);
    map_gn[new_map] = map_gn[cur_map] +1
    map_path[new_map] = (cur_map, drec[idx_zero][id_]);
    que.append(new_map);

     #此时已经遍历完成了, 如果结果必然有解, 就如下
     path = ''
     cur_map = targ;
     while map_path[cur_map][1]!='o':#不断向上找,一直到根节点标志
         (cur_map, direction) = map_path[cur_map];#从目标对象开始往回找
         path += direction;
     path = path[::-1];#反转
     return path;
    

    def calDishH(self, src_map, dest_map):#这里对索引的处理更是独特
    ‘’’
    启发函数h(n)
    :param src_map: 当前状态八数码
    :param dest_map: 目标状态八数码
    :return:
    clf, 当前状态到目标状态的启发值
    ‘’’

     dist =0;
     a = src_map.index('0')#直接得到0索引
     for i in range(0, 9):
         if i!=a:
             dist +=abs(i-dest_map.index(src_map[i]))
    
     return dist;
    

    def moveMap(self, cur_map, i, j):##凸显了py的性质,之前都想着用循环解决
    ‘’’
    基于0的位置变化,得到变化后的八数码
    :param cur_map: 原数码
    :param i: 原来0的索引位置
    :param j: 变化后0的索引位置
    :return:
    clf- 新的八数码状态
    ‘’’

     if i>j:
         i, j = j, i;
     new_map = cur_map[:i] + cur_map[j] + cur_map[i+1:j] + cur_map[i] + cur_map[j+1:]
    
     return new_map;
    

# 一般注意

```py
for poem in ['a', 'b', 'c']
    print(poem)#表示poem为指针,通过缩进表示分层


##特别的,同区块只要缩进相同(如括号数量或者tab相同,那就是同一个语句块另外,句尾可以
##通过添加分号;来实现一行多个语句.

关键字保留

import keyword
print(keyword.kwlist)#可以输出所有的关键字信息,不确定是否包括一切其他库的内容

注意点

  1. 运算前虽然不用基于变量空间声明(类似于c),但是需要归类变量的类型。例如在进行double 的运算时 要float(input)等

  2. 输出时如果需要格式化,需要用f标记或者.format()

  3. 对于单位类数值,优先化解单位后运算(归一化或者化标);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值