web-dev-qa-db-fra.com

Cette complexité temporelle est-elle réellement O (n ^ 2)?

Je travaille sur un problème de CTCI.

Le troisième problème du chapitre 1 vous fait prendre une chaîne telle que

'Mr John Smith '

et vous demande de remplacer les espaces intermédiaires par %20:

'Mr%20John%20Smith'

L'auteur propose cette solution en Python, en l'appelant O (n):

def urlify(string, length):
    '''function replaces single spaces with %20 and removes trailing spaces'''
    counter = 0
    output = ''
    for char in string:
        counter += 1
        if counter > length:
            return output
        Elif char == ' ':
            output = output + '%20'
        Elif char != ' ':
            output = output + char
    return output

Ma question:

Je comprends qu'il s'agit de O(n) en termes de balayage à travers la chaîne réelle de gauche à droite. Mais les chaînes de Python sont-elles immuables?) J'ai une chaîne et j'ajoute une autre chaîne avec l'opérateur +, N'alloue-t-il pas l'espace nécessaire, copie sur l'original, puis recopie sur la chaîne annexée?

Si j'ai une collection de n chaînes de longueur 1 chacune, cela prend:

1 + 2 + 3 + 4 + 5 + ... + n = n(n+1)/2

ou O (n ^ 2) fois, oui? Ou je me trompe sur la façon dont Python gère l'ajout?

Sinon, si vous êtes prêt à m'apprendre à pêcher: comment pourrais-je le découvrir par moi-même? J'ai échoué dans mes tentatives de recherche sur Google d'une source officielle. J'ai trouvé https://wiki.python.org/moin/TimeComplexity mais cela n'a rien sur les chaînes.

81
user5622964

Dans CPython, l'implémentation standard de Python, il existe un détail d'implémentation qui en fait généralement O (n), implémenté dans le code que la boucle d'évaluation du bytecode appelle + ou += avec deux opérandes de chaîne . Si Python détecte que l'argument de gauche n'a pas d'autres références, il appelle realloc pour tenter d'éviter une copie en redimensionnant la chaîne en place. Ce n'est pas quelque chose que vous devriez jamais compter on, car c'est un détail d'implémentation et parce que si realloc finit par devoir déplacer fréquemment la chaîne, les performances se dégradent quand même en O (n ^ 2).

Sans le détail d'implémentation étrange, l'algorithme est O (n ^ 2) en raison de la quantité quadratique de copie impliquée. Un code comme celui-ci n'aurait de sens que dans un langage avec des chaînes mutables, comme C++, et même en C++, vous voudriez utiliser +=.

75
user2357112

L'auteur s'appuie sur une optimisation qui se trouve ici, mais qui n'est pas explicitement fiable. strA = strB + strC Est généralement O(n), ce qui rend la fonction O(n^2). Cependant, il est assez facile de s'assurer que tout le processus est O(n), utilisez un tableau:

output = []
    # ... loop thing
    output.append('%20')
    # ...
    output.append(char)
# ...
return ''.join(output)

En bref, l'opération append est amortieO(1), (bien que vous puissiez la renforcer O(1) en pré-allouant le tableau à la bonne taille), créant la boucle O(n).

Et puis le join est aussi O(n), mais ça va parce qu'il est en dehors de la boucle.

35
njzk2

J'ai trouvé cet extrait de texte sur Python Speed> Utilisez les meilleurs algorithmes et outils les plus rapides :

La concaténation de chaînes est mieux effectuée avec ''.join(seq) qui est un processus O(n). En revanche, l'utilisation des opérateurs '+' Ou '+=' Peut entraîner un processus O(n^2) car de nouvelles chaînes peuvent être créées pour chaque étape intermédiaire. L'interpréteur CPython 2.4 atténue quelque peu ce problème; cependant, ''.join(seq) reste la meilleure pratique

24
cricket_007

Pour les futurs visiteurs: Puisqu'il s'agit d'une question CTCI, aucune référence à l'apprentissage rllib package n'est requise ici, spécifiquement selon OP et le livre, cette question concerne les tableaux et les cordes.

Voici une solution plus complète, inspirée du pseudo de @ njzk2:

text = 'Mr John Smith'#13 
special_str = '%20'
def URLify(text, text_len, special_str):
    url = [] 
    for i in range(text_len): # O(n)
        if text[i] == ' ': # n-s
            url.append(special_str) # append() is O(1)
        else:
            url.append(text[i]) # O(1)

    print(url)
    return ''.join(url) #O(n)


print(URLify(text, 13, '%20'))
1
geekidharsh