Während des Programmierstudiums stoße ich auf Beispiele für unmögliche Algorithmen. Intuition sagt, dass dies nicht sein kann, aber der Computer widerlegt es, indem er einfach den Code ausführt. Wie kann ein solches Problem, das ein Minimum an kubischen Kosten in der Zeit erfordert, in nur einem Quadrat gelöst werden? Und diesen werde ich definitiv für die Linie entscheiden. Was? Gibt es einen viel effizienteren und eleganteren Logarithmus-Algorithmus? Tolle!
In diesem Artikel werde ich einige dieser "Musterbrechungs" -Algorithmen vorstellen, die zeigen, dass die Intuition die zeitliche Komplexität eines Problems stark überschätzen kann.
Interessant? Willkommen unter dem Schnitt!
Berechnung des n-ten Elements der wiederkehrenden Folge im Logarithmus
Mit "wiederkehrend" meine ich eine Sequenz, die die folgende Gleichung erfüllt:
Der Erste Elemente der Sequenz gelten als gegeben. Nummer heißt die Kardinalität der Sequenz, und Koeffizienten der Sequenz. Typisches Beispiel: Fibonacci-Zahlen, wobei , , , ... Wir erhalten die bekannten Zahlen: 0, 1, 1, 2, 3, 5, 8, 13, ... Es scheint, dass es keine Schwierigkeit gibt, das n-te Element pro Zeile zu berechnen, aber es stellt sich heraus, dass dies möglich ist für einen Logarithmus!
Idee: Was ist, wenn Sie sich eine Berechnung vorstellen? als Erektion in - - ? , ? , ? . , , . - "" . ? : ! , , ?
, ! :
,
, ? ? , :
, " " . .
? , . , . :
,
,
, ,
,
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}
Beweisen wir die letzte Gleichheit. Es ist klar, dass T[i] >= 0
für jeden i
. Lass k = A[i + 1]
. Betrachten Sie drei Fälle:
k < 0
... Dann wird 0k
in der ersten übertreffenmax
.k = 0
... Im erstenmax
können Sie einfach das zweite Argument entfernen.k > 0
... Dannmax{T[i] + k, k, 0} = T[i] + k = max{T[i] + k, 0}
.
Aufgrund der Linearität und Einfachheit der Gleichung ist der Algorithmus ziemlich kurz:
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
Fazit
Bei beiden Aufgaben hat uns die dynamische Programmiertechnik geholfen, die Leistung qualitativ zu verbessern. Dies ist kein Zufall, Dynamik liefert dank integrierter Wirtschaftlichkeit oft asymptotisch optimale Algorithmen: Wir zählen alles nur einmal.
Welche erstaunlichen Algorithmen kennen Sie?