O (n)보다 빠르게 배열 요소의 인덱스 가져 오기


104

나는 거대한 배열과 그것의 값을 가지고 있습니다. 배열 값의 인덱스를 얻고 싶습니다. Array#index그것을 얻기 위해 전화 하는 것보다 다른 방법 이 있습니까? 문제는 정말 거대한 배열을 유지하고 Array#index엄청난 시간을 호출 할 필요가 있기 때문입니다.

몇 번의 시도 끝에 값 자체 대신 필드가있는 구조체를 저장하여 요소 내부에 인덱스 를 캐싱(value, index) 하면 성능이 크게 향상 된다는 사실을 발견했습니다 (20 배 승리).

그래도 캐싱없이 en 요소의 인덱스를 찾는 더 편리한 방법이 있는지 궁금합니다 (또는 성능을 향상시키는 좋은 캐싱 기술이 있습니다).

답변:


118

배열을 해시로 변환합니다. 그런 다음 열쇠를 찾으십시오.

array = ['a', 'b', 'c']
hash = Hash[array.map.with_index.to_a]    # => {"a"=>0, "b"=>1, "c"=>2}
hash['b'] # => 1

2
어레이가 매우 긴 경우 가장 빠름
Kevin

17
사용 사례에 따라 중복 값이있는 경우 문제가 될 수 있습니다. 위에 설명 된 메서드는 동등한 값 또는 #rindex (값의 마지막 발생)를 반환합니다. #index 동등한 결과를 얻으려면 값의 첫 번째 인덱스를 반환하는 해시가 생성하기 전에 배열을 반전하는 줄을 따라 수행해야합니다. 해시는 초기 배열의 총 길이에서 반환 된 인덱스 값을 뺀 것입니다.-1. # (array.length-1)
-hash

2
해시로 변환하는 데 O (n) 시간이 걸리지 않습니까? 한 번 이상 사용하면 해시 변환이 더 성능이 좋을 것이라고 생각합니다. 그러나 단일 용도의 경우 어레이를 반복하는 것과 다르지 않습니까?
ahnbizcad

예, 해시 계산이 비교만큼 빨리 단락되지 않기 때문에 실제로 중요한 경우 일회용으로는 더 나쁠 수 있습니다.
Peter DeWeese 2018

199

index 또는 rindex를 사용하지 않는 이유는 무엇입니까?

array = %w( a b c d e)
# get FIRST index of element searched
puts array.index('a')
# get LAST index of element searched
puts array.rindex('a')

색인 : http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-index

rindex : http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-rindex


13
이것은 OP가 어레이 크기가 크기 때문에 원하지 않는다고 말한 것입니다. Array # index는 O (n)이며 여러 번 수행하면 성능이 저하됩니다. 해시 조회는 O (1)입니다.
Tim

4
@tim, 내 대답 당시에 이것이 동일한 질문 이라는 것을 기억할 수 없습니다 . 아마도 OP가 나중에 질문을 수정 하여이 대답을 무효화 할 수 있습니다.
Roger

3
그때 특정 시간에 편집되었다고하지 않겠습니까?
Tim

헤헤, 맞아요. 저와 다른 30 명이 그 책을 읽었습니다. 나는 추측한다 : /
Roger

9

다른 답변은 배열에 여러 번 나열된 항목의 가능성을 고려하지 않습니다. 그러면 각 키가 배열의 고유 한 개체이고 각 값이 개체가있는 위치에 해당하는 인덱스 배열 인 해시가 반환됩니다.

a = [1, 2, 3, 1, 2, 3, 4]
=> [1, 2, 3, 1, 2, 3, 4]

indices = a.each_with_index.inject(Hash.new { Array.new }) do |hash, (obj, i)| 
    hash[obj] += [i]
    hash
end
=> { 1 => [0, 3], 2 => [1, 4], 3 => [2, 5], 4 => [6] }

이렇게하면 중복 항목을 빠르게 검색 할 수 있습니다.

indices.select { |k, v| v.size > 1 }
=> { 1 => [0, 3], 2 => [1, 4], 3 => [2, 5] }

6

해시를 사용하지 않는 합당한 이유가 있습니까? 조회는 어레이 O(1)와 비교 O(n)됩니다.


요점은-내가 #keys사용하는 배열을 반환하는 해시를 호출 하고 있습니다. 그래도 내 아키텍처에 대해서도 생각할 수 있습니다 ...
gmile 2011-06-05

3

그것은 만약 정렬 된 배열은 이진 검색 알고리즘을 사용할 수있다 ( O(log n)). 예를 들어 다음 기능으로 Array 클래스를 확장합니다.

class Array
  def b_search(e, l = 0, u = length - 1)
    return if lower_index > upper_index

    midpoint_index = (lower_index + upper_index) / 2
    return midpoint_index if self[midpoint_index] == value

    if value < self[midpoint_index]
      b_search(value, lower_index, upper_index - 1)
    else
      b_search(value, lower_index + 1, upper_index)
    end
  end
end

3
실제로 읽기 어렵지 않습니다. 첫 번째 부분은 하한이 상한보다 크면 반환합니다 (재귀가 제출 됨). 두 번째 부분은 중간 점 m을 해당 지점의 값과 e로 비교하여 왼쪽 또는 오른쪽이 필요한지 확인합니다. 우리가 원하는 답이 없으면 재귀합니다.
ioquatix 2014-07-20

나는 그것이 편집보다는 반대 투표를하는 사람들의 자존심에 더 좋다고 생각합니다.
Andre Figueiredo

2

@sawa의 답변과 거기에 나열된 주석을 조합하면 배열 클래스에 "빠른"인덱스와 rindex를 구현할 수 있습니다.

class Array
  def quick_index el
    hash = Hash[self.map.with_index.to_a]
    hash[el]
  end

  def quick_rindex el
    hash = Hash[self.reverse.map.with_index.to_a]
    array.length - 1 - hash[el]
  end
end

2

배열에 자연 순서가 있으면 이진 검색을 사용하십시오.

이진 검색을 사용하십시오.

이진 검색에는 O(log n)액세스 시간 이 있습니다.

이진 검색을 사용하는 방법에 대한 단계는 다음과 같습니다.

  • 배열 순서는 무엇입니까? 예를 들어, 이름별로 정렬되어 있습니까?
  • bsearch요소 또는 인덱스를 찾는 데 사용

코드 예

# assume array is sorted by name!

array.bsearch { |each| "Jamie" <=> each.name } # returns element
(0..array.size).bsearch { |n| "Jamie" <=> array[n].name } # returns index

0

그래도 캐싱없이 en 요소의 인덱스를 찾는 더 편리한 방법이 있는지 궁금합니다 (또는 성능을 향상시키는 좋은 캐싱 기술이 있습니다).

(배열 주문한 경우 당신은 이진 검색을 사용할 수 있습니다 그리고 당신은 배열에 저장 한 값은 어떤 방법으로 비교할 수 있습니다). 작동하려면 이진 검색이 현재 요소의 "왼쪽"또는 "오른쪽"을보고 있는지 여부를 알 수 있어야합니다. 그러나 index삽입 시간에 저장 한 다음 동일한 배열에서 요소를 가져 오는 경우 사용 하는 데 아무런 문제가 없다고 생각 합니다.

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