プログラミングを勉強していると、不可能なアルゴリズムの例に出くわします。直感ではこれは不可能だと言われていますが、コンピューターはコードを実行するだけで反論します。時間内に最小の立方体コストを必要とするこのような問題を、どのようにして1つの正方形で解決できるでしょうか。そして、それは私が間違いなくラインのために決めるでしょう。何?はるかに効率的でエレガントな対数アルゴリズムはありますか?すごい!
この記事では、これらの「パターンを壊す」アルゴリズムのいくつかを紹介し、直感が問題の時間計算量を大幅に過大評価する可能性があることを示します。
面白い?カットの下でようこそ!
対数での反復シーケンスのn番目の要素の計算
「再発」とは、次の式を満たすシーケンスを意味します。
最初 シーケンスの要素は指定されていると見なされます。数 シーケンス のカーディナリティと呼ばれ、 シーケンスの係数。典型的な例:フィボナッチ数、ここで 、 、 、 ..。よく知られている数値を取得します:0、1、1、2、3、5、8、13、...行ごとにn番目の要素を計算するのは難しいことではないようですが、それは可能であることがわかります対数のために!
アイデア:計算を想像するとどうなるか 勃起として - - ? , ? , ? . , , . - "" . ? : ! , , ?
, ! :
,
, ? ? , :
, " " . .
? , . , . :
,
,
, ,
,
Matrix :
class Matrix:
def __init__(self, n):
self.n = n
self.rows = [[0 for col in range(n)] for row in range(n)]
def set(self, row, col, value):
self.rows[row][col] = value
def get(self, row, col):
return self.rows[row][col]
def __str__(self):
result = ''
for row in self.rows:
result += ' '.join([str(col) for col in row])
result += '\n'
return result
def __mul__(self, other):
result = Matrix(self.n)
for row in range(self.n):
for col in range(self.n):
s = sum([self.get(row, k) * other.get(k, col) for k in range(self.n)])
result.set(row, col, s)
return result
def __len__(self):
return self.n
def __pow__(self, k):
if k == 0:
result = Matrix(len(self))
for i in range(len(self)):
result.set(i, i, 1)
elif k == 1:
result = self
elif k == 2:
result = self * self
else:
rem = k % 3
prev = self.__pow__((k - rem) // 3)
result = prev * prev * prev
if rem:
result *= self.__pow__(rem)
return result
__pow__
: M ** k
, M
Matrix
. , 3. .
Matrix
:
A = Matrix(3)
A.set(0, 0, 1)
A.set(0, 1, 1)
A.set(1, 0, 1)
A.set(1, 2, 1)
A.set(2, 0, 1)
T = Matrix(3)
T.set(0, 0, 3)
T.set(0, 1, 1)
T.set(0, 2, 1)
T.set(1, 0, 1)
T.set(1, 1, 1)
T.set(1, 2, 1)
T.set(2, 0, 1)
T.set(2, 1, 1)
T.set(2, 2, 0)
n = int(sys.argv[1])
if n:
print(T * A ** (n - 1))
else:
print(T ** 0)
: A[1..n]
( ). A[i..j]
. i
j
. ,
:
- . , . . , .
- . , .
-
. , : , , .O ( n log n ) -
.O ( n ) T[1..n]
,i
- ,i
. , ,T .T
. , T[i + 1]
, T[i]
? , i
, , . , T[i + 1]
T[i] + A[i + 1]
, A[i + 1]
, 0, A[i + 1] < 0
. :
T[0] = 0, T[i + 1] = max{T[i] + A[i + 1], A[i + 1], 0} = max{T[i] + A[i + 1], 0}
最後の平等を証明しましょう。T[i] >= 0
誰にとっても明らかですi
。しましょうk = A[i + 1]
。3つのケースを考えてみましょう。
k < 0
..。その場合k
、最初ので0がパフォーマンスを上回りmax
ます。k = 0
..。最初のmax
引数では、2番目の引数を削除するだけです。k > 0
..。次にmax{T[i] + k, k, 0} = T[i] + k = max{T[i] + k, 0}
。
方程式の線形性と単純さのために、アルゴリズムは非常に短いです。
def kadane(ints):
prev_sum = 0
answer = -1
for n in ints:
prev_sum = max(prev_sum + n, 0)
if prev_sum >= answer:
answer = prev_sum
return answer
結論
どちらのタスクでも、動的計画法はパフォーマンスを質的に向上させるのに役立ちました。これは偶然ではありません。組み込みの経済性のおかげで、ダイナミクスは漸近的に最適なアルゴリズムを提供することがよくあります。すべてを1回だけカウントします。
あなたはどんな素晴らしいアルゴリズムを知っていますか?