整周下来输入的信息量比较大,堆在一起梳理是件让人痛苦的事情,以后要养成每吸收一个主题的知识就总结归纳的习惯。下面进入主题。
Algorithm:
LeetCode problem 7: Reverse Integer
python代码:
class Solution:
def reverse(self, x: int) -> int:
if x == 0:
return 0
y = abs(x)
res = 0
while y != 0:
res = res*10 + y%10
y = int(y/10)
if(res < -2**31) or (res > 2**31 - 1):
return 0
return int(x/abs(x)*res)
运行时间:54ms,占用内存:13.4M
C++代码:
class Solution {
public:
int reverse(int x) {
long res = 0;
while(x != 0)
{
res = res*10 + x % 10;
x /= 10;
}
return (res > INT_MAX || res < INT_MIN)? 0 : res;
}
};
运行时间:4ms,占用内存:8M
算法复杂度分析:
时间复杂度:O(lg(x)),重复基本操作lg(x)次
空间复杂度:O(1)
problem 9: Palindrome Number
python代码:
class Solution:
def isPalindrome(self, x: int) -> bool:
y = x
res = 0
if x < 0 or (x%10 == 0 and x !=0): return False
while(x != 0):
res = res*10 + x%10
x= int(x/10)
return res == y
运行时间:100ms,占用内存:13.4M
C++代码:
class Solution {
public:
bool isPalindrome(int x) {
long int y = x;
if(x < 0 || (x % 10 == 0 && x != 0)) return false;
long int res = 0;
while(x != 0){
res = res*10 + x%10;
x /= 10;
}
return res == y ? true:false;
}
};
运行时间:32ms,占用内存:8M
算法复杂度分析:
时间复杂度:O(lg(x)),重复基本操作lg(x)次
空间复杂度:O(1)
这道题和上道题思路基本上一样。
对比python和C++在运行时间上的大小,可以发现在这两道题里python程序的运行时间是C++程序的3-14倍,而算法是相同的。为什么会这样呢?
我们知道,C++是编译语言而python是抽象层次比较高的解释语言;编译型语言运行时首先将源代码编译生成机器语言,再由机器运行机器码(二进制);而python先将录入的源代码转换为字节码(.pyc),之后字节码在python虚拟机(PVM)中运行,代码自动被编译,之后再解释成机器码在CPU中执行。
C++执行过程比python执行过程少了字节码生成和虚拟机执行字节码过程。所以自然比python快。
但是,这是python比C++慢的全部原因吗?带着这个疑问,我在Medium里面找到了一篇解释得非常全面的文章。请看Review部分内容。
Review
Article: Why is python so slow?
和其它语言如Java,C#,Go,JavaScript,C++等相比,Python是最慢的语言之一。这些语言中有JIT编译的(C#,Java),也有AOT编译的(C,C++),也有和python一样是解释语言的JavaScript.
为什么Python那么慢?我们可以让它运行得快一点吗?
注:
JIT(Just In Time):实时编译,即时编译,通常所说的JIT的优势是Profile-Based Optimization,也就是边跑边优化,根据运行时信息然后随着时间的推移得到尽可能最优的代码,适用于开发调试。
AOT(Ahead Of Time) : 预先编译,静态编译。
以下三个观点可以对这个问题进行解释:
那么哪个是对Python性能影像最大的原因呢?
因为它有GIL吗?
现代计算机的CPU通常是多核的,并且有些拥有多个处理器。为了充分利用多余的处理能力,操作系统定义了一种低级的结构叫做线程:一个进程(比如微信程序)可以产生多个线程并且指导内部系统。如果一个进程是CPU密集型,那么其负载可以被多核同时处理,从而有效提高大多数应用的速度。
区别于单线程进程,计算机需要确保当内存中的变量被修改时,多线程不会同时试图访问或者改变同一个存储地址。这就是线程锁的作用。
总结起来说:多线程可以同时运行多个任务,但是当多个线程同时访问共享数据时,可能导致数据不同步,甚至错误!举个例子: 比如你在银行取钱的同时你女朋友用支付宝取钱,不同线程同时访问同一资源,如果资源不加锁可能会导致银行亏本,卡里有100却取出200这种错误。
当CPython创建变量时,它会预先分配存储空间,然后计算当前变量的引用数目。这个概念被称为引用计数。如果引用计数为零,那么它将从系统中释放对应存储区域。
当存在多个线程调用变量时,CPython如何锁住引用计数成为了一个挑战。而“全局解释锁”应运而生,它能够谨慎控制线程的执行。无论有多少的线程,解释器每次只能执行一个操作。
这对Python的性能意味着什么呢?如果你的应用基于单线程、单解释器,那么讨论速度这一点就毫无意义,因为去掉GIL并不会影响代码性能。如果你想使用线程在单解释器(Python 进程)中实现并发,并且你的线程为IO密集型(例如网络IO或磁盘IO),那么GIL争用就会导致python运行时间长的结果。
因为它是解释语言吗?
这点在Algorithm部分有初略介绍。
因为它是动态类型语言吗?
在静态类型语言中,定义变量时必须声明类型。C, C++, Java, C#, Go都是这种语言。
在动态类型语言中,类型的概念依旧存在,但是这个变量的类型是动态变化的。
a = 1
a = "foo"
在上面这个例子中,Python创建第二个变量的时候用了同样的名字,但是变量类型是str(字符型),这样就对先前在内存中给a分配的空间进行了释放和再分配。
Python为了灵活性选择了动态类型。而这种选择会使对它的优化变得非常困难。
因此,是Python的动态类型让它变慢的吗?
总结:
Python的缓慢主要是由于它动态和多用途的特点,它可以用于解决几乎所有问题。同时更加优化而快捷的替代方案可能存在。可以通过一些利用异步计算,理解分析工具,以及考虑使用多个解释器的方法来优化Python应用程序。对于有些启动时间相对不重要,并且即时编译器(JIT)可以提高效率的应用,可以考虑使用PyPy。对于性能优先并且有更多静态变量的代码部分,请考虑使用Cython。
如果您想了解具体的细节,可以阅读英文原文,若您有不同的想法,欢迎在文章底部留言或者Email我和我一起讨论。
Article: Ten Things Python programmers Should Know
- A set is simply a container that holds items, like a list, but only holds distinct elements.
- What we talked about above is one of the set’s advantages, But possibly an even bigger advantage with sets is their super fast lookup,this requires set elements to be hashable.
- the downside with sets over lists is that they don’t support indexing of elements, so there’s no ordering.
- A dictionary is something that associates to each key a value, so it’s essentially a function that pairs up elements together.
- Making lists equal to other lists will essentially create two variable names pointing to the same list in memory. This will apply to any ‘container’ item, such as dictionaries.
- Since simple assignment does not create distinct copies, Python has a built-in list statement, as well as generic copy operations. It’s also possible to perform copies using slicing.
- deepcopy method.
这里需要理解浅拷贝和深拷贝的区别:
A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original.
A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original.
- If you’ve been thinking about memory management, you might wonder why we need to store gigantic lists in memory if we might not even access their values. This is where generators come into play.
- Generators in Python provide us with the advantageous concept of lazy evaluation, so when we “construct” generators, we don’t actually evaluate some value within it unless it’s absolutely needed.
- To understand generators better, it might also be useful to incorporate the yield keyword in Python. This (fantastic) Stack Overflow question and answer explains it far better than I can, and I learned a lot about generators just by reading that page.
- open() function
- close() function
- I almost never use f.close() though, because I can use the with keyword, which will automatically close the file for me once the code exits its block. eg:
# Note: sys.argv[1] is the file, and 'r' means I'm reading it
with open(sys.argv[1], 'r') as feature_file:
all_lines = feature_file.readlines()
for i in xrange(len(all_lines)):
# Do stuff here
- If you’re interested in writing files, you can change the r parameter to w (or r+ which will enable both reading and writing) and use the file.write() method.
- It’s pretty easy to define a function in Python, using def, such as the following trivial example, which counts the number of zeros in the input, which will be a string.
def count_zeros(string):
total = 0
for c in string:
if c == 0:
total += 1
return total
print(count_zeros('00102')) # Will return 3
- Python classes provide all the standard features of Object Oriented Programming: the class inheritance mechanism allows multiple base classes, a derived class can override any methods of its base class or classes, and a method can call the method of a base class with the same name. Objects can contain arbitrary amounts and kinds of data. As is true for modules, classes partake of the dynamic nature of Python: they are created at runtime, and can be modified further after creation.(from Python Classes documentation)
Tip
用Keras搭建神经网络:
实践了用keras搭建CNN,用minst数据集训练网络的过程,对用keras搭建神经网络的过程有了一个大致的思路,接下来将对数据预处理(为什么要这么处理)、网络的实现细节(各层参数的设置、优化器的选择、激活函数的选择、损失函数的选择)、模型编译model()函数、模型训练fit()函数进行深入学习。
#CNN应用于手写数字识别
import numpy as np
from keras.datasets import mnist
from keras.utils import np_utils
from keras.models import Sequential
from keras.layers import Dense, Dropout, Convolution2D, MaxPooling2D, Flatten
from keras.optimizers import Adam
#载入数据
(x_train, y_train), (x_test,y_test) = mnist.load_data()
print('x_shape', x_train.shape) #x_shape (60000, 28, 28)
print('y_shape', y_train.shape) #y_shape (60000,)
#(60000,28,28)->(60000,28,28,1)
x_train = x_train.reshape(-1, 28, 28, 1)/255.0
x_test = x_test.reshape(-1, 28, 28, 1)/255.0
#转换one hot格式
y_train = np_utils.to_categorical(y_train, num_classes=10)
y_test = np_utils.to_categorical(y_test, num_classes=10)
#定义顺序模型
model = Sequential()
#第一个卷积层
#input_shape 输入平面
#filters 卷积核/滤波器个数
#kernel_size 卷积窗大小
#padding padding方式 same/valid
#activation 激活函数
model.add(Convolution2D(
input_shape=(28, 28, 1),
filters=32,
kernel_size=5,
strides=1,
padding='same',
activation='relu'
))
#第一个池化层
model.add(MaxPooling2D(
pool_size=2,
strides=2,
padding='same',
))
#第二个卷积层
model.add(Convolution2D(64, 5, strides=1, padding='same', activation='relu'))
#第二个池化层
model.add(MaxPooling2D(2, 2, 'same'))
#把第二个池化层的输出扁平化为1维
model.add(Flatten())
#第一个全连接层
model.add(Dense(1024, activation='relu'))
#Dropout
model.add(Dropout(0.5))
#第二个全链接层
model.add(Dense(10, activation='softmax'))
#定义优化器
adam = Adam(lr=1e-4)
#定义优化器,loss function,训练过程中计算正确率
model.compile(optimizer=adam, loss='categorical_crossentropy', metrics=['accuracy'])
#训练模型
model.fit(x_train, y_train, batch_size=64, epochs=10)
#评估模型
loss, accuracy = model.evaluate(x_test,y_test)
print('test loss:', loss)
print('test accuracy', accuracy)
Share
上海交大软件学院臧斌宇院长为《深入理解操作系统(CSAPP)第3版》写的推荐序二:
序者在文中写道:
现在一般认为问题抽象、系统抽象和数据抽象是计算机类专业毕业生的核心能力。而本书担负起了系统抽象的重任。
这句话给我留下了深刻的印象,这三方面的能力是一个计算机类专业学生(/技术人员)往高端发展的基础;CSAPP担负起了系统抽象的重任,那么以我现在对专业知识体系浅薄的认识,我觉得问题抽象能力的重任是由数学课程和建模训练担任起来的,而数据抽象能力由数据结构和算法课程担任,其中这些课程互相联系,相辅相成。任何一位谋求往高端发展的技术人员都应该注重自己这三方面基础能力的培养,这样才能在技术道路上走得更远。