データ構造とアルゴリズム(パイソン)
なぜ?
私たちは、良い例を与えないことがあります。
プログラムが戦場になぞらえ書かれた決勝で実行されている場合は、我々のコードは、一般的な農場のコマンドであり、我々は、コードを書かれている兵士と武器です。
だから、どのようなデータ構造とアルゴリズム?答え:戦争の芸術!
私たちは、戦場での戦争の戦いの芸術を見ることができないので、勝つかもしれない、失敗することがあります。でも、勝利はまた、重い代償を支払ってもよいです。ほとんどの時間は、問題を解決する可能性がありますが、プログラムは、効率やコスト意識、パフォーマンスを実行していない、時々私は解決するために開始する方法がわからない、任意のアイデアを持っていない可能性が問題に直面して、データ構造とアルゴリズムを読んでいない:私たちは、その逆のプログラムを書きます低い;彼らはどのようにターゲットを絞った最適化を知らない、時には一時的に問題を解決するために他の人が開発した、ヘルプツールが、出会いのパフォーマンスのボトルネック。
私たちは、多くの場合、戦争の芸術を見れば、あなたはより少ないと、時々より、自信を持ってそれを行うことができます!私たちは、多くの場合、データ構造とアルゴリズムを見れば同様に、我々はまた、解決される問題が発生する可能性が貫通、洞察、簡単にプログラムを書くことができます。
そのため、データ構造とアルゴリズムは、プログラム開発者ではなく、比類のないマスターをExcelに簡単な修正不可欠の基本的なスキルです。ローマは一日にして成らず、我々は通常、蓄積することを学ぶためのイニシアチブを取るために継続する必要があります。
3日間の研究では、我々は概念を理解できるように、一般的に使用されるデータ構造とアルゴリズムを把握したいと考えています。
導入
最初の質問を見て:
+ B + C = 1000場合、及びA ^ 2 + B ^ 2 = C ^ 2の可能な組み合わせC、A、Bの全てを取得する方法、(A、B、Cは自然数ですか)?
1.1最初の試み
1 インポート時間 2 3 START_TIME = time.time() 4 5 #注意是三重循环 6 ために範囲(0 1001 ): 7 のための B における範囲(0 1001 ): 8 のために C で 1001の範囲(0 ): 9 もし ** 2 + B ** 2 == C ** 2 および A + B + C == 1000年: 10 プリント(" A、B、C:%Dは、%D、%dの"%( A、B、C)) 11 12 END_TIME =time.time() 13 プリント(" 経過:%F "%(END_TIME - START_TIME)) 14 プリント(" 完了!")
結果:
A、B、C:0、500、500 、B、C: 200、375、425 、B、C: 375、200、425 、B、C: 500、0、500は 経過: 214.583347 完全に!
1.2提案されたアルゴリズム
アルゴリズムの概念
コンピュータプログラムは、基本的にコンピュータに指定されたタスクを実行するための正確な手順を指示するアルゴリズムであるため、情報を処理するコンピュータアルゴリズムの本質は、あります。アルゴリズム情報処理は、記憶装置またはアドレス入力データからデータを読み出す際に、一般的に、出力デバイスまたは後のリコールのためのメモリアドレスに結果を書き込みます。
アルゴリズムは、問題解決の方法やアイデアの存在とは無関係です。
アルゴリズムの場合、言語は重要ではなく、重要なのはアイデアです。
アルゴリズムは、我々は今、Python言語で記述されている実感(C、C ++記述、説明のPythonなどと記載する)実装のバージョンを記述するための別の言語を持つことができます。
アルゴリズムのファイブ特性
- 複数の入力を有する0またはアルゴリズム:入力
- 出力:のアルゴリズムの出力に少なくとも一つ以上
- 有限:終了せずに自動的に制限され、無限ループのステップの後、アルゴリズム、及び各ステップは、許容可能な時間内に完了することができます
- 不確実性は:アルゴリズムの各ステップは、明確な意味を持っている、あいまいな表示されません。
- 実現可能性:アルゴリズムの各ステップは、実現可能であることができ、すべてのステップが実行の限られた数を完了することを意味
1.3秒の試み
1 インポート時間 2 3 START_TIME = time.time() 4 5 #注意是两重循环 6 ために範囲(0 1001 ): 7 のための B における範囲(0 1001 ): 8 、C = 1000 - - B 9 もし ** 2 + B ** 2 == C ** 2 : 10 プリント(" A、B、C:%のDは、%Dは、%D "%(A、B、C)) 11 12 END_TIME = time.time() 13 プリント("経過:%fを"%(END_TIME - START_TIME)) 14 プリント(" 完全な!」)
結果:
A、B、C:0、500、500 、B、C: 200、375、425 、B、C: 375、200、425 、B、C: 500、0、500は 経過: 0.182897 完全に!
ファイル名を指定して実行時間を注意:0.182897秒
1.4アルゴリズムの効率対策
アルゴリズムの実行時間の反応効率
同じ問題に対して、我々は(0.182897秒に比べ214.583347秒)で実行される2つのプログラムの間に有意差を発見した時のプログラムの実行が推定された時間を持って、2つのアルゴリズムの実現には2つの解のアルゴリズムを与え、このことから、我々は結論付けることができます:アルゴリズムプログラムの実行時間は、アルゴリズムの効率、そのアルゴリズムのメリットを反映することができます。
時間だけで絶対値が信用できますか?
私たちは、古いコンピュータと機械の低パフォーマンスで実行されているアルゴリズムの第2の試みは、事態がなると仮定しますか?私たちのコンピュータよりもはるかに高速に実行するために、時間の出ていない214.583347秒のメソッドを実行する可能性があります。
長所と短所を比較するためのアルゴリズムの実行時間はのみに依存することは、必ずしも客観的で正確ではありません!
コンピュータプログラムの実行は、(ハードウェアとオペレーティングシステムを含む)環境と不可分である、これらの客観的な理由は、プログラムの実行時間に走行速度や反応に影響を与えることができます。それでは、どのよう客観的アルゴリズムのメリットが行う判断することができますか?
時間の複雑さと「ビッグO記法」
私たちは、時間の単位の基本的な操作を実行するためのコンピュータアルゴリズムが固定されるたびに、そのユニットの代わりに、基本的な操作の数は多くの時間がかかりますことを想定しています。しかしながら、異なる環境のための機械のオペレータ、正確な時間は、単位が異なっているが、大きさの多数のアルゴリズムのための基本的な操作(すなわち、時間の多くの単位が費やさ方法)このように、無視できる環境影響機械の大きさと同じオーダーでありますアルゴリズムの反応時間効率の目標。
アルゴリズムの時間効率のために、我々は表現するために「大きなO記法」を使用することができます。
「ランダウの記号」:整数単調関数f、十分に大きいため、N(N)(N)<= C *とのG F常に存在するように、前記整数と実数の定数C> 0が存在する場合、関数g FおよびGの関数をf(N)= O(G(N))と表記漸近関数(定数を無視する)、です。換言すれば、有意の無限制限する傾向がある、関数f G特性に類似関数Gの関数に関数f対象の成長速度。
時間複雑度は:プレゼンス関数gを仮定すると、アルゴリズムAは、このようなサンプルのn時間T(N)= O(G(N))に問題の処理能力は、(G(N))アルゴリズムは漸近的であるOと呼ばれること時間の複雑さ、短い時間の複雑さは、T(N)と称される
「ビッグO記法」を理解する方法
良いが、しかし、非常に具体的かつ詳細な解析アルゴリズムであることを実際に実用的な価値を制限しました。時間空間自然とアルゴリズムの性質のために、最も重要なのは、アルゴリズムの効率解析の主要部分であり、その大きさや傾向、です。一定の係数が無視できる算術スケール計量機能の基本的な操作の数。例えば、それは3Nとみなされ2及び100N 2同じ大きさの順に属し、アルゴリズムの処理は、同じサイズの2つのインスタンスは、これら2つの機能でコストならば、私は彼らの効率「ほとんど」と思う、N-ある2段階。
最悪時間計算量
分析アルゴリズムは、考慮すべきいくつかの可能性があります。
- アルゴリズムの作業を完了するためには、基本的な操作の最小数を必要とする、つまり、最適な時間複雑
- つまり、仕事をどのように多くの基本的な算術演算取る最悪時間計算
- どのように多くの基本的な算術演算平均で作業を完了するために、その平均時間計算
对于最优时间复杂度,其价值不大,因为它没有提供什么有用信息,其反映的只是最乐观最理想的情况,没有参考价值。
对于最坏时间复杂度,提供了一种保证,表明算法在此种程度的基本操作中一定能完成工作。
对于平均时间复杂度,是对算法的一个全面评价,因此它完整全面的反映了这个算法的性质。但另一方面,这种衡量并没有保证,不是每个计算都能在这个基本操作内完成。而且,对于平均情况的计算,也会因为应用算法的实例分布可能并不均匀而难以计算。
因此,我们主要关注算法的最坏情况,亦即最坏时间复杂度。
时间复杂度的几条基本计算规则
- 基本操作,即只有常数项,认为其时间复杂度为O(1)
- 顺序结构,时间复杂度按加法进行计算
- 循环结构,时间复杂度按乘法进行计算
- 分支结构,时间复杂度取最大值
- 判断一个算法的效率时,往往只需要关注操作数量的最高次项,其它次要项和常数项可以忽略
- 在没有特殊说明时,我们所分析的算法的时间复杂度都是指最坏时间复杂度
1.5 算法分析
1.第一次尝试的算法核心部分
for a in range(0, 1001): for b in range(0, 1001): for c in range(0, 1001): if a**2 + b**2 == c**2 and a+b+c == 1000: print("a, b, c: %d, %d, %d" % (a, b, c))
时间复杂度:
T(n) = O(n*n*n) = O(n3)
2.第二次尝试的算法核心部分
for a in range(0, 1001): for b in range(0, 1001-a): c = 1000 - a - b if a**2 + b**2 == c**2: print("a, b, c: %d, %d, %d" % (a, b, c))
时间复杂度:
T(n) = O(n*n*(1+1)) = O(n*n) = O(n2)
由此可见,我们尝试的第二种算法要比第一种算法的时间复杂度好多的。
1.6 常见时间复杂度
执行次数函数举例 | 阶 | 非正式术语 |
12 | O(1) | 常数阶 |
2n+3 | O(n) | 线性阶 |
3n2+2n+1 | O(n2) | 平方阶 |
5log2n+20 | O(logn) | 对数阶 |
2n+3nlog2n+19 | O(nlogn) | nlogn阶 |
6n3+2n2+3n+4 | O(n3) | 立方阶 |
2n | O(2n) | 指数阶 |
注意,经常将log2n(以2为底的对数)简写成logn
常见时间复杂度之间的关系
所消耗的时间从小到大
O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) < O(2n) < O(n!) < O(nn)
练习: 时间复杂度练习( 参考算法的效率规则判断 )
O(5)
O(2n + 1)
O(n²+ n + 1)
O(3n³+1)
1.7 Python内置类型性能分析
timeit模块
timeit模块可以用来测试一小段Python代码的执行速度。
class timeit.Timer(stmt='pass', setup='pass', timer=<timer function>)
Timer是测量小段代码执行速度的类。
stmt参数是要测试的代码语句(statment);
setup参数是运行代码时需要的设置;
timer参数是一个定时器函数,与平台有关。
timeit.Timer.timeit(number=1000000)
Timer类中测试语句执行速度的对象方法。number参数是测试代码时的测试次数,默认为1000000次。方法返回执行代码的平均耗时,一个float类型的秒数。
list的操作测试
def test1(): l = [] for i in range(1000): l = l + [i] def test2(): l = [] for i in range(1000): l.append(i) def test3(): l = [i for i in range(1000)] def test4(): l = list(range(1000)) from timeit import Timer t1 = Timer("test1()", "from __main__ import test1") print("concat ",t1.timeit(number=1000), "seconds") t2 = Timer("test2()", "from __main__ import test2") print("append ",t2.timeit(number=1000), "seconds") t3 = Timer("test3()", "from __main__ import test3") print("comprehension ",t3.timeit(number=1000), "seconds") t4 = Timer("test4()", "from __main__ import test4") print("list range ",t4.timeit(number=1000), "seconds") # ('concat ', 1.7890608310699463, 'seconds') # ('append ', 0.13796091079711914, 'seconds') # ('comprehension ', 0.05671119689941406, 'seconds') # ('list range ', 0.014147043228149414, 'seconds')
pop操作测试
x = range(2000000) pop_zero = Timer("x.pop(0)","from __main__ import x") print("pop_zero ",pop_zero.timeit(number=1000), "seconds") x = range(2000000) pop_end = Timer("x.pop()","from __main__ import x") print("pop_end ",pop_end.timeit(number=1000), "seconds") # ('pop_zero ', 1.9101738929748535, 'seconds') # ('pop_end ', 0.00023603439331054688, 'seconds')
测试pop操作:从结果可以看出,pop最后一个元素的效率远远高于pop第一个元素
可以自行尝试下list的append(value)和insert(0,value),即一个后面插入和一个前面插入???
list内置操作的时间复杂度
1.8 数据结构
我们如何用Python中的类型来保存一个班的学生信息? 如果想要快速的通过学生姓名获取其信息呢?
实际上当我们在思考这个问题的时候,我们已经用到了数据结构。列表和字典都可以存储一个班的学生信息,但是想要在列表中获取一名同学的信息时,就要遍历这个列表,其时间复杂度为O(n),而使用字典存储时,可将学生姓名作为字典的键,学生信息作为值,进而查询时不需要遍历便可快速获取到学生信息,其时间复杂度为O(1)。
我们为了解决问题,需要将数据保存下来,然后根据数据的存储方式来设计算法实现进行处理,那么数据的存储方式不同就会导致需要不同的算法进行处理。我们希望算法解决问题的效率越快越好,于是我们就需要考虑数据究竟如何保存的问题,这就是数据结构。
在上面的问题中我们可以选择Python中的列表或字典来存储学生信息。列表和字典就是Python内建帮我们封装好的两种数据结构。
概念
数据是一个抽象的概念,将其进行分类后得到程序设计语言中的基本类型。如:int,float,char等。数据元素之间不是独立的,存在特定的关系,这些关系便是结构。数据结构指数据对象中数据元素之间的关系。
Python给我们提供了很多现成的数据结构类型,这些系统自己定义好的,不需要我们自己去定义的数据结构叫做Python的内置数据结构,比如列表、元组、字典。而有些数据组织方式,Python系统里面没有直接定义,需要我们自己去定义实现这些数据的组织方式,这些数据组织方式称之为Python的扩展数据结构,比如栈,队列等。
算法与数据结构的区别
数据结构只是静态的描述了数据元素之间的关系。
高效的程序需要在数据结构的基础上设计和选择算法。
程序 = 数据结构 + 算法
总结:算法是为了解决实际问题而设计的,数据结构是算法需要处理的问题载体
抽象数据类型(Abstract Data Type)
抽象数据类型(ADT)的含义是指一个数学模型以及定义在此数学模型上的一组操作。即把数据类型和数据类型上的运算捆在一起,进行封装。引入抽象数据类型的目的是把数据类型的表示和数据类型上运算的实现与这些数据类型和运算在程序中的引用隔开,使它们相互独立。
最常用的数据运算有五种:
- 插入
- 删除
- 修改
- 查找
- 排序