Временная и пространственная сложность алгоритма рекурсии файлов

Подходит ли мой анализ времени и пространства для этого алгоритма?

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)
  1. Строка ниже обнаружит все файлы по заданному пути, проверив, есть ли .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]
  1. Точно так же переход по 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. Входное пространство

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

    Рекурсия использует так называемый «стек вызовов». Когда функция вызывает себя, эта функция идет поверх стека.

    • In this algorithm, the recursive function is exhausted only if it has traversed every directory. In other words, when it has reached the depth. Therefore O(depth) space is required in the call stack.
  3. Общая сложность пространства => Входное пространство + Вспомогательное пространство => O(e + depth)


person Suhas Srivats Subburathinam    schedule 16.04.2020    source источник
comment
Зачем вам нужно ограничивать имена папок, содержащие .?   -  person Jim Mischel    schedule 16.04.2020
comment
И вы наверняка захотите немного улучшить свое состояние. Вы ищете ('.' + suffix) in file. Это перехватит .com, .cfg, my.cool.file и все остальное, что содержит .c.   -  person Jim Mischel    schedule 16.04.2020
comment
Хорошее предложение. В этом случае предположим, что в именах папок нет ., а в именах файлов есть только один .. Мне нужна ваша помощь, чтобы подтвердить мой анализ времени / пространства. Код отлично работает для данного варианта использования. В основном это структура папок: ./testdir ./testdir/subdir1 ./testdir/subdir1/a.c ./testdir/subdir1/a.h ./testdir/subdir2 ./testdir/subdir2/.gitkeep ./testdir/subdir3 ./testdir/subdir3/subsubdir1 ./testdir/subdir3/subsubdir1/b.c ./testdir/subdir3/subsubdir1/b.h ./testdir/subdir4 ./testdir/subdir4/.gitkeep ./testdir/t1.c ./testdir/t1.h   -  person Suhas Srivats Subburathinam    schedule 16.04.2020
comment
Мне кажется, что сложность равна O(path_elements), где path_elements - общее количество элементов, возвращаемых всеми вызовами os.listdir. Чтобы получить 100 файлов из 10 разных папок, требуется немного больше, чем только из двух, но это зависит от константы. Однако фактическое время будет несколько другим.   -  person Jim Mischel    schedule 17.04.2020
comment
Как насчет сложности рекурсии?   -  person Suhas Srivats Subburathinam    schedule 17.04.2020


Ответы (1)


Я думаю, что сложность составляет 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).

person Jim Mischel    schedule 17.04.2020
comment
Большое спасибо за подробное объяснение. Теперь это имеет смысл. - person Suhas Srivats Subburathinam; 17.04.2020