알고리즘 질문입니다.
글쓴이: zflute / 작성시간: 화, 2004/02/24 - 6:32오후
다음과 같은 집합이 있습니다.
Quote:
A = 3, 5, 4, 1
B = 4, 2, 5, 1, 6, 9, 10
B의 원소 중 A에 포함되는 것을 제외한 결과를 A와 합치고자 합니다.
결론적으로,
Quote:
C = 3, 5, 4, 1, 2, 6, 9, 10
으로 만들려고 합니다. 단, 여기서 각 숫자 값은 특정한 값으로 순서가 변경되면 안 됩니다.
가장 쉬운 방법은 A의 각 원소에 대해서 B의 원소를 하나씩 검사해서 중복되는 것일 경우 제거시키고 남은 B의 값을 A에 붙여주면 되는데... 이렇게 할 경우 O(n^2)이 될 것 같은데요.
A의 갯수가 1,000개이고 B의 갯수가 10,000개 이면 최악의 경우 10^7번 루프를 돌아야 하는군요. :shock:
좀 효율적인 알고리즘 없을까요?
Forums:
아래같은 건... [code:1]int A[4]
아래같은 건...
대강 이런 방법이 생각나네요. (위 코드는 돌려보지 않은, 즉 검증안 된 코드입니다.)
딱, 원소수만큼만 돌지만, 100,000 개의 원소들이 있다면 100,000 바이트의 메모리를 필요로 하고, 원소의 타입에 따른 크기(위 코드에서는 4바이트) * 100,000 바이트의 메모리가 결과 취합용으로 하죠.
고수 분들의 알고리즘이 보고 싶네요. :)
PS) 설마.. 저 위의 간단한 코드에 버그가 있진 않겠죠? :shock:
[code:1]if( flag[ A[i]
이렇게 사용하기 위해서는 A, B의 원소 중 가장 큰 값을 알아야 flag 변수 크기를 잡을 수 있지 않을까요? 메모리 낭비는 심하지만 좋은 아이디어 감사합니다. 적당한 해쉬 함수를 만들어서 쓰면 메모리 문제를 해결할 수 있을 것 같습니다.
왠지 '생각하는 Programming' 을 읽고 싶어지는군요...
Knuth 에게 Literate Programming 을 부탁하는 E-mail 이라도 보낼까요?
-_-
A, 와 B 의 각 set 에 자신의 set 내의 순서와 원래 어느 set 에 존재했는지 기록한 다음 정렬하고 합병한 후 다시 원래대로 정렬하는 것을 생각해볼 수 있겠군요.
A 의 크기가 M 이고 B 의 크기가 N 이면
1. 자신의 set 내의 순서와 어느 set 인지 기록한다.
time complexity : M + N
2. 각 set 을 정렬한다.
time complexity : M log M + N log N
3. 두 set 을 합병한다.
time complexity : M + N
4. 두 set 을 재정렬한다.
time complexity : (M+N) log (M+N)
이 경우 비교우선순위는 우선 어느 set 에 속해 있는지 본 후
같은 set 에 속해 있다면 원래의 set 의 자기 순서를 비교하면 되겠죠.
결국 최대 연산비용은 4번이 되는군요.
그다지 좋아보이지 않군요. -_-
위의 경우는 정렬 algorithm 에서 Bucket Sort 라고 하나요?
http://en.wikipedia.org/wiki/Sort_algorithm
WikiPedia 에서.
생각하는 Programming 의 첫 주제에서 bit 연산을 도입해서
1/8 로 필요한 용량을 줄이는 방법을 보여주었죠.
네, 값의 범위가 정해져 있어야 혹은 값의 범위를 알아 내야 가능한 방법
네, 값의 범위가 정해져 있어야 혹은 값의 범위를 알아 내야 가능한 방법입니다. 제가 잠시 착각을 했네요.
[quote]생각하는 Programming 의 첫 주제에서 bit 연
네, 비트 연산으로 하면 1/8로 줄일 수 있죠. 비트 연산으로 코드를 작성할까 하다 귀찮아져서...... :oops:
조금 개선.
합병할 때 linked list 를 사용한다면 B 에 속한 원소들을 무조건 뒤로 보낼 수 있으므로 굳이 자신이 어느 set 에 속해 있는지 기록할 필요는 없군요.
그리고 재정렬 비용도 자신의 set 내에서만 하면 되므로 최악 M log M + N log N 이면 되겠습니다.
최대비용은 2번이 되는군요.
재정렬인 4번은 원소가 줄어들 수 있으므로 2번보다 커지지는 않네요. 하지만 linked list 정렬은 까다로운데... -_-. 제가 기억하기로 STL 에서는 정렬범위의 절반에 해당하는 추가용량이 없으면 N * (log N) ^ 2 를 허용하는 것으로 기억...
그런데 역시 hash table 좋죠!!! (^_^)
파이썬으로 무식하게 비교하는 것이긴 하지만 올려봅니다[code:1
파이썬으로 무식하게 비교하는 것이긴 하지만 올려봅니다
A, B 각 집합의 원소들이 중복되지는 않는지요?
A, B 각 집합의 원소들이 중복되지는 않는지요?
1. A를 소팅해서 D를 만든다.2. B원소 개수만큼 루프를 돌면서
1. A를 소팅해서 D를 만든다.
2. B원소 개수만큼 루프를 돌면서 B[i] 를 D에서 찾는다. (binary_search같은)
3. 찾을 수 없으면 A에 B[i]를 추가한다.
이렇게 하면 D 복사 소팅 + M * logN 정도가 아닐까요?
bugii 님 Nice!!!
이 이상 줄어들 수 있을까요?
8) 감사합니다.p.s. i가 세개입니다. -_-;
8) 감사합니다.
p.s. i가 세개입니다. -_-;
[quote="ez8"]파이썬으로 무식하게 비교하는 것이긴 하지만 올려봅
방금 테스트 해봤는데... 절망이군요.
대신에
가 더 괜찮은 듯 싶습니다.
[quote="bugiii"]1. A를 소팅해서 D를 만든다.2. B
복사/정렬 대신에 hash를 쓰세요. 대략 O(M+N) 시간에 끝낼 수 있습니다.
[quote="ez8"][quote="ez8"]파이썬으로 무식하게 비교하
우선 a를 sets로 바꾸는 게 어떨까요? set 이 membership test에 더 유리할테니깐요. sets는 python 2.3부터 지원됩니다.
흠..
제가 문제를 잘못 이해한건가요? 무지 쉬워 보이는데..
A 는 그냥 더하고.. A 에서 원소가 발생 했는지 안했는지만 체크해서..
B 를 더할때 A 에서 발생 하지 않은것만 추가하면 될거 같은데..
A = [3,5,4,1]
B = [4,2,5,1,6,9,10]
C = A # 즉 C 랑 A 랑 같음..
Table = [0 for i in range(0,100)]
for i in A:
Table[i] = Table[i] + 1
for i in B:
if Table[i] == 0:
C.append(i)
print C
A의 원소를 얼만큼 적게 비교하냐가 문제니까요...
A의 원소를 얼만큼 적게 비교하냐가 문제니까요...
여러분들 감사합니다.
다들 열심히 고민해 주셔서 감사드립니다.
집에 오면서 전철 안에서 생각해 봤는데, chaeso님과 같은 생각입니다.
주어진 값들이 정수인 만큼 적당히 큰 배열을 잡아서 A의 값을 배열의 각 element 인자로 생각하고 변수에는 플래그를 걸어둡니다. 그런 다음에 B의 값에 대해서 배열의 값이 플래그가 셋팅되어 있는지를 보고 원래 A의 뒤에 붙여줄 것인지 결정하면 되는군요.
지금 다루려는 값이 실제로 몇 억 정도까지도 갈 수 있는 정수 값이기 때문에 비트 연산을 통해서 잘 쓰면 메모리도 그렇게 많이 필요하지 않을 것 같구요.
이렇게 하면, cdpark 님 말씀대로 O(N+M) 이 되는군요.
글 실수인지... -_-
Bucket Sort 를 활용해서 1억까지의 수를 다룬다면 간다면 bit 연산을 해도 12.5MB 가 필요할텐데... -_-
위의 chaeso 님의 source 대로라면...
8억이면 100MB... -_-
어찌됐든 hash 인가요?
그런데 bugiii 는 Number 3 처럼 Bug Three 라고 읽어야 하나요... -_-
저는 'bug 가 많은' 이라는 형용사처럼 읽어야 하는 줄 알았습니다.
buggy 가 변형됐는줄 알았죠...
최대값이 몇억이라면 비트저장에만도 엄청난 메모리가 소비될텐데요... 그리
최대값이 몇억이라면 비트저장에만도 엄청난 메모리가 소비될텐데요... 그리고 메모리 초기화 하는데도....
또하나 문제가 B원소값이 이리저리 점프하는 성질을 가졌다면 발생 비트 테이블의 페이지 문제로 속도가 확 떨어질 것입니다.
cdpark님께서 말씀하신 해쉬가 값의 존재 여부를 가장 빠르게 판단할 수 있는 것으로 보입니다. 정수라면 해쉬 함수를 크게 걱정하지 않아도 될테고... 그래도 정렬된 vector 의 binary_search도 왠만큼하니까...
p.s. cdpark님은 C++에서 어떤 해쉬 컨테이너를 사용하고 계십니까?
p.s. 벌레3호입니다.
비트 + 해쉬
실제 몇억이상의 숫자이고.. 그 간격이 촘촘하지 않다면..
해쉬를 이용한 비트플래그 세팅도 괜찮을 듯 합니다.
그렇네요...
숫자의 범위가 대충 1~10 정도 될거라고 예상했거든요..
답은 해쉬밖에 없군요
A 의 집합이 10000 개가 나온다면 테이블을 5001개 정도 잡고 해슁(+Quadratic probing) 써서 처리하시면 될듯..
생각하는 프로그래밍의 첫 주제가 생각나는군요.정렬을 한다는 가정이
생각하는 프로그래밍의 첫 주제가 생각나는군요.
정렬을 한다는 가정이 들어있었으면 거의 똑같은 알고리즘 일텐데.. 정렬이 되면 안된다는 조건이 들어가서 약간의 시간이 더 소요될것 같군요.
A = 3, 5, 4, 1
=> 10111
B = 4, 2, 5, 1, 6, 9, 10
=> 1101110011
교집합은 1111110011정도..
A의 개수와 B의 개수의 합정도의 시간.
만약, A안에 같은 수가 중복된다는 가정이 있다면 이 알고리즘은 사용불가겠지요.(추가 비용이 더 들어가거나 - 물론 B도 마찬가지겠군요.)
A와 대응되는 bit를 clear시켜주면서 A를 결과로 뿌려주고 A를 전부 뿌려준 후 남아있는 bit map과 대응되는 B를 뿌려주면.. 시간이 얼마나 나올까요.. 음..
어느순간부터인가 하루살이의 하루를 알고싶다.
tree구조로 해결하면 될 듯합니다.
zflute님이 생각한 것과 거의 같구요.
다른 점이라면 원소가 있는지 없는지 찾을 때 다 찾는게 binary로 찾는다는거죠.
그것만으로도 O(NlogM)정도의 복잡도를 가질 수 있습니다.
다음은 C++ 예제입니다.
이렇게 하면 set c에 저장이 되겠네요.
또 순서가 필요하다면 int result[]배열을 만들고 c에 있는지 없는지 확인하면 해서 result에 더하면 될 것 같습니다.
A = 3, 5, 4, 1 B = 4, 2, 5, 1, 6, 9,
A = 3, 5, 4, 1
B = 4, 2, 5, 1, 6, 9, 10
[A&A=null, B&B=null]
어
Re: 알고리즘 질문입니다.
각원소에 비트를 할당할이유가 없어보이는데 그리고 그비트도 실제 만만치 않을듯.
일단 A를출력한후 A와B를 비교하여 원소가 많은쪽을 정렬하시고
만일 A가 많다면 간단히 B를 A에서찾아 없으면 출력하시면되고
만일 B가 많다면 A를 읽어 B값을 찾아 제거한후 출력하시면 됩니다.
----------------------------------------------------------------------------
ruby:[code:1]C = A << B-A[/cod
ruby:
C = A << B-A
이렇게 해야 할 것 같다고 생각되면 실제로 그렇게 하면 되는 ...
----
http://nohmad.tumblr.com/
메모리를 좀 더 사용해서...
대충 자바로 하면..
다시 생각해 보니 contains가 필요하군요. contains로 물어 보지 않으면 순서가 바뀌니까.. 역시 알고리즘은 차분히 생각을 해봐야 하는가 봅니다.
Re: 알고리즘 질문입니다.
대략 말씀하신 과정이 GivenJazz 님이 말씀하신 O(NlogM) 일듯 싶군요.
두 집합의 원소의 갯수를 각각 M, N 이라 하고, N <= M 이라 하고요,
정렬 알고리즘은 O(n log n) 을 쓰고 검색 알고리즘을 O(log n) 알고리즘을 쓴다면,
N log N (정렬) + M * log N (M개의 원소를 정렬된 집합에서 검색)
= (M + N) log N
이 되겠네요.
댓글 달기