Давайте сначала рассмотрим аналитический подход. Мы можем попробовать запустить функцию с меньшим Ns и найти шаблон.
import numpy as np
global call_cnt
def cc(arr,i,j,N):
global call_cnt
call_cnt += 1
#print(call_cnt)
if i==N or j==N or i<0 or j<0:
return
if arr[i][j]==1 or arr[i][j]=='X':
return
arr[i][j]='X'
cc(arr,i,j+1,N)
cc(arr,i,j-1,N)
cc(arr,i+1,j,N)
cc(arr,i-1,j,N)
return(arr)
n_On = []
for i in range(1,30):
global call_cnt
call_cnt = 0
cc(np.zeros((i,i)).tolist(), 0, 0, i)
print(call_cnt)
n_On.append(call_cnt)
import matplotlib.pyplot as plt
plt.plot(n_On)
plt.show()
Мы инициализировали массивы с ненулевыми значениями, чтобы мы не соответствовали базовому случаю arr [i] [j] == 1, поэтому программа будет работать в худшем случае.
После 29 мы получаем максимальную глубину рекурсии, поэтому мы просто выбрали 1-29 для нашего N.
После построения графика числа общее количество вызовов для каждого N, мы получаем этот график.
5
17
37
65
101
145
197
257
325
401
485
577
677
785
901
1025
1157
1297
1445
1601
1765
1937
2117
2305
2501
2705
2917
3137
3365
Теперь мы можем подобрать эти точки, используя множество Линии регрессии, одна вещь наверняка не экспоненциальная.
После небольшой настройки с числами, вы можете увидеть, что функция 4xN^2
очень близка к ней, на самом деле, значения идеально подходят 4xN^2 + 1
функция.
Давайте попробуем представить эту функцию поверх числа вызовов.
import matplotlib.pyplot as plt
plt.plot(n_On)
plt.plot([4*(i**2) + 1 for i in range(1,30)], 'k+')
plt.xlabel('N')
plt.ylabel('number of calls')
plt.show()
Это идеально подходит!
Теперь, почему? В вашей рекурсивной функции у вас есть в основном 2 переменные, которые меняют i и j. Поскольку i и j меняются только в диапазоне N (из-за вашей проверки i, j> 0 и i, j NxN -> N ^ 2), и вы также использовали мемоизацию (не посещая уже посещенные ячейки ), так что в общем N ^ 2.
Наконец, для каждого случая есть 4 вызова. Отсюда и эта сложность.
Итак, это O(N^2)
.