[algorithm] 수천 개의 전화 번호를 저장하는 가장 효율적인 방법

이것은 Google 인터뷰 질문입니다.

각각 10 자리 숫자로 된 약 천개의 전화 번호가 저장됩니다. 각각의 처음 5 자리 숫자가 천 개의 숫자에서 동일하다고 가정 할 수 있습니다. 다음 작업을 수행해야합니다. 주어진 번호가 있는지 검색합니다. 비. 모든 번호를 인쇄

이를 수행하는 가장 효율적인 공간 절약 방법은 무엇입니까?

나는 해시 테이블과 나중에 허프만 코딩에 대답했지만 면접관은 내가 올바른 방향으로 가고 있지 않다고 말했습니다. 여기서 도와주세요.

접미사 트라이를 사용하면 도움이 될 수 있습니까?

이상적으로 1000 개의 숫자를 저장하는 데는 숫자 당 4 바이트가 필요하므로 1000 개의 숫자를 저장하는 데 4000 바이트가 걸립니다. 양적으로 저는 스토리지를 4000 바이트 미만으로 줄이고 싶습니다. 인터뷰 담당자가 설명해주었습니다.



답변

다음은 aix의 답변에 대한 개선 사항 입니다. 데이터 구조에 세 개의 “계층”을 사용하는 것을 고려하십시오. 첫 번째는 처음 5 자리 (17 비트)에 대한 상수입니다. 이제부터 각 전화 번호는 나머지 5 자리 만 남습니다. 나머지 5 자리를 17 비트 이진 정수로보고 한 가지 방법을 사용하여 해당 비트 중 k 를 저장 하고 다른 방법으로 17- k = m 을 저장 하여 필요한 공간을 최소화하기 위해 끝에 k 를 결정 합니다.

먼저 전화 번호를 정렬합니다 (모두 10 진수 5 자리로 축소됨). 그런 다음 우리는 첫 번째로 구성된 이진수하는 얼마나 많은 전화 번호 계산 m의 비트는 첫번째 얼마나 많은 전화 번호를 모두 0 m의 얼마나 많은 전화 번호를 처음 들어 … 0 대부분에 비트가 01 m 비트는 최대 0 … 10 등이며, 첫 번째 m 비트가 1 … 11 인 전화 번호 수까지입니다. 이 마지막 수는 1000 (십진수)입니다. 이러한 개수 는 2 ^ m 개이고 각 개수는 최대 1000 개입니다. 마지막 숫자를 생략하면 (어쨌든 1000 개라는 것을 알기 때문에)이 모든 숫자를 (2 ^ m -1) 의 연속 블록에 저장할 수 있습니다. * 10 비트. (1024 미만의 숫자를 저장하려면 10 비트이면 충분합니다.)

모든 (축소 된) 전화 번호 의 마지막 k 비트는 메모리에 연속적으로 저장됩니다. 그래서 만약 K가 인 마지막 7 비트의 메모리의 블록의 다음 제 7 비트 (비트 0-6을 통해) 제 (감소) 전화 번호의 최종 7 비트에 대응하는 비트 7 내지 13에 대응 통해, 7 말 두 번째 (축소 된) 전화 번호 등. 이를 위해서는 총 17 + (2 ^ (17- k )-1) * 10 + 1000 * k에 대해 1000 * k 비트 가 필요합니다 . 이는 k = 10에 대해 최소 11287에 도달합니다 . 따라서 모든 전화 번호를 ceil (에 저장할 수 있습니다. 11287/8) = 1411 바이트.

추가 공간은 1111111 (이진수)로 시작할 수있는 숫자가 없다는 것을 관찰함으로써 절약 할 수 있습니다. 그로 시작하는 가장 낮은 숫자는 130048이고 십진수 5 자리 만 있기 때문입니다. 이를 통해 첫 번째 메모리 블록에서 몇 개의 항목을 줄일 수 있습니다. 2 ^ m -1 카운트 대신 ceil (99999 / 2 ^ k ) 만 필요합니다 . 즉 공식이

17 + ceil (99999 / 2 ^ k ) * 10 + 1000 * k

놀랍게도 k = 9 및 k = 10 또는 ceil (10997/8) = 1375 바이트에 대해 최소 10997을 얻습니다 .

