2010-01-02 2 views
2

하나의 문제를 해결하는 알고리즘을 만드는 데 도움이 필요합니다. 행에 여러 번 나타나는 숫자가있는 행이 있습니다. 표시되는 숫자는 대부분이 행에 얼마나 많은 시간, 예하면 :행에서 가장 많이 나타나는 숫자를 찾는 알고리즘 - C++

1-1-5-1-3-7-2-1-8-9-1-2

될 것이라고 1과 5 번 나타납니다.

알고리즘이 빨라야합니다 (내 문제입니다). 아이디어가 있으십니까?

+0

하지만 많은 시간을 복용. – ggg

+5

문제에 대한 제약 조건을 설명해주십시오 ... 알려진 요소의 범위는 무엇입니까? 숫자의 설정 기간은 얼마나됩니까? –

답변

10

당신은 해시 테이블을 유지하고 mode라고 찾고있는이

h[1] = 5 
h[5] = 1 
... 
+0

C++의 "Hash table"은'std :: map' 클래스로 구현됩니다. –

+4

실제로 "해시 테이블"은'std :: map '과 다릅니다. ** 해시 테이블 **에서 키는 * 해시 *되거나 고유 값으로 변환 된 다음 컨테이너에 대한 인덱스로 사용됩니다. 'std :: map'은 [key, value] 쌍의 컨테이너입니다. 일부 구현은 해시 테이블을 제공하여 STL을 보강 할 수 있습니다. –

+2

현재'std :: tr1 :: unordered_map'이 있습니다. – jason

10

처럼, 그 구조에있는 모든 요소의 수를 저장할 수 있습니다. 배열을 정렬 한 다음 가장 긴 반복 시퀀스를 찾을 수 있습니다.

+1

이것은'O (n log n)'시간과'O (1)'공간입니다. 공간을 포기함으로써'O (n)'에서 이것을 할 수 있습니다. 분명히 이것은 최선의 방법입니다. – jason

+0

@ Jason : 주어진 숫자 세트 예제에서 O (n) 솔루션은 공간면에서 문제가 없을 것입니다. 우리는 세트에있을 수있는 값의 범위를 실제로 알지 못하기 때문에 상당히 클 수 있습니다. –

+1

아니, 그냥 사전을 사용하여 다음 범위는 중요하지 않습니다. – jason

6

적어도 한 번씩 각 숫자를 살펴볼 필요가 있기 때문에 선형 시간보다 더 빨리 얻을 수 없습니다.

숫자가 특정 범위에 있음을 알고있는 경우 추가 배열을 사용하여 각 숫자의 합계를 계산할 수 있습니다. 그렇지 않으면 해시 테이블이 약간 느려야합니다.

이 두 가지 모두 추가 공간이 필요하지만 결과를 얻으려면 끝에 다시 반복해야합니다.

실제로 엄청난 양의 숫자가 있고 O (n) 런타임이 절대적으로 필요한 경우가 아니면 단순히 숫자 배열을 정렬 할 수 있습니다. 그런 다음 숫자를 한 번 보면서 현재 숫자의 수와 최대 발생 수를 두 변수로 유지하면됩니다. 그래서 당신은 많은 시간을 절약하면서 많은 공간을 절약 할 수 있습니다.

+3

"적어도 각 번호를 한 번 봐야합니다." -이 문장은 거짓이다. –

+5

일부 입력 사례에서는 가장 많이 나타나는 입력 번호를 찾기 위해 모든 입력 번호를 볼 필요는 없지만 "가장 빈번한 횟수"를 찾으려면 모든 입력 번호를 살펴야합니다. 행에있다 "고 말했다. 프랭크가 맞습니다. –

+1

분명히 나에게 보인다. x를 보지 않고 x가 승자의 사본 인 경우 x가 발생한 횟수를 올바르게보고 할 수 있습니까? –

1

