序文
今日itertoolsは、私自身の学習経験を共有し、Pythonは非常に実用的な方法の多様性を含むライブラリが付属していますitertools、基本的な理解によって、私はそれが大幅に作業効率を向上させることができました。
itertoolsについてまず詳細は、私は公式ドキュメントを参照してくださいすることはPythonの3.7である:itertools -効率的なループ実行のためのイテレータ関数の作成には、私達は行くと見ることができます興味を持っている、何の中国語版がありません、非常に申し訳ありません、として、1をTucaoためにそこにいました日本語、韓国語は、中国語版はそれで何を維持しませんか?
ストーリーブックルール、私はクールな事だと思うのpython3をitertools!
あなたはそれを聞いていない場合、あなたは最大の隠された宝物のpython3標準ライブラリの一つを逃している、はい、私すぐに捨てられただけで共有収集モジュール:Pythonの研究ノートの白(7)魔法の宝コレクション結局、男性は大きなサブ速歩です
itertools機能モジュールを学ぶために利用可能な多くの優れたオンラインリソースがあります。しかし、私は公式文書が常に良い出発点だと思います。この記事はからの照合文書に基本的に基づいています。
私の全体的な感じは、それが含まれているitertools関数の機能について知っていることは十分にあることを知った後、コンテストを持っていません。本当の力を作成するために、これらの機能の組み合わせにあり、高速を、非常に少ないメモリ効率、美しくエレガントかかるコードを。
あなたが開始する前に、この長い記事では、私はかなりの科学リテラシーは、以下を参照してもよいことを何私の友人のイテレータとジェネレータ分からない場合は、完全コピーに向けて細部を私の学習プロセスの包括的な見直しを行います。
マジックitertools ##
着実に役立つ座って、私たちは、文書の正式な定義によれば、電車に乗るために準備ができています:
このモジュールは、APL、ハスケル、およびSMLの構造に触発反復子ビルディング・ブロックの数を実装しています。それぞれは、Pythonに適した形に作り直してきました。
それはおそらく、イテレータを構築したモジュールの数の実現である翻訳、彼らは、その上の効率を向上させることができます...... APL、ハスケルとSMLからのものを構築し、きっかけ
これは主機能は、より複雑なイテレータを生成するためにitertools反復子「操作」であることを意味します。例えば、パラメータとしてイテレート可能オブジェクトの任意の数およびタプル対応する要素にイテレータを返す組み込みのZIP()関数を考慮してください。
print(list(zip([1, 2, 3], ['a', 'b', 'c'])))
Out:[(1, 'a'), (2, 'b'), (3, 'c')]
复制代码
ここではそれがどのように動作するか最後にジップ?
他のすべてのリスト、[1,2,3]および[「A」、「B」、「C」]のように、それらが時間要素を返すことができることを意味し、反復的です。技術的には、任意の実装:
-
.__ ITER __()
-
または.__ __getitem __ __()
Pythonオブジェクトのメソッドが利用できるの繰り返しです。あなたはこの点で疑問を持っている場合は、私たちは、前文で述べたチュートリアルを見ることができます
オブジェクトまたは他の反復xのリストに呼び出されたとき、xは、独自のイテレータオブジェクトに戻ります、ITER()組み込み関数について:
iter([1, 2, 3, 4])
iter((1,2,3,4))
iter({'a':1,'b':2})
Out:<list_iterator object at 0x00000229E1D6B940>
<tuple_iterator object at 0x00000229E3879A90>
<dict_keyiterator object at 0x00000229E1D6E818>
复制代码
実際、ZIP各パラメータITER(上に呼び出すことによって()関数)は、次の()推進ITER()を使用し、各反復によって返さするタプルの重合の結果が達成されます。ジップ()返されたタプルを反復
そして、私はリコールする必要があり、前に書き込み白いPythonの研究ノート(5)マップ、フィルタ、削減、ジップ概要マップ()組み込み関数を導入する、実際には、また、イテレータ操作のようなものですそれは最も単純な形式単一引数関数は、反復配列の各要素に適用することができる、破壊します。
- テンプレート:マップ(FUNC、シーケンス)
list(map(len, ['xiaobai', 'at', 'paris']))
Out: [7, 2, 5]
复制代码
見つけるのは難しいことではありません参照マップテンプレート:順番にITERを呼び出すことにより、マップ()関数()、イテレータが使い果たされ、そしてfuncは次の()内の各ステップに適用されるまで、この反復子によって返された値を促進するために、次の()を使用します。上記の例では、これによりリスト内の各要素の長さイテレータを返す、各要素LEN [「xiaobai」、「で」、「パリ」]()に呼びかけ
反復子が反復的であり、したがって(ZIPができる)と反復の組み合わせで複数の要素を生成するマップ()イテレータため。例えば、2個の合計の対応する要素のリスト次
a = [1, 2, 3]
b = [4, 5, 6]
list(map(sum, zip(a,b)))
Out: [5, 7, 9]
复制代码
この例では、いわゆる良いitertools構築する方法を説明し、「イテレータ代数」の機能を意味します。建物のレンガのセットは和として場合のように、特別な「データパイプ」を形成するために組み合わせることができるよう私たちは見ることができますitertools。
これら2つの関数はイテレータを返すので)(実際には、Pythonの3で、私たちはマップを使用する場合()とジップ、それは、itertoolsを使用されています!
これは、いわゆる内itertools使用「イテレータ代数」のメリットは2つあり:
- メモリ効率を向上させる(遅延評価)
- スピード
両方の友人に利益があるかもしれない質問があり、私たちは特定のシナリオを分析することができ、心配しないでください。
今、私たちは、n個の長さの関数としてグループにリスト分割を準備するために、リストと正の整数nを持っています。簡単にするために、リストの入力の長さがNで割り切れることができると仮定する。例えば、入力場合= [1,2,3,4,5,6]、およびn = 2、関数は、[(1,2)、(3,4)、(5,6)]を返すべきです。
私たちは、最初に次のように解決策があるかもしれないと思いました。
def naive_grouper(lst, n):
num_groups = len(lst) // n
return [tuple(lst[i*n:(i+1)*n]) for i in range(num_groups)]
复制代码
私たちの簡単なテスト、結果は正しいです。
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
naive_grouper(nums, 2)
Out: [(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]
复制代码
しかし、問題は、我々は億個の要素を含むリストを渡すしようとした場合何が起こるか、ですか?私たちは、多くのメモリを必要とします!十分なメモリがある場合でも、最終的な結果が生成されるまで、プログラムは、いくつかの時間のために中断されます
私たちはこの時間を使用している場合は、イテレータ内のitertoolsを大幅に状況を改善することができます。
def better_grouper(lst, n):
iters = [iter(lst)] * n
return zip(*iters)
复制代码
この方法では、我々は今、よりオープンな表情、表現[iters(LST)] * nはnは同じイテレータへの参照のリストを作成し、大量の情報が少し含まれています:
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
iters = [iter(nums)] * 2
list(id(itr) for itr in iters) # Id 没有变化,就是创建了n个索引
Out: [1623329389256, 1623329389256]
复制代码
次に、反復子はitersの各反復においてZIP(* iters)に対応する要素を返しています。最初の要素1は、それが「最初」イテレータにだけ言及だから、今すぐ開始し、その一歩を取るために、「最初の」イテレータ2から「第二」イテレータから取得された場合。したがって、ZIP()は、最初のタプルが(1,2)で生成します。
この時点で、いわゆる「二」イテレータ3つの開始、ZIPを(iters)(「第二」を生成する4タプルから得られるイテレータの「第1」から引き出さ3、 3、4)。このプロセスは、ZIP()最終世代(9、10)へと続くと、「二つの」イテレータが使い果たされるiters。
注意:番号変更ので、ここでは「第1」、「第2」、「2」は、イテレータを指しています!!
最後に、我々は結果は同じであることがわかります。
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
list(better_grouper(nums, 2))
Out: [(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]
复制代码
しかし、ここで私がテストを行なったし、二つのメモリ消費量が大きな違いであることがわかったが、また、ITERのzip(の使用)+組み合わせ、より速くより500倍を実行するために、我々は興味を持っている自分自身をテストすることができ、NUMSに入れます範囲(100000000)に
今度は、ちょうど書かbetter_grouper(LST、n)の方法、見つけるのは難しいことではありませんを見てみましょう、この方法の明らかな欠点があります:私たちは、LSTすることができないのnで割り切れる長さを渡す場合に実行する場合、重大な問題があるでしょう。
>>> nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> list(better_grouper(nums, 4))
[(1, 2, 3, 4), (5, 6, 7, 8)]
复制代码
パケット出力の要素9と10がありません。これは、反復の最短数に渡された後に排出されるので、ZIP()要素は、重合を停止する起こります。そして、我々はすべての要素が欠落していませんします。だから、解決策は、我々が使用できるということです)itertools.zip_longestを(それはパラメータのイテレート可能オブジェクトの任意の数を受け入れ、このキーワードをfillvalueことができ、デフォルトはNoneです。私たちは、簡単な例を見て
>>> import itertools as it
>>> x = [1, 2, 3, 4, 5]
>>> y = ['a', 'b', 'c']
>>> list(zip(x, y)) # zip总是执行完最短迭代次数停止
[(1, 'a'), (2, 'b'), (3, 'c')]
>>> list(it.zip_longest(x, y))
[(1, 'a'), (2, 'b'), (3, 'c'), (4, None), (5, None)]
复制代码
この例は非常に明確になってジップ()とzip_longest()の違いを反映して、そして今、私たちはbetter_grouper方法を最適化することができました。
import itertools as it
def grouper(lst, n, fillvalue=None):
iters = [iter(lst)] * n
return it.zip_longest(*iters, fillvalue=fillvalue) # 默认就是None
复制代码
私たちは、最適化されたテストを見て:
>>> nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> print(list(grouper(nums, 4)))
[(1, 2, 3, 4), (5, 6, 7, 8), (9, 10, None, None)]
复制代码
非常に良いされている、とあなたの学生が認識していない可能性が、私たちは行っているがハタメソッド内のすべてのプロセス全体のitertoolsを作成することです!
今度は本物を見てみましょう公式ドキュメント書き込まれるハタ方法:
そして、我々は反復可能で*引数を使用して、複数のパラメータを受け入れることができますを除いて、基本的に同じ書き込み
最後に満足のテスト:
from itertools import zip_longest
def grouper(iterable, n, fillvalue=None):
"Collect data into fixed-length chunks or blocks"
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
args = [iter(iterable)] * n
return zip_longest(*args, fillvalue=fillvalue)
test_list = list(grouper("ABCDEFG",3))
test_tuple = tuple(grouper(range(0,7),2,'Null'))
test_dict = dict(zip(test_list,test_tuple))
复制代码
出力:
print(test_list)
Out:[('A', 'B', 'C'), ('D', 'E', 'F'), ('G', None, None)]
复制代码
print(test_tuple)
Out:((0, 1), (2, 3), (4, 5), (6, 'Null'))
复制代码
print(test_dict)
Out: {('A', 'B', 'C'): (0, 1), ('D', 'E', 'F'): (2, 3), ('G', None, None): (4, 5)}
复制代码
解決暴力(ブルートフォース)
まず、リテラシーの基本的な考え方は、簡単な言葉で、いわゆる暴力を解決するためのアルゴリズムであるあるすべての状況の列挙、または他の方法を使用して問題を解決するために、コンピューティング・スキルの多くを持っていません。私は暴力アルゴリズムの広い概念を読んだ後、最初に考えたのは、実際に王脂肪の墓であります
あなたが墓の友人を見ている場合、あなたは王脂肪が実際に解決するために暴力の尊敬の男であることがわかります、「列挙法」に遭遇する数々の困難に自分自身を再配置暴力を解決することであり、例えば、私が最も感動ゲンティン寺でしたジュエリーのフルクローゼットの中に閉じ込められた歩行者に、あなたは直接の「幽霊の周りに」最終的な解決策からあらゆる可能性を、ルールを列挙することによって、王の脂肪を免れることはできません。
PS:ここではおじさんを送信するために韓国を敬礼し、彼のピットに記入していない人
脱線は、バック現実に、私たちはしばしば、次の古典的なトピックが発生します。
次の3枚の$ 20札、5枚の$ 10札、2枚の$ 5札と5枚の$ 1札を持っています。$ 100どのように多くの異なる方法を得ることができますか?
問題をブルートフォースするために、私たちだけの組み合わせの全ての可能性のリストを出し、その後、$ 100の組み合わせを見つけるには、すべての最初にすることができ、私たちの手の中にすべてのドルを含む、のリストを作成してみましょう:
bills = [20, 20, 20, 10, 10, 10, 10, 10, 5, 5, 1, 1, 1, 1, 1]
复制代码
ここではitertoolsたちを助けます。itertools.combinations()は 2つのパラメータを受け付け
- 反復入力
- 正の整数n
最終的にn個の要素のすべての組み合わせの入力要素に反復子を生成します。
import itertools as it
bills = [20, 20, 20, 10, 10, 10, 10, 10, 5, 5, 1, 1, 1, 1, 1]
result =list(it.combinations(bills, 3))
print(len(result)) # 455种组合
print(result)
Out: 455
[(20, 20, 20), (20, 20, 10), (20, 20, 10), ... ]
复制代码
私だけで、残りの高校の数学は3(上付き文字)の問題、これはC 15(添字)の確率の内側に実際に私に語っています、だけでなく、今私たちは、様々な組み合わせを持っているので、我々は唯一の様々な組み合わせで必要選択等しい100の総数は、問題が解決されます。
makes_100 = []
for n in range(1, len(bills) + 1):
for combination in it.combinations(bills, n):
if sum(combination) == 100:
makes_100.append(combination)
复制代码
このようにして得られた結果は、繰り返しの組み合わせを含まれている、我々は最終的に答えを得る、ストレートセットで最終的に重複する値をフィルタリングすることができます:
import itertools as it
bills = [20, 20, 20, 10, 10, 10, 10, 10, 5, 5, 1, 1, 1, 1, 1]
makes_100 = []
for n in range(1, len(bills) + 1):
for combination in it.combinations(bills, n):
if sum(combination) == 100:
makes_100.append(combination)
print(set(makes_100))
Out:{(20, 20, 10, 10, 10, 10, 10, 5, 1, 1, 1, 1, 1),
(20, 20, 10, 10, 10, 10, 10, 5, 5),
(20, 20, 20, 10, 10, 10, 5, 1, 1, 1, 1, 1),
(20, 20, 20, 10, 10, 10, 5, 5),
(20, 20, 20, 10, 10, 10, 10)}
复制代码
だから、最後に、私たちは5つの方法の合計を発見しました。今の質問がされて話題を変えてみましょう、それは完全に異なっています:
今すぐ変更に$ 100札を取る、あなたは$ 50から$ 20 $ 10 $ 5 $ 1札の任意の番号を使用することができ、どのように多くの方法?
この場合、我々は法案の一定量を持っていないので、我々はすべての可能な組み合わせの任意の番号を使用して請求書を生成するための方法が必要です。この目的のために、我々は** itertools.combinations_with_replacement()**機能を使用する必要があります。
これは、入力受け付けた入力と、反復nは正の整数の組合せ()と同様であり、nタプルから入力されたイテレータを返します。)(そのcombination_with_replacement以外タプルを返す反復要素が小さな栗を見ることができます。
>>> list(it.combinations_with_replacement([1, 2], 2)) #自己和自己的组合也可以
[(1, 1), (1, 2), (2, 2)]
复制代码
itertools.combinationsコントラスト():
>>> list(it.combinations([1, 2], 2)) #不允许自己和自己的组合
[(1, 2)]
复制代码
新しい問題、以下の解決のためにそう:
bills = [50, 20, 10, 5, 1]
make_100 = []
for n in range(1, 101):
for combination in it.combinations_with_replacement(bills, n):
if sum(combination) == 100:
makes_100.append(combination)
复制代码
我々はこの方法を組み合わせて繰り返されることはありませんので、重い行く必要はありません最終結果:
>>> len(makes_100)
343
复制代码
あなたはそれを自分で実行する場合は、出力には時間がかかるだろうことがあります。それは96560645個の組み合わせで対処する必要があるからです!ここでは、解決するために暴力の実装であります
別の「暴力」機能は、単一の反復可能に受け入れ、(再配置)に配置されているすべての可能な要素を生成itertools順列()、です。
>>> list(it.permutations(['a', 'b', 'c']))
[('a', 'b', 'c'), ('a', 'c', 'b'), ('b', 'a', 'c'),
('b', 'c', 'a'), ('c', 'a', 'b'), ('c', 'b', 'a')]
复制代码
(このようなリストなど)のiterableの三つの要素のいずれかが6が配置されています、そして長い反復に配置されたオブジェクトの数は非常に速く成長します。実際には、イテレート可能オブジェクトの長さnがn個あります!編曲:
入力の組み合わせを使用する(で組み合わせ爆発として知られる現象)、combinations_with_replacement()および順列(大量のごく少数の結果)我々はこれを覚えておく必要があります。
正直に言うと、このアルゴリズムは、暴力を避けることが最善ですが、時には我々は、使用する必要があります(このようなアルゴリズムの基本的な正しさとして、またはすべての可能な結果を考慮しなければなりません)
概要
限られたスペースのため、私はこの記事では、我々は次のような機能の基本原則の主の深い理解され、ここで共有します:
- 地図()
- 郵便番号()
- itertools.combinations
- itertools.combinations_with_replacement
- itertools.permutations
次回の記事で私は、最初の最後の三つをまとめ、その後、itertools内部の魔法のあらゆる種類のものを共有していきます
ます。https://juejin.im/post/5d075227f265da1bad570620で再現