특정 전화 번호가 세트에 있는지 알고 싶다면 먼저 처음 5 자리 이진수가 저장 한 5 자리 숫자와 일치하는지 확인합니다. 그런 다음 나머지 5 자리를 상위 m = 7 비트 (즉, m 비트 수 M )와 하위 k = 10 비트 (수 K ) 로 나눕니다 . 우리는 지금 번호를 찾을 수 [M-1] 처음있는 감소 된 전화 번호 m의 숫자가 가장에있는 M 1, 및 수 – [M] 처음있는 감소 된 전화 번호 m의 숫자가 가장에있는 M을 , 둘 다 첫 번째 비트 블록에서 가져온 것입니다. 이제 우리 [M-1] 번째 및 A는 [M]가의 번째 시퀀스 유전율 우리가 발견되면 메모리의 제 2 블록 내의 비트보고 K를 ; 최악의 경우 1000 개의 시퀀스가 ​​있으므로 이진 검색을 사용하면 O (log 1000) 연산을 완료 할 수 있습니다.

1000 개의 숫자를 모두 인쇄하기위한 의사 코드는 다음과 같습니다. 여기서 첫 번째 메모리 블록의 K 번째 k 비트 항목을 a [K] 로 액세스하고 두 번째 메모리 블록의 M ‘번째 m 비트 항목을 b [M]으로 액세스합니다. (둘 다 작성하는 데 지루한 몇 가지 작업이 필요합니다). 처음 5 자리 숫자는 c 입니다.

i := 0;
for K from 0 to ceil(99999 / 2^k) do
  while i < a[K] do
    print(c * 10^5 + K * 2^k + b[i]);
    i := i + 1;
  end do;
end do;

K = ceil (99999 / 2 ^ k ) 의 경계 케이스에 문제가있을 수 있지만 수정하기에는 쉽습니다.

마지막으로, 엔트로피 관점에서 10 ^ 5보다 작은 10 ^ 3 양의 정수의 하위 집합을 ceil (log [2] (binomial (10 ^ 5, 10 ^ 3)) 미만으로 저장할 수 없습니다. ) = 8073. 처음 5 자리에 필요한 17 개를 포함하여 여전히 10997-8090 = 2907 비트의 간격이 있습니다. 상대적으로 효율적으로 숫자에 액세스 할 수있는 더 나은 솔루션이 있는지 확인하는 것은 흥미로운 도전입니다!


답변

다음에서는 숫자를 문자열이 아닌 정수 변수로 취급합니다.

  1. 숫자를 정렬하십시오.
  2. 각 숫자를 처음 5 자리와 마지막 5 자리로 나눕니다.
  3. 처음 다섯 자리는 숫자간에 동일하므로 한 번만 저장하십시오. 이를 위해서는 17 비트의 저장 공간이 필요합니다.
  4. 각 숫자의 마지막 5 자리를 개별적으로 저장합니다. 숫자 당 17 비트가 필요합니다.

요약하자면, 처음 17 비트는 공통 접두사이고, 이후 1000 개의 17 비트 그룹은 오름차순으로 저장된 각 숫자의 마지막 5 자리입니다.

총 1000 개의 숫자에 대해 2128 바이트 또는 10 자리 전화 번호 당 17.017 비트를 찾습니다.

검색은 O(log n)(이진 검색)이고 전체 열거는 O(n)입니다.


답변

http://en.wikipedia.org/wiki/Acyclic_deterministic_finite_automaton

나는 그들이 데이터 구조에 대해 물었던 인터뷰를 한 적이있다. “배열”을 잊었습니다.


답변

나는 아마 일부 압축 된 버전 사용을 고려할 것 트리는 (아마도 임마 @Misha에 의해 제안을).

그것은 그들이 모두 공통 접두사를 가지고 있다는 사실을 자동으로 이용할 것입니다.

검색은 일정한 시간으로 수행되고 인쇄는 선형 시간으로 수행됩니다.


답변

이전에이 문제에 대해 들어 본 적이 있습니다 (하지만 처음 5 자리 숫자는 같은 가정이 아님).이를 수행하는 가장 간단한 방법은 Rice Coding 이었습니다 .

1) 순서는 중요하지 않으므로 정렬하고 연속 된 값 간의 차이 만 저장할 수 있습니다. 우리의 경우 평균 차이는 100.000 / 1000 = 100입니다.

2) Rice 코드 (base 128 또는 64) 또는 Golomb 코드 (base 100)를 사용하여 차이점을 인코딩합니다.

편집 : 기본 128을 사용하는 Rice 코딩에 대한 추정 (최상의 결과를 제공하기 때문이 아니라 계산하기 쉽기 때문에) :

첫 번째 값을있는 그대로 (32 비트) 저장합니다.
나머지 999 개 값은 차이입니다 (작게 예상되며 평균적으로 100 개).

단항 값 value / 128(가변 비트 수 + 종결 자로 1 비트) (7 비트)에
대한 이진 값value % 128