여기에 그 (N 로그 n) O이며, 단순한 하나입니다

보편적 인 방법 "을 정렬하고 긴 순서를 검색"입니다 O(nlog n)입니다 :

Sort the vector @ O(n log n) 
Create vars: int MOST, VAL, CURRENT 
for ELEMENT in LIST: 
    CURRENT += 1 
    if CURRENT >= MOST: 
     MOST = CURRENT 
     VAL = ELEMENT 
return (VAL, MOST) 
+0

프랭크도 나를 이길거야! O (n log n) 타이밍이지만, 추가 공간을 필요로하지 않습니다. 물론 전체 목록 (즉 스트림이 아님)이 있다고 가정합니다. –

+0

은 파이썬처럼 보입니다 :-) – Kugel

+0

첫 번째 두 줄을 제외하고 * 유효한 파이썬입니다. :) –

1

몇 가지 방법이 있습니다. 가장 빠른 정렬 알고리즘은 quicksort (평균, 최악의 경우 O(n^2))입니다. 또한 heapsort를 사용할 수는 있지만 평균적인 경우에는 상당히 느리지 만 최악의 경우 점근 적 복잡도는 O(n log n)입니다.

숫자에 대한 정보가 있으면 몇 가지 트릭을 사용할 수 있습니다. 숫자가 제한된 범위에서 나온다면 알고리즘의 일부를 사용하여 정렬 수를 계산할 수 있습니다. O(n)입니다.

이것이 사실이 아니라면 선형 시간에는 할 수 있지만 다른 누구도 보편적이지 않은 정렬 알고리즘이 있습니다.

+0

quicksort는 거의 가장 빠른 알고리즘입니다. – Kugel

+0

예, 평균적인 경우입니다.통계를 확인하십시오 – Gaim

+0

선형 복잡도를 갖는 알고리즘은 일반적인 경우에는 사용할 수 없으며 비교를 기반으로 한 알고리즘에서는 평균이 가장 빠른 QS입니다. – Gaim

0

편집 : 사용되지 않는 비교 기능을 사용하지 않았습니다.

#Python 3.1 
lst = [1,1,5,1,3,7,2,1,8,9,1,2] 

dct = {} 
for i in lst: 
    if i in dct: 
     dct[i] += 1 
    else: 
     dct[i] = 1 

mx = max(dct.keys(), key=lambda k: dct[k]) 

print('Value {0} appears {1} times.'.format(mx, dct[mx])) 

>>> 
Value 1 appears 5 times. 
+1

어디에서 비교 기능을 사용합니까? – Kugel

+1

파이썬 3.1에서 이것을 코딩하려면이 문제에 가장 적합한'collections.Counter'를 찾아보십시오. http://docs.python.org/dev/py3k/library/collections.html – hughdbrown

+0

감사합니다. 불필요한 것이 었습니다. –

4

선형 시간 문제 (입력 항목 수에 선형)를 해결하는 알고리즘이 있습니다 : 여기

파이썬 3.1 IMPL입니다.아이디어는 해시 테이블을 사용하여 입력의 각 값에 값이 표시된 횟수를 나타내는 계수를 연결하는 것입니다. 예상 입력에 대해 프로파일을 작성하고 이것이 요구 사항을 충족시키는 지 확인해야합니다.

여기에는 O(n) 여분의 공백이 사용됩니다. 이것이 받아 들일 수 없다면 다른 사람들이 제안한 것처럼 입력을 정렬하는 것이 좋습니다. 그 해결책은 시간에 O(n log n)이고 공간에 O(1)이 될 것입니다.

여기 std::tr1::unordered_map을 사용하여 C의 구현 ++ 같습니다

#include <iostream> 
#include <unordered_map> 

using namespace std; 
using namespace std::tr1; 

typedef std::tr1::unordered_map<int, int> map; 

