2010-06-09 1 views
4

저는 int64s의 배열을 많이 사용하는 파이썬의 알고리즘을 연구하고 있습니다. 배열은 일반적으로 스파 스 (sparse)하고 지속적으로 읽고 쓰여집니다. 현재 상대적으로 큰 기본 배열을 사용하고 있으며 성능은 좋지만 메모리 사용량은 예상대로 높습니다.누구나 Python에서 훌륭한 스파 스 1 차원 배열 라이브러리를 아십니까?

배열 구현을 사용하고 싶지 않은 값을위한 공간을 낭비하지 않고 0 이외의 인덱스 오프셋을 허용하고 싶습니다. 예를 들어, 내 숫자가 1,000,000에서 시작하면 1,000,000부터 배열을 인덱싱 할 수 있기를 원하며 미사용 값이 백만 개가 아닌 메모리를 낭비하지 않아도됩니다.

어레이 읽기 및 쓰기가 빠를 필요가 있습니다. 새로운 영토로 확장하는 것은 약간 지연 될 수 있지만 가능하다면 읽기와 쓰기는 O (1)이어야합니다.

누구나 할 수있는 라이브러리에 대해 알고 있습니까?

감사합니다.

데이터 유형으로 언급 된 int64로 업데이트되었습니다.

+0

:

는의는 2 레벨 페이지 테이블의 간단한 파이썬 구현을 보자. 멋진 제안에 감사드립니다. 나는 blist 제안을 대답으로 표시했다. 왜냐하면 그것은 blist 제안을 지금까지 가장 가까웠 기 때문이다. 모두에게 감사드립니다! – TheJacobTaylor

+0

많은 사람들이 사전을 제안했습니다. 파이썬을 모르는 사람은 파이썬 사전에 해당하는 것을 제안했습니다. 사전에는 O (1) 무작위 액세스 및 O (n) 메모리 사용이 있습니다. 여기서 n은 데이터 공간 크기가 아닌 실제 데이터 크기입니다. 이러한 특성은 당신이 요구 한 것과 정확히 같습니다. 당신은 그것을 시도했다고 말했지만 "CPU를 엄청나게"사용했습니다. 어레이보다 다른 속도로는 원하는 속도를 얻을 수 없으며 높은 메모리 사용량을 받아 들여야합니다. –

답변

4

blist 유형 (documentation, download)과 같이 들릴 수도 있습니다 (면책 조항 : 저자입니다). Python의 list과 똑같은 인터페이스를 가지고 있으므로 학습 곡선이 없지만 성능 특성이 다릅니다. 특히, 많은 경우에 스파 스리스트를 효율적으로 처리 할 수 ​​있습니다. 다음은 2 ** 29 요소가있는 목록을 만드는 예제입니다. 그것은 거의 즉시입니다. 이런 방식으로 생성 된 스파 스 목록은 O (log n) 공간을 사용합니다. 전체 목록을 반복

>>> from blist import * 
>>> x = blist([0])    # x is a blist with one element 
>>> x *= 2**29     # x is a blist with > 500 million elements 
>>> x.append(5)    # append to x 
>>> y = x[4:-234234]   # Take a 500 million element slice from x 
>>> del x[3:1024]    # Delete a few thousand elements from x 

작업은 여전히 ​​O (n)의 시간 (remove, reverse, count 등)을 가지고. 이 문서는 각 작업의 시간 복잡성에 대해 설명하므로 요구 사항을 충족시킬 수 있는지 평가할 수 있어야합니다.

당신이 당신의 데이터 공간에 색인 공간에서 함수를 정의하여 스파 스 배열을 시뮬레이션 할 수있는 몇 가지 언어에 : 나는 파이썬을 모르는

+0

이것은 꽤 놀라운 패키지처럼 보입니다. 내가 볼 수없는 한 가지는 각 값에 대한 색인의 연결입니다. 그것이 현재 데이터를 저장하는 방법이었습니다. 내 알고리즘을 작은 뒤쪽으로 뒤집어 넣고 구h 주걱을 넣는 것이 실제로 가능할 수도 있습니다. 도전. 감사. – TheJacobTaylor

+0

@TheJacobTaylor : "각 값에 대한 색인 연결"이란 무엇을 의미하는지 자세히 설명해 주시겠습니까? –

+0

@Daniel 거친 것. 현재, 실제 0 기반 배열을 사용하고 있습니다. 인덱스는 배열의 데이터뿐만 아니라 의미가 있습니다. A [x] <=> A [y] 값을 바꾼 경우 (예 : x! = y) 내 코드가 손상됩니다. x 이전에 몇 개의 항목을 삭제하면 x의 색인이 감소합니다. 맞습니까? 델 A [0..2]라고 말하면 A [x]에 있었던 값을 찾으려면 A [x-3]을 올바르게 호출해야합니다. 그것은 각 값에 대한 색인의 연결을 말할 때 내가 언급하고있는 것입니다. – TheJacobTaylor

1

희박한 배열로 numpy sparse matrix을 다시 매핑하거나 해시 테이블 (python dict)을 사용할 수 있습니다. 오프셋에 관해서는, 사용하고있는 저장 클래스를 랩핑하고, insert/lookup/remove 메소드를 소유하게하십시오.

+0

사전을 사용했지만 입력 조작으로 CPU를 많이 먹었습니다. 지금 나는 많은 메모리와 0 CPU를 사용하고있다. 잠재적으로 더 흥미로운 타협을 찾고 있습니다. 나는 희소 행렬 사용법을 고려했다, 그러나 일반적으로 그것을 만들기 위하여 불필요한 오버 헤드를 많이 가질 것 같은 느낌. 감사! – TheJacobTaylor

