Enquanto estudo programação, encontro exemplos de algoritmos impossíveis. A intuição diz que não pode ser, mas o computador refuta simplesmente executando o código. Como esse problema, que requer um mínimo de custos cúbicos no tempo, pode ser resolvido em apenas um quadrado? E esse eu definitivamente decidirei para a linha. O que? Existe um algoritmo de logaritmo muito mais eficiente e elegante? Surpreendente!
Neste artigo, apresentarei vários desses algoritmos de "quebra de padrões", mostrando que a intuição pode superestimar muito a complexidade de tempo de um problema.
Interessante? Bem-vindo ao corte!
Cálculo do enésimo elemento da sequência recorrente no logaritmo
Por "recorrente", quero dizer uma sequência que satisfaça a seguinte equação:
O primeiro elementos da sequência são considerados dados. Número é chamado decardinalidade dasequência, e coeficientes i da sequência. Exemplo típico: números de Fibonacci, onde , , , . Obtemos os números conhecidos: 0, 1, 1, 2, 3, 5, 8, 13, ... Parece que não há dificuldade em calcular o enésimo elemento por linha, mas acontece que pode ser feito para um logaritmo!
Idéia: e se você imaginar um cálculo como ereção em enésima potência de algum objeto? Você pode elevar a uma potência para o logaritmo, mas que tipo de objeto será? Em geral, o que você precisa saber para calcular ? Somente . , , . - "" . ? : ! , , ?
, ! :
,
, ? ? , :
, " " . .
? , . , . :
,
,
, ,
,
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]
. :
k < 0
. 0k
max
.k = 0
.max
.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
Em ambas as tarefas, a técnica de programação dinâmica nos ajudou a melhorar qualitativamente o desempenho. Isso não é coincidência, a dinâmica geralmente fornece algoritmos assintoticamente ideais graças à economia embutida: contamos tudo apenas uma vez.
Que algoritmos incríveis você conhece?