int main() { 
    map m; 

    int a[12] = {1, 1, 5, 1, 3, 7, 2, 1, 8, 9, 1, 2}; 
    for(int i = 0; i < 12; i++) { 
     int key = a[i]; 
     map::iterator it = m.find(key); 
     if(it == m.end()) { 
      m.insert(map::value_type(key, 1)); 
     } 
     else { 
      it->second++; 
     } 
    } 
    int count = 0; 
    int value; 
    for(map::iterator it = m.begin(); it != m.end(); it++) { 
     if(it->second > count) { 
      count = it->second; 
      value = it->first; 
     } 
    } 

    cout << "Value: " << value << endl; 
    cout << "Count: " << count << endl; 
} 

알고리즘은 각 정수가 나타나는 횟수를 카운트하는 해시 테이블에서 키로 입력 된 정수를 사용하여 작동한다. 따라서 알고리즘의 열쇠 (의도 된 말장난)이 해시 테이블을 구축하고있다 : 우리는 우리의 입력 목록에서 i 번째 요소에서 찾고있다

int key = a[i]; 
map::iterator it = m.find(key); 
if(it == m.end()) { 
    m.insert(map::value_type(key, 1)); 
} 
else { 
    it->second++; 
} 

그래서 여기에. 그러면 우리가하는 일은 우리가 이미 그것을 보았는지를 보는 것입니다. 그렇지 않은 경우이 새 정수가 포함 된 해시 테이블에 새 값을 추가하고 초기 수를 나타내는 첫 번째 카운터가 처음으로 표시된다는 것을 나타냅니다. 그렇지 않으면이 값과 관련된 카운터를 증가시킵니다.

int count = 0; 
int value; 
for(map::iterator it = m.begin(); it != m.end(); it++) { 
    if(it->second > count) { 
     count = it->second; 
     value = it->first; 
    } 
} 

는 현재 두 가지 값이 나타나는 경우를 처리 할 논리가 없다 : 우리가이 표를 구축하면

, 그것은 단순히 대부분의 표시를 찾기 위해 값을 통해 실행의 문제 동일한 횟수와 횟수는 모든 가치 중에서 가장 큽니다. 자신의 필요에 따라 스스로 처리 할 수 ​​있습니다.

+1

값 -> 개수 맵의 모든 요소에 대해 최종 반복의 가격을 지불하고 싶지는 않을 것입니다. 계속 진행하면서 가장 높은 수 (및 그 수)로 값을 추적 할 수 있습니다. – user242275

0

여기서 얻을 수있는 가장 좋은 시간 복잡성은 O (n)입니다. 마지막 요소는 모드를 결정하는 요소 일 수 있기 때문에 모든 요소를 ​​살펴야합니다.

해결 방법은 시간이나 공간이 더 중요한지 여부에 달려 있습니다.

공간이 더 중요한 경우 목록을 정렬 한 다음 가장 긴 연속 요소 시퀀스를 찾을 수 있습니다.

시간이 중요한 경우 각 요소의 발생 횟수 (예 : 해싱 요소 -> 개수)를 유지하면서 목록을 반복 할 수 있습니다. 이 작업을 수행하는 동안 최대 개수의 요소를 추적하고 필요한 경우 전환합니다.

모드가 대부분 요소 인 경우 (이 값이 배열에 n/2 개 이상의 요소가있는 경우) O(n) speed and O(1) space efficiency을 얻을 수 있습니다.

0

파이썬 2.6 솔루션은 아래 각 숫자의 수를 제공

>>> from collections import defaultdict 
>>> lst = [1,1,5,1,3,7,2,1,8,9,1,2] 
>>> d = defaultdict(int) 
>>> for i in lst: 
...  d[i] += 1 
... 
>>> max_occurring = max((v, k) for k, v in d.items()) 
>>> print "%d occurs %d times" % (max_occurring[1], max_occurring[0]) 
1 occurs 5 times 
+0

