一、Python基础回顾
定义类
定义新类的做法是:提供一个类名以及一整套与函数定义语法类似的方法定义。所有的类都应该先提供构造方法,构造方法定义了数据对象的创建方式。self是一个总是指向对象本身的特助参数,它必须是第一个形式参数,但是在调用方法时,不需要提供相应的实际参数。要创建实例必须调用构造方法,使用类名并传入状态的实际值就能完成调用。
python为所有的类都提供了一套标准的方法,其中之一就是将对象转换成字符串的方法__str__。默认实现是返回实例的地址字符串。重新定义该方法的行为。
from fractions import gcd
class Fraction:
def __init__(self,top,bottom):
self.num = top
self.den = bottom
def show(self):
print(self.num,"/",self.den)
def __str__(self):
return str(self.num)+"/"+str(self.den)
# 化为最简 寻找分子和分母的最大公因数,然后将分子和分母除以最大公因数,结果就是最简分数
# 重写add方法
def __add__(self,otherfraction):
newnum = self.num * otherfraction.den + self.den*otherfraction.num
newden = self.den*otherfraction.den
# 化简结果
common = gcd(newnum,newden)
return Fraction(newnum//common,newden//common)
# 当两个对象指向同一个引用时,f1==f2,称为浅相等
# 重写__eq__方法,建立深相等,根据值来判断而不是根据对象的引用
def __eq__(self,other):
firstnum = self.num * other.den
secondnum = other.num * self.den
return firstnum == secondnum
def gcd(m, n):
# 欧几里得算法:如果m能被n整除,最大公因数就是n,否则,n与m除以n的余数才是子u大公因数
# 分母必须为正
while m % n != 0:
oldm = m
oldn = n
m = oldn
n = oldm % oldn
return n
myfraction = Fraction(3,5)
# myfraction.show()
print(myfraction)
# 可以重写Fraction类中的很多其他方法,最重要的一些是基本的数学运算
f1 = Fraction(1,4)
f2 = Fraction(1,2)
f3 = f1+f2
print(f3)
运行结果:
3/5
3/4
继承
python中的子类可以从父类中集成特征数据和行为,父类也称为超类。
构建模拟程序用于模拟数字电路,逻辑门是这个模拟程序的基本构造的单元,它们代表其输入和输出之间的布尔代数关系,一般来说,逻辑门都有单一的输出,输出值取决于提供的输入值。
通过不同的模式将这些逻辑门组合起来并提供一系列输入值,可以构建具有逻辑功能的电路。为实现电路,首先需要构建逻辑门的表示,可以轻松的将逻辑门组织成类的继承层次结构。
Connector类并不在逻辑门的继承层次结构中,但是他会使用该结构,从而使每一个连接器的两端都有一个逻辑门,这称为HAS-A关系。Connector与LogicGate是HAS-A关系。这意味着链接器内部包含LogicGate类的实例,但是不在继承层次结构中。在涉及类时,区分IS-A关系(需要继承)和HAS-A(不需要继承)非常重要。
但是在BinaryGate类中,逻辑门有两个输入,但连接器必须只连接其中一个,如果两个都能连接,那么默认选择PinA。如果PinA已经有了连接,就选择PinB。如果两个输入都已有连接,则无法连接逻辑门。现在的输入来源有两个:外部以及上一个逻辑门的输出。对方法getPinA和getPinB进行修改,如果输入没有与任何逻辑门相互连接,就和之前一样要求用户进行输入,如果有了连接就访问fromgate的输出值。这会出发fromgate处理逻辑。这个过程会已知持续,知道获取所有输入并且最终的输出值称为正在查询的逻辑门的输入。在某种意义上,这个电路反向工作,以获得所需的输入,在计算最后的结果。
class LogicGate:
# 每一个逻辑门都有一个用于识别的标签以及一个输出
# 所有的逻辑门还需要能够知道自己的输出值,需要知道自己对应的逻辑运算是什么
def __init__(self, n):
self.label = n
self.output = None
def getLabel(self):
return self.label
def getOutput(self):
#参数self指向实际调用方法的逻辑门对象的引用
self.output = self.performGateLogic()
return self.output
# 我们依据输入的个数来对逻辑门进行分类
class BinaryGate(LogicGate):
# 唯一的行为是取得两个输入的值,由于这些值来自于外部 因此通过一条输入语句来要求用户提供
def __init__(self, n):
super().__init__(n)
# 两个输入
self.pinA = None
self.pinB = None
'''
对方法getPinA和getPinB进行修改,如果输入没有与任何逻辑门相互连接,就和之前一样要求用户进行输入,如果有了连接就访问fromgate的输出值。这会出发fromgate处理逻辑
'''
def getPinA(self):
if self.pinA == None:
return int(input("enter pin A input for gate" + self.getLabel() + "-->"))
else:
return self.pinA.getFrom().getOutput()
# def getPinB(self):
# return int(input("enter pin B input for gate" + self.getLabel() + "-->"))
def getPinB(self):
if self.pinB == None:
return int(input("Enter Pin B input for gate "+self.getLabel()+"-->"))
else:
return self.pinB.getFrom().getOutput()
# 使得每个togate都能选择适当的输入
def setNextPin(self,source):
if self.pinA == None:
self.pinA = source
else:
if self.pinB == None:
self.pinB = source
else:
print("Cannot Connect: NO EMPTY PINS on this gate")
class UnaryGate(LogicGate):
def __init__(self, n):
super().__init__(n)
self.pin = None
# def getPin(self):
# return int(input("enter pin input for gate" + self.getLabel() + "-->"))
def getPin(self):
if self.pin == None:
return int(input("Enter Pin input for gate "+self.getLabel()+"-->"))
else:
return self.pin.getFrom().getOutput()
def setNextPin(self,source):
if self.pin == None:
self.pin = source
else:
print("Cannot Connect: NO EMPTY PINS on this gate")
class AndGate(BinaryGate):
def __init__(self, n):
super().__init__(n)
def performGateLogic(self):
a = self.getPinA()
b = self.getPinB()
if a == 1 and b == 1:
return 1
else:
return 0
class OrGate(BinaryGate):
def __init__(self, n):
super().__init__(n)
def performGateLogic(self):
a = self.getPinA()
b = self.getPinB()
if a == 1 or b == 1:
return 1
else:
return 0
class NotGate(UnaryGate):
def __init__(self,n):
UnaryGate.__init__(self,n)
def performGateLogic(self):
if self.getPin():
return 0
else:
return 1
# 连接器类
'''
每一个连接器对象都包含一个fromgate和togate两个逻辑门实例,数值会从一个逻辑门的输出流向下一个逻辑门的输入
对setNextPin的调用对于建立连接来说非常重要
需要将这个方法添加到逻辑门类中,以使每一个togate能够选择适当的输入
'''
class Connector:
def __init__(self,fgate,tgate):
# 连接来源
self.fromgate = fgate
# 连接去向
self.togate = tgate
tgate.setNextPin(self)
def getFrom(self):
return self.fromgate
def getTo(self):
return self.togate
# 创建实例对逻规则进行验证
def main1():
g1 = AndGate("G1")
print(g1.getOutput())
# 构造上图中的电路
def main2():
g1 = AndGate("G1")
g2 = AndGate("G2")
g3 = OrGate("G3")
g4 = NotGate("G4")
c1 = Connector(g1, g3)
c2 = Connector(g2, g3)
c3 = Connector(g3, g4)
print(g4.getOutput())
main1()
main2()
运行结果:
enter pin A input for gateG1-->1
Enter Pin B input for gate G1-->0
0
enter pin A input for gateG1-->1
Enter Pin B input for gate G1-->1
enter pin A input for gateG2-->1
Enter Pin B input for gate G2-->1
0
算法分析
算法和程序是不同的,算法是为逐步解决问题而涉及的一系列通用指令,程序是用某种变成语言对算法编码,同一个算法可以对应许多程序
算法分析关心的是基于所使用的计算资源比较算法,解决问题时要占用的空间或内存,或者根据算法窒执行所需要的时间进行比较和分析,这个指标有时称作算法的执行时间或运行时间。time中有一个time函数,她会以秒为单位返回自指定时间点起到当前的系统时钟时间,在首位调用一次这个函数计算差值,就可以得到以秒为单位执行的时间。
异序词检测示例
如果一个字符串只是重排了另一个字符串的字符,例如heart,earth
假设要检查的两个字符串长度相同,并且都是由26个英文字母的小写形似组成的,我们的目标时编写一个布尔函数,它接受两个字符串,并能判断它们是否是异序词。
'''
清点法:
清点第一个字符串中的字符是否都出现在第2个字符中
清点是通过使用python中的特殊值None取代字符来实现的
python中字符串中的字符是不可修改的,先转换成列表
'''
def anagramSolution1(s1,s2):
alist = list(s2)
pos1 = 0
stillOK = True
while pos1 <len(alist) and stillOK:
pos2 = 0
found = False
while pos2 <len(alist) and not found:
if s1[pos1] == alist[pos2]:
found = True
else:
pos2 = pos2 + 1
if found:
alist[pos2] =None
else:
stillOK = False
pos1 = pos1 + 1
return stillOK
分析算法:对于s1中的n个字符,检查每一个时都要遍历s2中的n个字符。要匹配s1中的一个字符,列表中的n个位置都要被访问一次。因此访问次数就变成1到n的整数之和。
所以该方案的时间复杂度是O(n*n)
'''
尽管s1和s2是不同的字符串,但是只要是由相同的字符构成,它们就是异序词
按照字母表顺序个字符排序,异序词得到的结果将是同一个字符串
'''
def anagramSolution2(s1,s2):
alist = list(s1)
blist = list(s2)
alist.sort()
blist.sort()
pos = 0
matches = True
while pos<len(s1) and matches:
if alist[pos] == blist[pos]:
pos = pos +1
else:
matches = False
return matches
算法分析:在排序之后只需要遍历一次就可以比较n个字符,但是调用两次sort()是有代价的。
'''
计数法:
两个异序词具有相同数目的的字符
判断每个字符出现的次数,两个计数器列表相同,则这两个列表是异序词
'''
def anagramSolution3(s1,s2):
c1 = [0]*26
c2 = [0]*26
for i in range(len(s1)):
pos = ord(s1[i]) - ord('a')
c1[pos] = c1[pos] + 1
for i in range(len(s2)):
pos = ord(s2[i]) - ord('a')
c2[pos] = c2[pos]+1
j=1
stillOK = True
while j<26 and stillOK:
if c1[j] == c2[j]:
j = j+1
else:
stillOK = False
return stillOK
算法分析:这个方案也有循环,但是这个循环没有嵌套,前两个计数循环都是n阶,第三个循环只有26次,这是异序词检测的线性阶算法,尽管这个方法是线性的,但是它需要用额外的空间来存储计数器,这个算法用空间来换时间。