실제로 피보나치 힙을 효율적으로 구현 한 사람이 있습니까?


151

피보나치 힙을 구현 한 사람이 있습니까? 나는 몇 년 전에 그렇게했지만 배열 기반 BinHeaps를 사용하는 것보다 몇 배나 느 렸습니다.

당시 나는 그것이 연구가 항상 주장하는 것만 큼 좋지 않은 방법에 대한 귀중한 교훈이라고 생각했습니다. 그러나 많은 연구 논문은 피보나치 힙을 사용한 알고리즘의 실행 시간을 주장합니다.

효율적인 구현을 수행 한 적이 있습니까? 아니면 피보나치 힙이 더 효율적일 정도로 큰 데이터 세트로 작업 했습니까? 그렇다면 일부 세부 사항을 주시면 감사하겠습니다.


25
이 알고리즘 친구들이 항상 큰 상수 뒤에 큰 상수를 숨기는 것을 배웠습니까? :) 실제로는 "n"이 "n0"에 가까워지지 않는 것 같습니다.
Mehrdad Afshari

나는 이제 안다. "알고리즘 소개"를 처음 받았을 때 구현했습니다. 또한, 그의 Splay-Trees는 실제로 매우 멋지 기 때문에 쓸모없는 데이터 구조를 발명하려는 사람을 위해 Tarjan을 선택하지 않았습니다.
mdm

mdm : 물론 쓸모는 없지만 작은 데이터 세트에서 퀵 정렬을이기는 삽입 정렬과 마찬가지로 이진 힙은 작은 상수로 인해 더 잘 작동 할 수 있습니다.
Mehrdad Afshari

1
실제로 필자가 힙을 필요로하는 프로그램은 VLSI 칩에서 라우팅 할 Steiner-Trees를 찾는 것이 었으므로 데이터 세트는 정확히 작지 않았습니다. 그러나 요즘 (정렬과 같은 간단한 것들을 제외하고) 나는 데이터 세트에서 "깨질"때까지 항상 더 간단한 알고리즘을 사용합니다.
mdm

1
이것에 대한 나의 대답은 실제로 "예"입니다. (음, 논문의 공동 저자는 그랬습니다.) 지금 코드가 없으므로 실제로 응답하기 전에 더 많은 정보를 얻을 것입니다. 그러나 그래프를 보면 F 힙은 b 힙보다 비교가 적습니다. 비교가 저렴한 것을 사용하고 있었습니까?
A. Rex

답변:


136

부스트 C ++ 라이브러리 에서 피보나치 힙의 구현을 포함한다 boost/pending/fibonacci_heap.hpp. 이 파일은 pending/수년 동안 사용 되어 왔으며 내 계획으로는 결코 받아 들여지지 않을 것입니다. 또한, 그 구현에 버그가 있었는데, 내 지인과 만능의 멋진 남자 Aaron Windsor가 수정했습니다. 불행히도, 온라인에서 찾을 수있는 대부분의 파일 버전 (및 Ubuntu의 libboost-dev 패키지에있는 버전)에는 여전히 버그가있었습니다. Subversion 저장소에서 깨끗한 버전 을 가져와야 했습니다 .

버전 1.49 부터 Boost C ++ 라이브러리 는 피보나치 힙을 포함하여 많은 새로운 힙 구조체를 추가했습니다.

나는 컴파일 할 수 있었다 dijkstra_heap_performance.cpp을 의 수정 된 버전에 대해 dijkstra_shortest_paths.hpp 피보나치 힙 및 이진 힙을 비교. (라인 typedef relaxed_heap<Vertex, IndirectCmp, IndexMap> MutableQueue에서으로 변경 relaxed하십시오 fibonacci.) 먼저 최적화로 컴파일하는 것을 잊었습니다.이 경우 피보나치 및 이진 힙은 거의 동일하게 수행되며 피보나치 힙은 일반적으로 중요하지 않은 양만큼 성능이 뛰어납니다. 매우 강력한 최적화로 컴파일 한 후 바이너리 힙이 크게 향상되었습니다. 내 테스트에서 피보나치 힙은 그래프가 엄청나게 크고 밀도가 높을 때 이진 힙보다 성능이 뛰어납니다.

Generating graph...10000 vertices, 20000000 edges.
Running Dijkstra's with binary heap...1.46 seconds.
Running Dijkstra's with Fibonacci heap...1.31 seconds.
Speedup = 1.1145.