@MrFooz 제안 된 코드 변경을 실행하지 않았습니다. 그리고 당신은 제 생성기가'items()'의 키와 값의 순서를 뒤집는 것을 보지 못했습니다. 이것은 중요합니다. 그렇지 않으면 키가 아닌 값을 기준으로 정렬합니다. – hughdbrown

+0

네 말이 맞아. 원본 댓글이 삭제되었습니다. –

0

. 시간과 공간면에서 맵을 사용하는 것보다 나은 접근 방법입니다. 가장 많은 횟수로 나타난 번호를 가져와야하는 경우 이전 번호보다 좋지 않습니다.

편집 :이 방법은 부호없는 숫자와 1부터 시작하는 숫자에 유용합니다.

std::string row = "1,1,5,1,3,7,2,1,8,9,1,2"; 
    const unsigned size = row.size(); 
    int* arr = new int[size]; 
    memset(arr, 0, size*sizeof(int)); 
    for (int i = 0; i < size; i++) 
    { 
     if (row[i] != ',') 
     { 
      int val = row[i] - '0'; 
      arr[val - 1]++; 
     } 
    } 

    for (int i = 0; i < size; i++) 
     std::cout << i + 1 << "-->" << arr[i] << std::endl; 
1

일반 C++ 솔루션 :

#include <algorithm> 
#include <iterator> 
#include <map> 
#include <utility> 

template<class T, class U> 
struct less_second 
{ 
    bool operator()(const std::pair<T, U>& x, const std::pair<T, U>& y) 
    { 
     return x.second < y.second; 
    } 
}; 

template<class Iterator> 
std::pair<typename std::iterator_traits<Iterator>::value_type, int> 
most_frequent(Iterator begin, Iterator end) 
{ 
    typedef typename std::iterator_traits<Iterator>::value_type vt; 
    std::map<vt, int> frequency; 
    for (; begin != end; ++begin) ++frequency[*begin]; 
    return *std::max_element(frequency.begin(), frequency.end(), 
          less_second<vt, int>()); 
} 

#include <iostream> 

int main() 
{ 
    int array[] = {1, 1, 5, 1, 3, 7, 2, 1, 8, 9, 1, 2}; 
    std::pair<int, int> result = most_frequent(array, array + 12); 
    std::cout << result.first << " appears " << result.second << " times.\n"; 
} 

하스켈 솔루션 :

import qualified Data.Map as Map 
import Data.List (maximumBy) 
import Data.Function (on) 

count = foldl step Map.empty where 
    step frequency x = Map.alter next x frequency 
    next Nothing = Just 1 
    next (Just n) = Just (n+1) 

most_frequent = maximumBy (compare `on` snd) . Map.toList . count 

example = most_frequent [1, 1, 5, 1, 3, 7, 2, 1, 8, 9, 1, 2] 

짧은 하스켈 솔루션 스택 오버 플로우의 도움이 :

이 이후
import qualified Data.Map as Map 
import Data.List (maximumBy) 
import Data.Function (on) 

most_frequent = maximumBy (compare `on` snd) . Map.toList . 
       Map.fromListWith (+) . flip zip (repeat 1) 

example = most_frequent [1, 1, 5, 1, 3, 7, 2, 1, 8, 9, 1, 2] 
0

숙제 I입니다 그것이 괜찮다고 생각해. 다른 언어로 솔루션을 제공하십시오. 다음과 같은 스몰 토크 뭔가

좋은 출발점이 될 것입니다 : 내가 배열 및 여러 루프가 그 일을 시도했습니다

SequenceableCollection>>mode 
    | aBag maxCount mode | 

    aBag := Bag new 
      addAll: self; 
      yourself. 
    aBag valuesAndCountsDo: [ :val :count | 
    (maxCount isNil or: [ count > maxCount ]) 
     ifTrue: [ mode := val. 
       maxCount := count ]]. 

    ^mode 
관련 문제