우리는 어떻게 든 VBL가변 비트 수에 대한 한계를 추정해야합니다 (
하한 한계 : 우리가 운이 좋다고 생각하고이 경우에는 128). 이것은 0 개의 추가 비트를 제공한다는 것을 의미합니다.
상한 : 기본보다 작은 모든 차이는 숫자의 이진 부분으로 인코딩되므로 단항으로 인코딩해야하는 최대 수는 100000/128 = 781.25입니다 (대부분의 차이가 0이 될 것으로 예상하지 않기 때문에 더 적습니다. ).

따라서 결과는 32 + 999 * (1 + 7) + 변수 (0..782) 비트 = 1003 + 변수 (0..98) 바이트입니다.


답변

이것은 Bentley의 Programming Pearls에서 잘 알려진 문제입니다.

해결 방법 : 모든 숫자에 대해 동일하므로 숫자에서 처음 다섯 자리를 제거하십시오. 그런 다음 비트 연산을 사용하여 나머지 9999 가능한 값을 나타냅니다. 숫자를 표현하려면 2 ^ 17 비트 만 필요합니다. 각 비트는 숫자를 나타냅니다. 비트가 설정된 경우 번호는 전화 번호부에 있습니다.

모든 숫자를 인쇄하려면 접두사와 연결된 비트가 설정된 모든 숫자를 인쇄하면됩니다. 주어진 숫자를 검색하려면 숫자의 비트 표현을 확인하는 데 필요한 비트 산술을 수행하십시오.

O (1)에서 숫자를 검색 할 수 있으며 비트 표현으로 인해 공간 효율성이 극대화됩니다.

HTH 크리스.


답변

1,000 개의 숫자에 대해 1073 바이트의 고정 스토리지 :

이 저장 방법의 기본 형식은 처음 5 자리, 각 그룹의 개수 및 각 그룹의 각 숫자에 대한 오프셋을 저장하는 것입니다.

접두사 :
5 자리 접두사는 처음 17 비트를 차지합니다 .

그룹화 :
다음으로 숫자에 적합한 크기의 그룹을 찾아야합니다. 그룹당 1 개 정도의 숫자를 만들어 봅시다. 저장할 숫자가 약 1000 개라는 것을 알기 때문에 99,999 개를 약 1000 개의 부분으로 나눕니다. 그룹 크기를 100으로 선택하면 낭비되는 비트가 있으므로 7 비트로 표현할 수있는 그룹 크기 128을 시도해 보겠습니다. 이를 통해 782 개의 그룹과 함께 작업 할 수 있습니다.


개수 : 다음으로 782 개 그룹 각각에 대해 각 그룹의 항목 개수를 저장해야합니다. 각 그룹에 대한 7 비트 카운트는을 산출합니다 7*782=5,474 bits. 이는 우리가 그룹을 선택한 방식으로 인해 표시된 평균 수가 약 1이기 때문에 매우 비효율적입니다.

따라서 대신 그룹의 각 숫자에 대해 선행 1과 0이 뒤 따르는 가변 크기의 개수가 있습니다. 따라서 x그룹에 숫자가있는 경우 개수를 나타내는 x 1'sa 가 뒤 따랐을 것 0입니다. 예를 들어, 한 그룹에 5 개의 숫자가있는 경우 개수는로 표시됩니다 111110. 이 방법을 사용하면 1000 개의 숫자가 있는 경우 카운트 에 대해 총 1000 + 782 = 1,782 비트에 대해 1000 개의 1과 782 0으로 끝납니다 .

오프셋 :
마지막으로 각 숫자의 형식은 각 그룹에 대한 7 비트 오프셋입니다. 예를 들어 00000과 00001이 0-127 그룹의 유일한 숫자 인 경우 해당 그룹의 비트는입니다 110 0000000 0000001. 1,000 개의 숫자를 가정하면 오프셋에 7,000 비트 가 있습니다 .

따라서 1,000 개의 숫자를 가정 한 최종 개수는 다음과 같습니다.

17 (prefix) + 1,782 (counts) + 7,000 (offsets) = 8,799 bits = 1100 bytes

이제 128 비트로 반올림하여 그룹 크기 선택이 그룹 크기에 가장 적합한 선택인지 확인하겠습니다. x각 그룹을 나타내는 비트 수를 선택하면 크기에 대한 공식은 다음과 같습니다.

Size in bits = 17 (prefix) + 1,000 + 99,999/2^x + x * 1,000

정수 값에 대한이 식을 최소화하는 것은 x제공 x=68,580 비트 = 수득되는, 1,073 바이트 . 따라서 이상적인 스토리지는 다음과 같습니다.

  • 그룹 규모 : 2 ^ 6 = 64
  • 그룹 수 : 1,562
  • 총 저장 용량 :

    1017 (prefix plus 1's) + 1563 (0's in count) + 6*1000 (offsets) = 8,580 bits = 1,073 bytes