私は、任意の関数のBigO表記を計算する方法を自分自身に教えようとしています。この機能は教科書で見つけました。この本は、関数がO(n 2)であると断言しています。これがなぜなのかを説明していますが、私はそれに従うのに苦労しています。これがなぜそうなのか、誰かが数学を教えてくれるのではないかと思います。基本的に、O(n 3)より小さいことを理解していますが、O(n 2)に単独で着陸することはできませんでした
A、B、Cの3つの数字のシーケンスが与えられたと仮定します。個々のシーケンスには重複した値が含まれていないが、2つまたは3つのシーケンスにある数字があると仮定します。3方向の集合のばらばらの問題は、3つのシーケンスの共通部分が空かどうか、つまり、x∈A、x∈B、x∈Cのような要素xがないかどうかを判断することです。
ちなみに、これは私にとって宿題の問題ではありません-その船は何年も前に航海しました:)、私はもっと賢くしようとしています。
def disjoint(A, B, C):
"""Return True if there is no element common to all three lists."""
for a in A:
for b in B:
if a == b: # only check C if we found match from A and B
for c in C:
if a == c # (and thus a == b == c)
return False # we found a common value
return True # if we reach this, sets are disjoint
[編集]教科書によると:
改良版では、運が良ければ時間を節約するだけではありません。互いに素な最悪の実行時間はO(n 2)であると主張します。
私が従うのに苦労している本の説明はこれです:
全体の実行時間を説明するために、各コード行の実行に費やされた時間を調べます。Aに対するforループの管理にはO(n)時間を必要とします。Bに対するforループの管理は、そのループがn回実行されるため、合計O(n 2)時間を占めます。テストa == bはO(n 2)回評価されます。残りの時間は、一致する(a、b)ペアの数に依存します。前述したように、このようなペアは最大でn個あるため、Cを介したループの管理、およびそのループの本体内のコマンドは、最大でO(n 2)時間を使用します。費やされた合計時間はO(n 2)です。
(そして、適切な信用を与えるために...)本は、マイケル・T・グッドリッチらによるPythonのデータ構造とアルゴリズムです。すべて、Wiley Publishing、pg。135
[編集]正当化; 以下は最適化前のコードです。
def disjoint1(A, B, C):
"""Return True if there is no element common to all three lists."""
for a in A:
for b in B:
for c in C:
if a == b == c:
return False # we found a common value
return True # if we reach this, sets are disjoint
上記では、各ループを最大限に実行する必要があるため、これがO(n 3)であることが明確にわかります。本は、(最初に与えられた)単純化された例では、3番目のループはO(n 2)の複雑さだけであると断言するので、複雑さの方程式はk + O(n 2)+ O(n 2) O(n 2)。
これが事実であることを証明することはできませんが(質問)、簡略化されたアルゴリズムの複雑さは少なくとも元のものよりも小さいことに読者は同意できます。
[編集]そして、簡易版が2次であることを証明するために:
if __name__ == '__main__':
for c in [100, 200, 300, 400, 500]:
l1, l2, l3 = get_random(c), get_random(c), get_random(c)
start = time.time()
disjoint1(l1, l2, l3)
print(time.time() - start)
start = time.time()
disjoint2(l1, l2, l3)
print(time.time() - start)
利回り:
0.02684807777404785
0.00019478797912597656
0.19134306907653809
0.0007600784301757812
0.6405444145202637
0.0018095970153808594
1.4873297214508057
0.003167390823364258
2.953308343887329
0.004908084869384766
2番目の差は等しいため、単純化された関数は実際には2次関数です。
[編集]そしてさらにさらなる証拠:
最悪の場合(A = B!= C)を仮定すると、
if __name__ == '__main__':
for c in [10, 20, 30, 40, 50]:
l1, l2, l3 = range(0, c), range(0,c), range(5*c, 6*c)
its1 = disjoint1(l1, l2, l3)
its2 = disjoint2(l1, l2, l3)
print(f"iterations1 = {its1}")
print(f"iterations2 = {its2}")
disjoint2(l1, l2, l3)
収量:
iterations1 = 1000
iterations2 = 100
iterations1 = 8000
iterations2 = 400
iterations1 = 27000
iterations2 = 900
iterations1 = 64000
iterations2 = 1600
iterations1 = 125000
iterations2 = 2500
2番目の差分テストを使用すると、最悪の場合の結果は正確に2次です。