내가 아는 한, 이것은 피보나치 힙과 이진 힙의 근본적인 차이점을 다룹니다. 두 데이터 구조 사이의 유일한 이론적 인 차이점은 피보나치 힙이 일정한 시간에 감소 키를 지원한다는 것입니다. 반면, 이진 힙은 배열로 구현하여 많은 성능을 얻습니다. 명시적인 포인터 구조를 사용한다는 것은 피보나치 힙이 큰 성능 저하를 겪음을 의미합니다.

따라서 실제로 피보나치 힙의 이점을 얻으려면 reduce_keys 가 매우 빈번한 응용 프로그램에서 사용해야합니다. Dijkstra와 관련하여 이는 기본 그래프가 밀집되어 있음을 의미합니다. 일부 응용 프로그램은 본질적으로 reduce_key-intense 일 수 있습니다. Nagomochi-Ibaraki 최소 자르기 알고리즘 을 시도 하고 싶었지만 명백히 많은 reduce_keys를 생성하기 때문에 타이밍 비교 작업을 수행하는 데 너무 많은 노력이 필요했습니다.

경고 : 뭔가 잘못했을 수 있습니다. 이러한 결과를 직접 재현 해 볼 수 있습니다.

이론적 참고 : reduce_key에서 피보나치 힙의 향상된 성능은 Dijkstra의 런타임과 같은 이론적 응용 프로그램에 중요합니다. 피보나치 힙은 삽입 및 병합시 이진 힙보다 성능이 뛰어납니다 (피보나치 힙의 상각 상수 시간 모두). 삽입은 Dijkstra의 런타임에 영향을 미치지 않기 때문에 본질적으로 무관하며, 상각 된 일정한 시간에 삽입되도록 바이너리 힙을 수정하는 것은 매우 쉽습니다. 일정한 시간에 병합하는 것은 환상적이지만이 응용 프로그램과 관련이 없습니다.

개인 메모 : 내 친구와 나는 한때 새로운 우선 순위 대기열을 설명하는 논문을 작성했는데, 복잡하지 않고 피보나치 힙의 (이론적) 실행 시간을 복제하려고 시도했습니다. 이 논문은 출판되지 않았지만 공동 저자는 이진 힙, 피보나치 힙 및 자체 우선 순위 대기열을 구현하여 데이터 구조를 비교했습니다. 실험 결과의 그래프는 피보나치 힙이 전체 비교 측면에서 이진 힙보다 약간 성능이 우수하다는 것을 나타내며, 이는 비교 비용이 오버 헤드를 초과하는 상황에서 피보나치 힙이 더 나은 성능을 나타냅니다. 불행히도, 사용할 수있는 코드가 없으며 아마도 귀하의 상황에서 비교가 저렴하기 때문에 이러한 의견은 관련이 있지만 직접 적용 할 수는 없습니다.

또한 피보나치 힙의 런타임을 자신의 데이터 구조와 일치시키는 것이 좋습니다. 나는 단순히 피보나치 더미를 재창조한다는 것을 알았습니다. 피보나치 더미의 모든 복잡성이 임의의 아이디어라고 생각하기 전에, 나는 그것들이 모두 자연스럽고 상당히 강제적이라는 것을 깨달았습니다.


감사! 이 질문은 오랫동안 내 마음에 앉아있었습니다. 실제로 Steiner-Trees를 시도하기 전에 Fibonacci-Heaps를 사용하여 Dijkstra를 구현했습니다. 그러나 내 그래프는 귀하의 예보다 밀도가 훨씬 낮은 것 같습니다. 그들은 수백만 개의 노드를 가지고 있지만 평균 정도는 5-6입니다.
mdm

일련의 작업을 통해 Fib 힙 성능을 예측할 수 있습니다. 나는 Fib Heap (vs. Bin Heap)으로 더 빨리 끝나는 Heap-heavy 알고리즘을 작성했습니다. 트릭은 작업을 일괄 처리했습니다. 작업 빈도에 관계없이 차이는 여기에 있습니다. DecreaseKey-ExtractMin-DecreaseKey-ExtractMin 대 DecreaseKey-DecreaseKey-ExtractMin-ExtractMin (아래에 계속)
Gaminic

