반복 문자열의 시간 복잡성이 실제로 O (n ^ 2) 또는 O (n)을 추가합니까?


89

CTCI에서 문제를 해결하고 있습니다.

1 장의 세 번째 문제는 다음과 같은 문자열을 사용하는 것입니다.

'Mr John Smith '

중간 공간을 %20다음 으로 대체하도록 요청합니다 .

'Mr%20John%20Smith'

저자는이 솔루션을 Python으로 제공하며 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

내 질문:

나는 이것이 왼쪽에서 오른쪽으로 실제 문자열을 스캔하는 측면에서 O (n)임을 이해합니다. 하지만 파이썬의 문자열은 불변하지 않습니까? 문자열이 있고 +연산자를 사용하여 다른 문자열을 추가 하면 필요한 공간을 할당하고 원본을 복사 한 다음 추가 문자열을 복사하지 않습니까?

n길이가 각각 1 인 문자열 모음이 있으면 다음을 수행합니다.

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

또는 O (n ^ 2) 시간 , 그래? 아니면 파이썬이 추가를 처리하는 방법이 잘못 되었습니까?

또는 낚시하는 법을 가르쳐 주실 수 있다면 어떻게해야합니까? 나는 구글을 공식 소스로 시도하는데 실패했다. https://wiki.python.org/moin/TimeComplexity를 찾았 지만 문자열에 아무것도 없습니다.


17
누군가에 대해 저자에게 말해야합니다urllib.urlencode
— wim

11
배열과 문자열에 대한 연습 문제가 의미하는 것 @wim
— user5622964

3
이 책의 목적은 인터뷰 질문을 가르치는 것인데, 일반적으로 인터뷰 대상자의 사고 과정을보기 위해 바퀴를 재발 명하도록 요구합니다.
— James

1
이 파이썬 때문에, 나는 일을 생각 rtrim하고 replace더 선호의 야구장에있을 것입니다 O(n). 문자열 복사는 가장 효율적인 방법으로 보이지 않습니다.
— OneCricketeer 2015

2
@RNar 복사본이 어떻게 일정한 시간이 걸리는지 설명해 주시겠습니까?
— James Wierzba 2015

답변:


83

Python의 표준 구현 인 CPython에는이를 일반적으로 O (n)으로 만드는 구현 세부 사항이 있으며 , 바이트 코드 평가 루프가 호출 +하거나 +=두 개의 문자열 피연산자 를 사용 하여 코드에서 구현됩니다 . 파이썬이 왼쪽 인수에 다른 참조가 없음을 감지 realloc하면 문자열의 크기를 조정하여 복사를 방지하도록 호출 합니다. 이것은 구현 세부 사항이므로 realloc문자열을 자주 이동해야하는 경우 성능이 어쨌든 O (n ^ 2)로 저하 되기 때문에 의존해서는 안됩니다 .

이상한 구현 세부 사항이 없으면 알고리즘은 2 차 복사 양으로 인해 O (n ^ 2)입니다. 이와 같은 코드는 C ++와 같이 변경 가능한 문자열이있는 언어에서만 의미가 있으며 C ++에서도 사용하려는 +=.


2
링크 한 코드를보고 있습니다 ... 그 코드의 큰 부분이 추가되는 문자열에 대한 포인터 / 참조를 정리 / 제거하는 것 같습니다. 맞습니까? 그리고 마지막으로 _PyString_Resize(&v, new_len)연결된 문자열에 메모리를 할당 memcpy(PyString_AS_STRING(v) + v_len, PyString_AS_STRING(w), w_len);하고 복사를 수행합니다. 내부 크기 조정이 실패하면 실패합니다 PyString_Concat(&v, w);(원래 문자열 주소의 끝에있는 연속 메모리가 비어 있지 않을 때를 의미한다고 가정합니다). 이것이 속도 향상을 어떻게 보여줍니까?
— user5622964 2015

이전 의견에서 공간이 부족했지만 해당 코드를 올바르게 이해하고 있는지 여부와 해당 조각의 메모리 사용량 / 런타임을 해석하는 방법에 대한 질문이 있습니다.
— user5622964 2015

1
@ user5622964 : 이런, 이상한 구현 세부 사항을 잘못 기억했습니다. 효율적인 크기 조정 정책이 없습니다. 그것은 단지 realloc최고를 요구 하고 희망합니다.
— user2357112 모니카 지원

어떻게 memcpy(PyString_AS_STRING(v) + v_len, PyString_AS_STRING(w), w_len);작동합니까? cplusplus.com/reference/cstring/memcpy 에 따르면 정의 void * memcpy ( void * destination, const void * source, size_t num );와 설명이 있습니다. "Copies the values of num bytes from the location pointed to by source directly to the memory block pointed to by destination."이 경우 num은 추가 문자열의 크기이고 source는 두 번째 문자열의 주소라고 가정합니다. 하지만 목적지 (첫 번째 문자열) + len (첫 번째 문자열)은 왜입니까? 더블 메모리?
— user5622964 2015

7
@ user5622964 : 그것은 포인터 산술입니다. CPython 소스 코드를 이상한 구현 세부 사항까지 이해하려면 C를 알아야합니다. 초 압축 버전은 PyString_AS_STRING(v)첫 번째 문자열 데이터 v_len의 주소 이며 추가 하면 문자열 바로 뒤에 주소가 표시됩니다. 데이터가 끝납니다.
— user2357112 모니카 지원

40

작성자는 여기에있는 최적화에 의존하지만 명시 적으로 신뢰할 수는 없습니다. strA = strB + strC일반적으로 O(n)기능을 만드는 것 O(n^2)입니다. 그러나 전체 프로세스가 O(n)이고 배열을 사용 하는지 확인하는 것은 매우 쉽습니다 .

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

요컨대, append작업은 분할 O(1) 되어 ( O(1)배열을 올바른 크기로 미리 할당하여 강력하게 만들 수 있지만 ) 루프를 O(n)만듭니다.

그리고 join는 또한 O(n)이지만 루프 외부에 있기 때문에 괜찮습니다.


이 대답은 문자열을 연결하는 방법을 알려주기 때문에 좋습니다.
— user877329

런타임 계산의 맥락에서 정확한 대답.
— Intesar Haider

25

Python Speed> 최고의 알고리즘과 가장 빠른 도구 사용 에서이 텍스트 스 니펫을 찾았습니다 .

문자열 연결은 가장으로 수행 ''.join(seq)하는이다 O(n)과정. 반대로 '+'또는 '+='연산자를 사용하면 O(n^2)각 중간 단계에 대해 새 문자열이 작성 될 수 있으므로 프로세스 가 발생할 수 있습니다. CPython 2.4 인터프리터는이 문제를 다소 완화합니다. 그러나 ''.join(seq)모범 사례로 남아 있습니다.


3

미래 방문자를 위해 : CTCI 질문이므로 여기에서는 특히 OP 및 책에 따라 urllib 패키지 학습에 대한 참조가 필요하지 않습니다.이 질문은 배열 및 문자열에 관한 것입니다.

@ 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'))
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.