Пространственно-временная сложность алгоритма File Recursion - PullRequest
0 голосов
/ 16 апреля 2020

Является ли мой анализ времени и пространства правильным для этого алгоритма?

def find_files(suffix, path):
if suffix == '':
    return []

# Base condition
if len(os.listdir(path)) == 0:
    return []

path_elements = os.listdir(path)
path_files = [file for file in path_elements if ('.' + suffix) in file]
path_folders = [folder for folder in path_elements if '.' not in folder]

for folder in path_folders:
    path_files.extend(find_files(suffix=suffix, path=path + '/' + folder))

return path_files

# Tests
path_base = os.getcwd() + '/testdir'
print(find_files(suffix='c', path=path_base))

Объяснение: Рекурсия файла

Требование:

Цель - написать код для поиска всех файлов в каталоге (и всех каталогах под ним), которые заканчиваются на ". c"

Сводка:

Для реализации Для этого я использовал функцию find_files, которая будет принимать suffix (расширение файла) и path (путь к каталогу, в котором мы должны искать). В этой функции я рекурсивно ищу файл с заданным расширением в родительском каталоге и во всех его подкаталогах. Я храню все эти файлы с заданным суффиксом в списке и возвращаю его.

Сложность времени и пространства:

Сложность времени:

os.listdir (путь) выведет список всех файлов и папок по указанному пути. Поскольку для получения списка приходится перебирать каждый элемент, требуется сложность линейного времени. Пусть e будет длиной path_elements => O(len(path_elements)) => O(e)

path_elements = os.listdir(path)
В нижней строке будут найдены все файлы по заданному пути, проверяя, есть ли в имени файла .c. Если s - длина строки (имя файла / папки), то O(s) требуется время, чтобы найти файл с заданным расширением. Поэтому для f количества файлов требуется O(f*s) времени. Для практических целей, таких как этот, справедливо предположить, что имена файлов НЕ являются бесконечно длинными. Следовательно, мы можем принять s как константу => O(f * 1) => O(f).
path_files = [file for file in path_elements if ('.' + suffix) in file]
Аналогично, обход каталогов g займет линейное время => O(g)
path_folders = [folder for folder in path_elements if '.' not in folder]

Вышеупомянутые 3 шага будут выполняться => O(e + f + g) => O(e). Поскольку e (len(path_elements)) является доминирующим термином, поскольку он представляет собой комбинацию path_files и path_folders

  • Для рекурсии, если k - это число каталогов (branches) на каждом уровне в depth, тогда рекурсия займет O(brancher^depth) => O(k^depth). Но для каждого рекурсивного вызова выполняются три вышеуказанных шага. Таким образом, общая сложность времени составляет O((k^depth) * e

Сложность пространства:

  1. Пространство ввода

    • суффикс - O(1)
    • путь - O(1)
    • path_elements + path_files + path_folders => O(e + f + g) => O(e)
  2. Вспомогательное пространство

    В рекурсии используется нечто, называемое "стек вызовов". Когда функция вызывает себя, эта функция попадает на вершину стека.

    • В этом алгоритме рекурсивная функция исчерпывается, только если она прошла через все каталоги. Другими словами, когда он достиг depth. Поэтому в стеке вызовов требуется O(depth) пространство.
  3. Общая сложность пространства => Пространство ввода + Вспомогательное пространство => O(e + depth)

1 Ответ

1 голос
/ 17 апреля 2020

Мне кажется, сложность O (path_elements), где path_elements - общее количество файлов и папок, возвращаемых всеми вызовами os.listdir(). Рассмотрим папку с четырьмя файлами в одном каталоге:

folder
  zip.c
  foo.c
  bar.c
  bas.c

Итак, ваша функция вызывает os.listdir(), которая возвращает четыре файла. Так что n = 4. Затем код перебирает список, чтобы найти папки, из которых он не находит ни одной, и снова выбирает файлы. c, которые он добавляет в список.

Сложность здесь O (n) вызвать os.listdir(), O (n) для поиска папок и O (n), чтобы выбрать. c файлы и добавить их в список. Всего O (3 * n), то есть O (n).

Теперь рассмотрим это дерево каталогов:

folder
  sub1
    zip.c
    foo.c
  sub2
    bar.c
    bas.c

Итак, первый вызов find_files вызывает os.listdir(folder), что возвращает две папки. Каждая папка выполняет рекурсивный вызов find_files. Всего к os.listdir() поступают три вызова, и каждый возвращает два файла, поэтому общее количество элементов, возвращаемых os.listdir(), равно 6.

Теперь представьте, что у вас было:

folder
  sub0
    sub1
      sub2
        sub3
          ...
            sub30
              file1.c

Сложность здесь все еще O (n). В данном случае n равно 31.

Здесь я пытаюсь подчеркнуть, что вы смотрите на каждый элемент, возвращаемый с os.listdir() постоянное число раз. Таким образом, O (n).

...