두 번째 ExtractMin은 거의 무료이기 때문에 후자는 대략 두 배 빠릅니다. 우리의 알고리즘은 다수가 폐기되는 Min 요소의 배치를 추출합니다. 빈 힙에는 폐기물이 있지만 Fib 힙에는 더 좋습니다. 안타깝게도 힙 기반 알고리즘에 대해 이야기 할 때 사람들이 제공하는 시간에는 분명히 반영되지 않습니다. Amortized 경계를 사용하면 총 복잡성이 단순히 # 연산 * 연산의 복잡성이 아닙니다.
Gaminic

1
힙 및 / 또는 완화 된 힙 쌍을 시도 할 가능성이 있습니까?
Thomas Ahle

1
결과가 왜 그렇게 가깝게 보이는지 잘 모르겠습니다 .STL priority_queue vs self-implemented fibonacci heap을 사용했으며 바이너리 힙은 테스트 에서 큰 차이로 뒤쳐졌습니다 .
Nicholas Pipitone

33

Knuth는 그의 책 Stanford Graphbase에 대해 1993 년에 최소 스패닝 트리에 대해 피보나치 힙과 이진 힙을 비교 했습니다 . 그는 피보나치가 테스트중인 그래프 크기에서 이진 힙보다 30 ~ 60 % 느리다는 것을 발견했습니다.

소스 코드 (C 아니라, 수학 텍 간의 크로스 CWEB 또는) 단면 MILES_SPAN의 C이다.


1

부인 성명

나는 결과가 상당히 유사하다는 것을 알고 있으며 "런타임이 완전히 다른 것보다 우세한 것처럼 보인다"(@Alpedar). 그러나 코드에서 그 증거를 찾을 수 없었습니다. 코드가 열려 있으므로 테스트 결과에 영향을 줄 수있는 것이 있으면 알려주세요.


어쩌면 내가 뭔가 잘못을했다,하지만 난 시험을 썼다 을 기반으로, A.Rex의 비교 anwser :

  • 피보나치 힙
  • D- 아리 힙 (4)
  • 이진 힙
  • 편안한 힙

모든 힙에 대한 실행 시간 (완전한 그래프의 경우에만)은 매우 가깝습니다. 1000,2000,3000,4000,5000,6000,7000 및 8000 정점으로 완전한 그래프를 테스트했습니다. 각 테스트마다 50 개의 무작위 그래프가 생성되었으며 출력은 각 힙의 평균 시간입니다.

출력에 대해 죄송합니다. 텍스트 파일에서 일부 차트를 작성해야했기 때문에 매우 장황하지 않습니다.


결과는 초 단위입니다.

힙 결과 테이블


4
각 경우에 얼마나 많은 모서리가 있습니까? 그리고 정확히 어떤 알고리즘을 실행하고 있습니까? 우리가 다루고있는 것을 모르는 경우 결과가 의미가 없습니다.
kokx

슬프게도 모든 그래프가 완성되었으므로 각 경우에 대한 모서리 수를 계산할 수 있습니다. 당신이 의미하는 것은 "정확히 달리고있다"는 것입니다. 그들은 테이블의 머리에 있습니다.
길 레르 메 토레스 카스트로

22
이것은 실행 시간이 힙이 아닌 다른 것 (그래프 또는 일부 IO 생성 일 수 있음)에 의해 지배되는 것처럼 보입니다. 거의 똑같은 결과는 믿을 수 없습니다.
Alpedar

2
아마도 시간 os가 다른 것에 의해 지배되었을 수도 있지만, 이것이 IO 또는 그래프 생성이 아니라고 확신합니다. 어쨌든 소스 코드를 사용할 수 있으며 누군가 오류를 발견하고 측정을 수정하면 매우 기쁠 것입니다.
길 레르 메 토레스 카스트로

이 테스트는 완전히 다른 것을 측정하는 것 같습니다. 실행 한 테스트에 대해 언급 해 주시겠습니까? 거리가 기하 / 유클리드 인 경우 전체 그래프에서 최단 경로 문제는 O (1)입니다.
Gaminic

0

나는 또한 피보나치 더미로 작은 실험을했습니다. 세부 사항에 대한 링크는 다음과 같습니다. Experimenting-with-dijkstras-algorithm . 방금 "Fibonacci heap java"라는 용어를 검색하고 피보나치 힙의 기존 오픈 소스 구현 몇 가지를 시도했습니다. 그들 중 일부는 성능 문제가있는 것 같지만 꽤 좋은 것이 있습니다. 적어도, 그들은 내 테스트에서 순진하고 바이너리 힙 PQ 성능을 뛰고 있습니다. 아마도 그들은 효율적인 것을 구현하는 데 도움이 될 수 있습니다.

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