Je travaille sur un problème avec CTCI.
Le troisième problème du chapitre 1 consiste à 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 que c'est O (n) en termes de balayage de la chaîne réelle de gauche à droite. Mais les chaînes en Python ne sont-elles pas immuables? Si j'ai une chaîne et que j'y ajoute une autre chaîne avec l' +
opérateur, n'alloue-t-elle pas l'espace nécessaire, ne copie-t-elle pas l'original, puis ne copie-t-elle pas la chaîne d'ajout?
Si j'ai une collection de n
chaînes chacune de longueur 1, alors cela prend:
1 + 2 + 3 + 4 + 5 + ... + n = n(n+1)/2
ou O (n ^ 2) fois , oui? Ou est-ce que 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 pour rechercher une source officielle sur Google. J'ai trouvé https://wiki.python.org/moin/TimeComplexity mais cela n'a rien sur les chaînes.
la source
urllib.urlencode
rtrim
etreplace
serait plus préféré et dans le ballpark deO(n)
. La copie de chaînes semble la manière la moins efficace.Réponses:
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 de 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 appellerealloc
pour tenter d'éviter une copie en redimensionnant la chaîne sur place. Ce n'est pas quelque chose sur lequel vous devriez jamais vous fier, car c'est un détail d'implémentation et parce que si vous devezrealloc
déplacer la chaîne fréquemment, les performances se dégradent de toute façon à O (n ^ 2).Sans les détails d'implémentation étranges, 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 ++ que vous voudriez utiliser
+=
.la source
_PyString_Resize(&v, new_len)
pour allouer la mémoire pour la chaîne concaténée, puismemcpy(PyString_AS_STRING(v) + v_len, PyString_AS_STRING(w), w_len);
qui fait la copie. Si le redimensionnement sur place échoue, c'est le casPyString_Concat(&v, w);
(je suppose que cela signifie que la mémoire contiguë à la fin de l'adresse de chaîne d'origine n'est pas libre). Comment cela montre-t-il l'accélération?realloc
et espère le meilleur.memcpy(PyString_AS_STRING(v) + v_len, PyString_AS_STRING(w), w_len);
marche? Selon cplusplus.com/reference/cstring/memcpy, il a une définitionvoid * memcpy ( void * destination, const void * source, size_t num );
et une description:"Copies the values of num bytes from the location pointed to by source directly to the memory block pointed to by destination."
le nombre dans ce cas est la taille de la chaîne d'ajout, et la source est l'adresse de la deuxième chaîne, je suppose? Mais alors pourquoi la destination (première chaîne) + len (première chaîne)? Double mémoire?PyString_AS_STRING(v)
est l'adresse des données de la première chaîne, et l'ajoutv_len
vous donne l'adresse juste après la chaîne. les données se terminent.L'auteur s'appuie sur une optimisation qui se trouve ici, mais qui n'est pas explicitement fiable.
strA = strB + strC
est généralementO(n)
, faire la fonctionO(n^2)
. Cependant, il est assez facile de s'assurer que tout le processus estO(n)
, utilisez un tableau:output = [] # ... loop thing output.append('%20') # ... output.append(char) # ... return ''.join(output)
En un mot, l'
append
opération est amortieO(1)
(bien que vous puissiez la rendre forteO(1)
en pré-allouant le tableau à la bonne taille), ce qui fait la boucleO(n)
.Et puis
join
c'est aussiO(n)
, mais ce n'est pas grave car c'est en dehors de la boucle.la source
J'ai trouvé cet extrait de texte sur Python Speed> Utilisez les meilleurs algorithmes et les outils les plus rapides :
la source
Pour les futurs visiteurs: Puisqu'il s'agit d'une question CTCI, aucune référence à l'apprentissage du package urllib n'est requise ici, en particulier selon OP et le livre, cette question concerne les tableaux et les chaînes.
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'))
la source