+0

@TheJacobTaylor 귀하의 문제가 너무 구체적이라고 생각합니다. 거기에는 맞춤형 솔루션이 있다는 것을의 미합니다. 더 많은 코드가 더 많은 mem/cpu 사용을 의미하지 않는다는 것을 기억하십시오. – zdav

+0

솔직히, 나는 내가 원하는 것을 얻는 것이 가능하다는 것을 실제로 확신하지 못한다. 부동 초기 색인이 가능해야합니다. 나머지는 그렇지 않을 수도 있습니다. 현재 나는 하나의 추가로 값을 찾고 있습니다. 이를 극복 할 수있는 드문 드문 한 데이터 구조를 만드는 것은 어렵습니다. 나는 읽기/쓰기를 위해 더 많은 것을 지불 하겠지만 상당한 가치를 제공해야합니다. – TheJacobTaylor

1

은 그래서 이것은 아마 않은 대답이다. 예를 들어 (의사 코드) : 일부 언어 (가장 좋은 것들) 배열 참조의 형태로

f[1000000] = 32; 
f[2000000] = 51; 

(예 f[3])는 단지 함수 호출의 형태 (예를 들어 f[3])처럼 보인다. 물론 이것은 배열이 인덱스 공간에서 데이터 공간으로 함수를 정의하기 때문입니다. 이런 식으로 고차원의 희소 배열을 구현하는 것은 매우 쉽습니다.

+0

와우 ... 나는이 점에 더 많은 의미가 있다고 생각해야한다. 나는 인덱스 함수를 깨끗하게 다시 매핑하는 것을 고려하지 않았습니다. 오랫동안 효율적으로 오프셋을 계산할 수 있어야합니다. 현재는 속도 향상을 위해 코드의이 섹션에서 모든 함수 호출을 제거했습니다. 이렇게하려면 함수 호출이나 두 개의 함수를 추가해야하지만 복잡한 작업을 추가하지 않고도 많은 메모리 요구 사항을 줄일 수 있습니다. – TheJacobTaylor

1

왜 그냥 딕테이션을 사용하지 않는가?

sparse = dict() 
sparse[100000] = 1234 
sparse[123456] = 2345 
+0

내가 가진 가장 큰 문제는 속도입니다. 내가하고있는 일에 대해 사전은 CPU를 비례하여 사용합니다.또한 사전을 정렬해야하는 데이터 세트를 가끔씩 살펴볼 필요가 있습니다. – TheJacobTaylor

+0

"CPU의 톤"에 대해 구체적으로 설명해 드리겠습니다. 추가 및 메모리 할당 비용으로 읽기/쓰기가 가능한 정적 int64 어레이를 사용하고 있습니다. 사전은 더 많은 작업을 수행합니다. 이 코드의 많은 부분에서 함수 호출을 제거하여 더 빠르게 만들었습니다. 아래의 색인 매핑 제안은 사전보다 더 효과적 일 수 있다고 생각합니다. – TheJacobTaylor

+0

@TheJacobTaylor : "인덱스 매핑"으로 하이 퍼포먼스 마크의 제안을 의미한다면, 필자는 어떻게하면 더 잘할 수 있는지 (파이썬에서) 딕테이션보다 좋지 않다. 너 해봤 니? 파이썬 함수 호출은 비싸고 (파이썬 표준에 의해서조차도) 파이썬 표준에 의한 사전 검색은 매우 빠릅니다. 즉, dict *은 Python의 "인덱스 매핑"의 기본 구현입니다. –

1

또 다른 옵션은 - 적어도 하나를 직접 구현하고자하는 경우 - Page table이다. 이것은 가상 메모리 시스템에서 가상 주소를 실제 주소에 매핑하는 데 일반적으로 사용되며 주소 공간이 드문 드문 있고 사용 된 주소가 클러스터 된 경우 가장 잘 작동합니다. 사용 된 주소가 무작위로 배포되는 경우 효과가 떨어집니다.

페이지 테이블의 기본 접근 방식은 Trie - 재귀 적 하위 구분과 동일합니다. 페이지 테이블은 고정 된 수의 레벨을 가지며 각 노드는 고정 된 크기의 배열입니다. 지정된 서브 노드의 엔트리가 null의 경우, 그 노드가 가리키는 모든 잎은 null가됩니다. 페이지 테이블의 가장 큰 장점은 조회가 빠르며 약간의 비트 시프트 및 참조 해제 만 필요하다는 것입니다. 난 아직이 질문에 대한 "완벽한"해답을 발견하지 않았습니다

class Pagetable(object): 
    def __init__(self, num_bits=8): 
    """Creates a new Pagetable with num_bits bits per level. 

    Args: 
     num_bits: The number of bits per pagetable level. 
     A 2 level pagetable will be able to store indexes between 0 and 2^(num_bits*2). 
    """ 
    self.num_bits = num_bits 
    self.mask = (1 << num_bits) - 1 
    self.root = [None] * (2 ** num_bits) 

    def __getitem__(self, idx): 
    page = self.root[idx >> self.num_bits] 
    return page and page[idx & self.mask] 

    def __setitem__(self, idx, val): 
    page = self.root[idx >> self.num_bits] 
    if not page: 
     page = self.root[idx >> self.num_bits] = [None] * (2 ** self.num_bits) 
    page[idx & self.mask] = val