pairing_heap 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 06be98427116306a2d0968edb9757188dde8d9ee97226ab03edf7fe4aba8d9d6
4
- data.tar.gz: f91175ec148649d8a1987e8af3935b6f96740c0b21b6bf2e34dc154b355c35a8
3
+ metadata.gz: bfbfbd0337f23ec71179ae039bd54331178fbe5317c7fdd53730f8e9b2152656
4
+ data.tar.gz: a8fa80b6831cf3bbf2b13bafd72d7f899748c1698ac1c1d6300a3378b49a9dcc
5
5
  SHA512:
6
- metadata.gz: 5441d35660f0fec2c66f4e1ea5c4b89499d9e4d97f96e01b39f5fada5c462fafe75196562a611a2ae85e95bceb006a4b9fbd1252d9135a5859e4cb165b57ab74
7
- data.tar.gz: 01b5f44ca3f608ca26091c5fd983965e95591fd097f9a82f87ef87e28bb01c8f66f0f6bf21bdbad2f9dcfc1f0b70fe34243a277d6a743b24c4d03f6377b419af
6
+ metadata.gz: 87d4f15844b9772ae6a13c2f1ec4a259f16a5a3091eaf4148730d487cc77d602939e3409de4314de8e1457800b718d6cb5d9624c81a7977eceef1f46d29ee02b
7
+ data.tar.gz: a78e724d551ec09e7058ad3f7a50580cff5769d5b5269973dacd36e85486df9dfdfb21da0606c6738bd134f6d151cbfbc50a3ace2f246433badd2cb02fbe91a2
data/Gemfile.lock CHANGED
@@ -1,16 +1,16 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pairing_heap (0.1.0)
4
+ pairing_heap (0.2.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
- minitest (5.14.3)
10
- rake (13.0.3)
9
+ minitest (5.15.0)
10
+ rake (13.0.6)
11
11
 
12
12
  PLATFORMS
13
- x86_64-darwin-20
13
+ x86_64-darwin-21
14
14
 
15
15
  DEPENDENCIES
16
16
  minitest (~> 5.0)
@@ -18,4 +18,4 @@ DEPENDENCIES
18
18
  rake (~> 13.0)
19
19
 
20
20
  BUNDLED WITH
21
- 2.2.3
21
+ 2.3.6
data/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  PairingHeap is a pure Ruby priority queue implementation using a pairing heap as the underlying data structure. While a pairing heap is asymptotically less efficient than the Fibonacci heap, it is usually faster in practice. This makes it a popular choice for Prim's MST or Dijkstra's algorithm implementations.
4
4
 
5
+ Also implementation without priority change support is provided(`SimplePairingHeap`), while the asymptotical complexity of the methods stay the same, bookkeeping of elements is not needed making the constant smaller.
6
+
5
7
  ## Installation
6
8
 
7
9
  Add this line to your application's Gemfile:
@@ -18,10 +20,24 @@ Or install it yourself as:
18
20
 
19
21
  $ gem install pairing_heap
20
22
 
23
+
24
+ ## Documentation
25
+ https://rubydoc.info/gems/pairing_heap
26
+
21
27
  ## Usage
22
28
  ```ruby
23
29
  require 'pairing_heap'
24
30
 
31
+ # Simple PairingHeap
32
+ simple_heap = PairingHeap::SimplePairingHeap.new
33
+ simple_heap.push(:a, 1)
34
+ simple_heap.push(:b, 2)
35
+ simple_heap.push(:c, 3)
36
+ simple_heap.peek # => :a
37
+ simple_heap.peek_priority # => [:a, 1]
38
+ simple_heap.pop_priority # => [:a, 1]
39
+ simple_heap.pop # => :b
40
+
25
41
  # Min priority queue
26
42
  best_defenses = PairingHeap::MinPriorityQueue.new
27
43
  best_defenses.push('Chelsea', 24)
@@ -72,30 +88,33 @@ This API is a drop-in replacement of [lazy_priority_queue](https://github.com/ma
72
88
  * `change_priority` returns `self` instead of the first argument
73
89
  * `enqueue` returns `self` instead of the first argument
74
90
  * Queue classes are in the `PairingHeap` namespace, so `require 'pairing_heap` does not load `MinPriorityQueue` to the global scope
75
- * `top_condidition` constructor argument is removed
91
+ * `top_condition` constructor argument is removed
76
92
 
77
93
  ## Time Complexity
78
- | Operation | Time complexity | Amortized time complexity |
79
- | --------------- | --------------- | ------------------------- |
80
- | enqueue | O(1) | O(1) |
81
- | peek | O(1) | O(1) |
82
- | change_priority | O(1) | o(log n) |
83
- | dequeue | O(n) | O(log n) |
84
- | delete | O(n) | O(log n) |
94
+ | Operation | Time complexity | Amortized time complexity |
95
+ | --------------- | --------------- | ------------------------- |
96
+ | enqueue | O(1) | O(1) |
97
+ | peek | O(1) | O(1) |
98
+ | dequeue | O(n) | O(log n) |
99
+ | * change_priority | O(1) | o(log n) |
100
+ | * delete | O(n) | O(log n) |
101
+
102
+ `*` Not available in `SimplePairingHeap`
85
103
 
86
104
  ## Benchmarks
87
- I picked the two fastest pure Ruby priority queue implementations I was aware of for the comparison:
105
+ I picked the three fastest pure Ruby priority queue implementations I was aware of for the comparison:
88
106
 
89
107
  * [lazy_priority_queue](https://github.com/matiasbattocchia/lazy_priority_queue) that uses a lazy binomial heap. This is probably the most popular option, used for example in [RGL](https://github.com/monora/rgl/)
90
108
  * Pure Ruby implementation of Fibonacci Heap from [priority-queue](https://github.com/supertinou/priority-queue) ([link to source](https://github.com/supertinou/priority-queue/blob/master/lib/priority_queue/ruby_priority_queue.rb))
109
+ * [rb_heap](https://github.com/florian/rb_heap) that uses a binary heap. Note however that this implementation does not support change_priority operation.
91
110
 
92
- All tests except for the third one were executed by [benchmark-ips](https://github.com/evanphx/benchmark-ips) with parameters `time = 180` and `warmup = 30`, on an `Intel(R) Core(TM) i7-10700K CPU @ 3.80GHz`.
111
+ All tests except for the third one were executed by [benchmark-ips](https://github.com/evanphx/benchmark-ips) with parameters `time = 60` and `warmup = 15`, on an `Intel(R) Core(TM) i7-10700K CPU @ 3.80GHz`.
93
112
  ### Stress test without changing priority test(N = 1000) [source code](./test/performance.rb)
94
113
  Original performance test from [lazy_priority_queue](https://github.com/matiasbattocchia/lazy_priority_queue)
95
114
  > A stress test of 1,000,000 operations: starting with 1,000 pushes/0 pops, following 999 pushes/1 pop, and so on till 0 pushes/1000 pops.
96
115
  <table>
97
116
  <tr>
98
- <th colspan="4">ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin20]</th>
117
+ <th colspan="4">ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x86_64-darwin21]</th>
99
118
  </tr>
100
119
  <tr>
101
120
  <th>Library</th>
@@ -104,25 +123,37 @@ Original performance test from [lazy_priority_queue](https://github.com/matiasba
104
123
  <th>Iterations per second</th>
105
124
  </tr>
106
125
  <tr>
107
- <td>pairing_heap</td>
108
- <td>14</td>
109
- <td>60.564595</td>
110
- <td>0.231</td>
126
+ <td>pairing_heap (SimplePairingHeap)</td>
127
+ <td>18</td>
128
+ <td>60.232046</td>
129
+ <td>0.299</td>
130
+ </tr>
131
+ <tr>
132
+ <td>pairing_heap (PairingHeap)</td>
133
+ <td>15</td>
134
+ <td>63.978031</td>
135
+ <td>0.234(1.27x slower)</td>
111
136
  </tr>
112
137
  <tr>
113
138
  <td>lazy_priority_queue</td>
114
- <td>8</td>
115
- <td>62.489819</td>
116
- <td>0.128(1.81x slower)</td>
139
+ <td>9</td>
140
+ <td>60.031283</td>
141
+ <td>0.150(1.99x slower)</td>
142
+ </tr>
143
+ <tr>
144
+ <td>rb_heap</td>
145
+ <td>9</td>
146
+ <td>60.497355</td>
147
+ <td>0.149(2.01x slower)</td>
117
148
  </tr>
118
149
  <tr>
119
150
  <td>Fibonacci</td>
120
151
  <td>8</td>
121
- <td>68.719194</td>
122
- <td>0.116(1.99x slower)</td>
152
+ <td>66.866055</td>
153
+ <td>0.120(2.50x slower)</td>
123
154
  </tr>
124
155
  <tr>
125
- <th colspan="4">jruby 9.2.14.0 (2.5.7) 2020-12-08 ebe64bafb9 OpenJDK 64-Bit Server VM 15.0.2+7 on 15.0.2+7 +jit [darwin-x86_64]</th>
156
+ <th colspan="4">ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) +YJIT [x86_64-darwin21]</th>
126
157
  </tr>
127
158
  <tr>
128
159
  <th>Library</th>
@@ -131,30 +162,120 @@ Original performance test from [lazy_priority_queue](https://github.com/matiasba
131
162
  <th>Iterations per second</th>
132
163
  </tr>
133
164
  <tr>
134
- <td>pairing_heap</td>
135
- <td>17</td>
136
- <td>61.195794</td>
137
- <td>0.278</td>
165
+ <td>pairing_heap (SimplePairingHeap)</td>
166
+ <td>22</td>
167
+ <td>62.866807</td>
168
+ <td>0.350</td>
169
+ </tr>
170
+ <tr>
171
+ <td>pairing_heap (PairingHeap)</td>
172
+ <td>16</td>
173
+ <td>61.358679</td>
174
+ <td>0.261(1.34x slower)</td>
175
+ </tr>
176
+ <tr>
177
+ <td>Fibonacci</td>
178
+ <td>14</td>
179
+ <td>64.394112</td>
180
+ <td>0.217(1.61x slower)</td>
181
+ </tr>
182
+ <tr>
183
+ <td>rb_heap</td>
184
+ <td>12</td>
185
+ <td>60.975479</td>
186
+ <td>0.197(1.78x slower)</td>
187
+ </tr>
188
+ <tr>
189
+ <td>lazy_priority_queue</td>
190
+ <td>11</td>
191
+ <td>65.568648</td>
192
+ <td>0.168(2.09x slower)</td>
193
+ </tr>
194
+ <tr>
195
+ <th colspan="4">jruby 9.3.3.0 (2.6.8) 2022-01-19 b26de1f5c5 OpenJDK 64-Bit Server VM 16.0.1+9-24 on 16.0.1+9-24 +jit [darwin-x86_64]</th>
196
+ </tr>
197
+ <tr>
198
+ <th>Library</th>
199
+ <th>Iterations</th>
200
+ <th>Seconds</th>
201
+ <th>Iterations per second</th>
202
+ </tr>
203
+ <tr>
204
+ <td>pairing_heap (SimplePairingHeap)</td>
205
+ <td>21</td>
206
+ <td>60.357577s</td>
207
+ <td>0.348</td>
208
+ </tr>
209
+ <tr>
210
+ <td>pairing_heap (PairingHeap)</td>
211
+ <td>15</td>
212
+ <td>60.417252</td>
213
+ <td>0.248(1.40x slower)</td>
138
214
  </tr>
139
215
  <tr>
140
216
  <td>lazy_priority_queue</td>
141
217
  <td>14</td>
142
- <td>64.375927</td>
143
- <td>0.218(1.28x slower)</td>
218
+ <td>61.022450</td>
219
+ <td>0.229(1.52x slower)</td>
220
+ </tr>
221
+ <tr>
222
+ <td>rb_heap</td>
223
+ <td>13</td>
224
+ <td>63.661862</td>
225
+ <td>0.204(1.70x slower)</td>
144
226
  </tr>
145
227
  <tr>
146
228
  <td>Fibonacci</td>
147
- <td>9</td>
148
- <td>67.415358</td>
149
- <td>0.134(2.08x slower)</td>
229
+ <td>8</td>
230
+ <td>62.643449</td>
231
+ <td>0.128(2.72x slower)</td>
232
+ </tr>
233
+ <tr>
234
+ <th colspan="4">jruby 9.3.3.0 (2.6.8) 2022-01-19 b26de1f5c5 OpenJDK 64-Bit Server VM 16.0.1+9-24 on 16.0.1+9-24 +indy +jit [darwin-x86_64]</th>
235
+ </tr>
236
+ <tr>
237
+ <th>Library</th>
238
+ <th>Iterations</th>
239
+ <th>Seconds</th>
240
+ <th>Iterations per second</th>
241
+ </tr>
242
+ <tr>
243
+ <td>pairing_heap (SimplePairingHeap)</td>
244
+ <td>43</td>
245
+ <td>60.472129</td>
246
+ <td>0.711</td>
247
+ </tr>
248
+ <tr>
249
+ <td>pairing_heap (PairingHeap)</td>
250
+ <td>30</td>
251
+ <td>60.359748</td>
252
+ <td>0.497(1.43x slower)</td>
253
+ </tr>
254
+ <tr>
255
+ <td>Fibonacci</td>
256
+ <td>25</td>
257
+ <td>62.084250</td>
258
+ <td>0.403(1.77x slower)</td>
259
+ </tr>
260
+ <tr>
261
+ <td>rb_heap</td>
262
+ <td>23</td>
263
+ <td>62.419893</td>
264
+ <td>0.369(1.93x slower)</td>
265
+ </tr>
266
+ <tr>
267
+ <td>lazy_priority_queue</td>
268
+ <td>22</td>
269
+ <td>60.947299</td>
270
+ <td>0.361(1.97x slower)</td>
150
271
  </tr>
151
272
  </table>
152
273
 
153
274
  ### Stress test with changing priority(N = 1000) [source code](./test/performance_with_change_priority.rb)
154
- A stress test of 2,000,000 operations: starting with 1,000 pushes/1000 change_priorities/0 pops, following 999 pushes/999 change_priorities/1 pop, and so on till 0 pushes/0 change_priorities/1000 pops.
275
+ A stress test of 1,501,500 operations: starting with 1,000 pushes/1000 change_priorities/0 pops, following 999 pushes/999 change_priorities/1 pop, and so on till 0 pushes/0 change_priorities/1000 pops.
155
276
  <table>
156
277
  <tr>
157
- <th colspan="4">ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin20]</th>
278
+ <th colspan="4">ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x86_64-darwin21]</th>
158
279
  </tr>
159
280
  <tr>
160
281
  <th>Library</th>
@@ -164,24 +285,51 @@ A stress test of 2,000,000 operations: starting with 1,000 pushes/1000 change_pr
164
285
  </tr>
165
286
  <tr>
166
287
  <td>pairing_heap</td>
167
- <td>13</td>
168
- <td>60.280165</td>
169
- <td>0.216</td>
288
+ <td>14</td>
289
+ <td>63.536300</td>
290
+ <td>0.220</td>
170
291
  </tr>
171
292
  <tr>
172
293
  <td>lazy_priority_queue</td>
294
+ <td>9</td>
295
+ <td>63.319474s</td>
296
+ <td>0.142(1.55x slower)</td>
297
+ </tr>
298
+ <tr>
299
+ <td>Fibonacci</td>
173
300
  <td>8</td>
174
- <td>67.414861s</td>
175
- <td>0.119(1.82x slower)</td>
301
+ <td>67.385714</td>
302
+ <td>0.119(1.86x slower)</td>
303
+ </tr>
304
+ <tr>
305
+ <th colspan="4">ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) +YJIT [x86_64-darwin21]</th>
306
+ </tr>
307
+ <tr>
308
+ <th>Library</th>
309
+ <th>Iterations</th>
310
+ <th>Seconds</th>
311
+ <th>Iterations per second</th>
312
+ </tr>
313
+ <tr>
314
+ <td>pairing_heap</td>
315
+ <td>15</td>
316
+ <td>62.243080</td>
317
+ <td>0.241</td>
176
318
  </tr>
177
319
  <tr>
178
320
  <td>Fibonacci</td>
179
- <td>7</td>
180
- <td>61.067436</td>
181
- <td>0.115(1.88x slower)</td>
321
+ <td>13</td>
322
+ <td>63.030390</td>
323
+ <td>0.206(1.17x slower)</td>
324
+ </tr>
325
+ <tr>
326
+ <td>lazy_priority_queue</td>
327
+ <td>10</td>
328
+ <td>64.865853</td>
329
+ <td>0.154(1.56x slower)</td>
182
330
  </tr>
183
331
  <tr>
184
- <th colspan="4">jruby 9.2.14.0 (2.5.7) 2020-12-08 ebe64bafb9 OpenJDK 64-Bit Server VM 15.0.2+7 on 15.0.2+7 +jit [darwin-x86_64]</th>
332
+ <th colspan="4">jruby 9.3.3.0 (2.6.8) 2022-01-19 b26de1f5c5 OpenJDK 64-Bit Server VM 16.0.1+9-24 on 16.0.1+9-24 +jit [darwin-x86_64]</th>
185
333
  </tr>
186
334
  <tr>
187
335
  <th>Library</th>
@@ -191,29 +339,75 @@ A stress test of 2,000,000 operations: starting with 1,000 pushes/1000 change_pr
191
339
  </tr>
192
340
  <tr>
193
341
  <td>pairing_heap</td>
194
- <td>16</td>
195
- <td>62.519677</td>
196
- <td>0.256</td>
342
+ <td>15</td>
343
+ <td>61.540851</td>
344
+ <td>0.244</td>
197
345
  </tr>
198
346
  <tr>
199
347
  <td>lazy_priority_queue</td>
200
- <td>13</td>
201
- <td>63.832733</td>
202
- <td>0.204(1.26x slower)</td>
348
+ <td>14</td>
349
+ <td>61.471507</td>
350
+ <td>0.228(1.07x slower)</td>
203
351
  </tr>
204
352
  <tr>
205
353
  <td>Fibonacci</td>
206
- <td>8</td>
207
- <td>60.250658</td>
208
- <td>0.133(1.93x slower)</td>
354
+ <td>9</td>
355
+ <td>67.393730</td>
356
+ <td>0.134(1.83x slower)</td>
357
+ </tr>
358
+ <tr>
359
+ <th colspan="4">jruby 9.3.3.0 (2.6.8) 2022-01-19 b26de1f5c5 OpenJDK 64-Bit Server VM 16.0.1+9-24 on 16.0.1+9-24 +indy +jit [darwin-x86_64]</th>
360
+ </tr>
361
+ <tr>
362
+ <th>Library</th>
363
+ <th>Iterations</th>
364
+ <th>Seconds</th>
365
+ <th>Iterations per second</th>
366
+ </tr>
367
+ <tr>
368
+ <td>pairing_heap</td>
369
+ <td>27</td>
370
+ <td>61.322001</td>
371
+ <td>0.440</td>
372
+ </tr>
373
+ <tr>
374
+ <td>Fibonacci</td>
375
+ <td>21</td>
376
+ <td>60.334636</td>
377
+ <td>0.349(1.26x slower)</td>
378
+ </tr>
379
+ <tr>
380
+ <td>lazy_priority_queue</td>
381
+ <td>20</td>
382
+ <td>61.471507</td>
383
+ <td>0.327(1.35x slower)</td>
209
384
  </tr>
210
385
  </table>
211
386
 
212
387
  ### Stress test with changing priority(N = 10) [source code](./test/performance_with_change_priority.rb)
213
- A stress test of 200 operations: starting with 10 pushes/10 change_priorities/0 pops, following 9 pushes/9 change_priorities/1 pop, and so on till 0 pushes/0 change_priorities/10 pops.
388
+ A stress test of 165 operations: starting with 10 pushes/10 change_priorities/0 pops, following 9 pushes/9 change_priorities/1 pop, and so on till 0 pushes/0 change_priorities/10 pops.
214
389
  <table>
215
390
  <tr>
216
- <th colspan="4">ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin20]</th>
391
+ <th colspan="4">ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x86_64-darwin21]</th>
392
+ </tr>
393
+ <tr>
394
+ <th>Library</th>
395
+ <th>Iterations per second</th>
396
+ </tr>
397
+ <tr>
398
+ <td>pairing_heap</td>
399
+ <td>5914.3</td>
400
+ </tr>
401
+ <tr>
402
+ <td>lazy_priority_queue</td>
403
+ <td>4293.5(1.38x slower)</td>
404
+ </tr>
405
+ <tr>
406
+ <td>Fibonacci</td>
407
+ <td>3755.2(1.57x slower)</td>
408
+ </tr>
409
+ <tr>
410
+ <th colspan="4">ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) +YJIT [x86_64-darwin21]</th>
217
411
  </tr>
218
412
  <tr>
219
413
  <th>Library</th>
@@ -221,18 +415,18 @@ A stress test of 200 operations: starting with 10 pushes/10 change_priorities/0
221
415
  </tr>
222
416
  <tr>
223
417
  <td>pairing_heap</td>
224
- <td>5991.2</td>
418
+ <td>7082.7</td>
225
419
  </tr>
226
420
  <tr>
227
421
  <td>Fibonacci</td>
228
- <td>3803.5(1.58x slower)</td>
422
+ <td>6687.1(1.06x slower)</td>
229
423
  </tr>
230
424
  <tr>
231
425
  <td>lazy_priority_queue</td>
232
- <td>3681.9(1.64x slower)</td>
426
+ <td>5006.4(1.41x slower)</td>
233
427
  </tr>
234
428
  <tr>
235
- <th colspan="4">jruby 9.2.14.0 (2.5.7) 2020-12-08 ebe64bafb9 OpenJDK 64-Bit Server VM 15.0.2+7 on 15.0.2+7 +jit [darwin-x86_64]</th>
429
+ <th colspan="4">jruby 9.3.3.0 (2.6.8) 2022-01-19 b26de1f5c5 OpenJDK 64-Bit Server VM 16.0.1+9-24 on 16.0.1+9-24 +jit [darwin-x86_64]</th>
236
430
  </tr>
237
431
  <tr>
238
432
  <th>Library</th>
@@ -240,22 +434,41 @@ A stress test of 200 operations: starting with 10 pushes/10 change_priorities/0
240
434
  </tr>
241
435
  <tr>
242
436
  <td>pairing_heap</td>
243
- <td>6784.3</td>
437
+ <td>6861.6</td>
244
438
  </tr>
245
439
  <tr>
246
440
  <td>lazy_priority_queue</td>
247
- <td>6044.5(1.12x slower)</td>
441
+ <td>6446.4(1.06x slower)</td>
248
442
  </tr>
249
443
  <tr>
250
444
  <td>Fibonacci</td>
251
- <td>4070.5(1.67x slower)</td>
445
+ <td>4365.4(1.57x slower)</td>
446
+ </tr>
447
+ <tr>
448
+ <th colspan="4">jruby 9.3.3.0 (2.6.8) 2022-01-19 b26de1f5c5 OpenJDK 64-Bit Server VM 16.0.1+9-24 on 16.0.1+9-24 +indy +jit [darwin-x86_64]</th>
449
+ </tr>
450
+ <tr>
451
+ <th>Library</th>
452
+ <th>Iterations per second</th>
453
+ </tr>
454
+ <tr>
455
+ <td>pairing_heap</td>
456
+ <td>14032</td>
457
+ </tr>
458
+ <tr>
459
+ <td>Fibonacci</td>
460
+ <td>12841(1.09x slower)</td>
461
+ </tr>
462
+ <tr>
463
+ <td>lazy_priority_queue</td>
464
+ <td>10404(1.35x slower)</td>
252
465
  </tr>
253
466
  </table>
254
467
 
255
468
  ### Dijkstra's algorithm with RGL [source code](./test/performance_rgl.rb)
256
469
  <table>
257
470
  <tr>
258
- <th colspan="4">ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin20]</th>
471
+ <th colspan="4">ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x86_64-darwin21]</th>
259
472
  </tr>
260
473
  <tr>
261
474
  <th>Library</th>
@@ -265,24 +478,24 @@ A stress test of 200 operations: starting with 10 pushes/10 change_priorities/0
265
478
  </tr>
266
479
  <tr>
267
480
  <td>pairing_heap</td>
268
- <td>7</td>
269
- <td>64.768526</td>
270
- <td>0.108</td>
481
+ <td>9</td>
482
+ <td>64.505899</td>
483
+ <td>0.140</td>
271
484
  </tr>
272
485
  <tr>
273
486
  <td>lazy_priority_queue</td>
274
- <td>6</td>
275
- <td>63.278091</td>
276
- <td>0.095(1.14x slower)</td>
487
+ <td>8</td>
488
+ <td>63.970577</td>
489
+ <td>0.125(1.12x slower)</td>
277
490
  </tr>
278
491
  <tr>
279
492
  <td>Fibonacci</td>
280
- <td>6</td>
281
- <td>65.898081</td>
282
- <td>0.091(1.19x slower)</td>
493
+ <td>7</td>
494
+ <td>62.573724</td>
495
+ <td>0.112(1.25x slower)</td>
283
496
  </tr>
284
497
  <tr>
285
- <th colspan="4">jruby 9.2.14.0 (2.5.7) 2020-12-08 ebe64bafb9 OpenJDK 64-Bit Server VM 15.0.2+7 on 15.0.2+7 +jit [darwin-x86_64]</th>
498
+ <th colspan="4">ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) +YJIT [x86_64-darwin21]</th>
286
499
  </tr>
287
500
  <tr>
288
501
  <th>Library</th>
@@ -292,28 +505,83 @@ A stress test of 200 operations: starting with 10 pushes/10 change_priorities/0
292
505
  </tr>
293
506
  <tr>
294
507
  <td>pairing_heap</td>
295
- <td>12</td>
296
- <td>60.277567</td>
297
- <td>0.199</td>
508
+ <td>9</td>
509
+ <td>63.567801</td>
510
+ <td>0.142</td>
511
+ </tr>
512
+ <tr>
513
+ <td>Fibonacci</td>
514
+ <td>9</td>
515
+ <td>64.575079</td>
516
+ <td>0.140(1.02x slower)</td>
298
517
  </tr>
299
518
  <tr>
300
519
  <td>lazy_priority_queue</td>
301
- <td>12</td>
302
- <td>61.238395</td>
303
- <td>0.196(1.02x slower)</td>
520
+ <td>8</td>
521
+ <td>60.123700</td>
522
+ <td>0.133(1.06x slower)</td>
523
+ </tr>
524
+ <tr>
525
+ <th colspan="4">jruby 9.3.3.0 (2.6.8) 2022-01-19 b26de1f5c5 OpenJDK 64-Bit Server VM 16.0.1+9-24 on 16.0.1+9-24 +jit [darwin-x86_64]</th>
526
+ </tr>
527
+ <tr>
528
+ <th>Library</th>
529
+ <th>Iterations</th>
530
+ <th>Seconds</th>
531
+ <th>Iterations per second</th>
532
+ </tr>
533
+ <tr>
534
+ <td>pairing_heap</td>
535
+ <td>14</td>
536
+ <td>64.124373</td>
537
+ <td>0.218</td>
538
+ </tr>
539
+ <tr>
540
+ <td>lazy_priority_queue</td>
541
+ <td>13</td>
542
+ <td>61.147807</td>
543
+ <td>0.213(1.03x slower)</td>
304
544
  </tr>
305
545
  <tr>
306
546
  <td>Fibonacci</td>
307
547
  <td>10</td>
308
- <td>62.687378</td>
309
- <td>0.160(1.25x slower)</td>
548
+ <td>64.250067</td>
549
+ <td>0.156(1.40x slower)</td>
550
+ </tr>
551
+ <tr>
552
+ <th colspan="4">jruby 9.3.3.0 (2.6.8) 2022-01-19 b26de1f5c5 OpenJDK 64-Bit Server VM 16.0.1+9-24 on 16.0.1+9-24 +indy +jit [darwin-x86_64]</th>
553
+ </tr>
554
+ <tr>
555
+ <th>Library</th>
556
+ <th>Iterations</th>
557
+ <th>Seconds</th>
558
+ <th>Iterations per second</th>
559
+ </tr>
560
+ <tr>
561
+ <td>pairing_heap</td>
562
+ <td>22</td>
563
+ <td>61.450341</td>
564
+ <td>0.361</td>
565
+ </tr>
566
+ <tr>
567
+ <td>Fibonacci</td>
568
+ <td>18</td>
569
+ <td>61.618204</td>
570
+ <td>0.296(1.22x slower)</td>
571
+ </tr>
572
+ <tr>
573
+ <td>lazy_priority_queue</td>
574
+ <td>17</td>
575
+ <td>60.156184</td>
576
+ <td>0.283(1.27x slower)</td>
310
577
  </tr>
311
578
  </table>
312
579
 
313
580
  ### Simple Dijkstra's algorithm implementation [source code](./test/performance_dijkstra.rb)
581
+ Heaps that support change_priority operation use it. Heaps that do not support it use dijkstra implementation that do not rely on change_priority instead and do additional pops and pushes instead(see Dijkstra-NoDec from [Priority Queues and Dijkstra’s Algorithm](https://www3.cs.stonybrook.edu/~rezaul/papers/TR-07-54.pdf)).
314
582
  <table>
315
583
  <tr>
316
- <th colspan="4">ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin20]</th>
584
+ <th colspan="4">ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x86_64-darwin21]</th>
317
585
  </tr>
318
586
  <tr>
319
587
  <th>Library</th>
@@ -322,25 +590,76 @@ A stress test of 200 operations: starting with 10 pushes/10 change_priorities/0
322
590
  <th>Iterations per second</th>
323
591
  </tr>
324
592
  <tr>
325
- <td>pairing_heap</td>
326
- <td>20</td>
327
- <td>60.028380</td>
328
- <td>0.334</td>
593
+ <td>pairing_heap (SimplePairingHeap)</td>
594
+ <td>25</td>
595
+ <td>61.386477</td>
596
+ <td>0.407</td>
597
+ </tr>
598
+ <tr>
599
+ <td>pairing_heap (PairingHeap)</td>
600
+ <td>22</td>
601
+ <td>62.044470</td>
602
+ <td>0.355(1.15x slower)</td>
603
+ </tr>
604
+ <tr>
605
+ <td>rb_heap</td>
606
+ <td>13</td>
607
+ <td>60.717112</td>
608
+ <td>0.214(1.90x slower)</td>
609
+ </tr>
610
+ <tr>
611
+ <td>lazy_priority_queue</td>
612
+ <td>10</td>
613
+ <td>61.730614</td>
614
+ <td>0.162(2.51x slower)</td>
329
615
  </tr>
330
616
  <tr>
331
617
  <td>Fibonacci</td>
332
618
  <td>10</td>
333
- <td>64.471303</td>
334
- <td>0.155(2.14x slower)</td>
619
+ <td>65.899982</td>
620
+ <td>0.152(2.68x slower)</td>
621
+ </tr>
622
+ <tr>
623
+ <th colspan="4">ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) +YJIT [x86_64-darwin21]</th>
624
+ </tr>
625
+ <tr>
626
+ <th>Library</th>
627
+ <th>Iterations</th>
628
+ <th>Seconds</th>
629
+ <th>Iterations per second</th>
630
+ </tr>
631
+ <tr>
632
+ <td>pairing_heap (SimplePairingHeap)</td>
633
+ <td>29</td>
634
+ <td>61.656995</td>
635
+ <td>0.471</td>
636
+ </tr>
637
+ <tr>
638
+ <td>pairing_heap (PairingHeap)</td>
639
+ <td>24</td>
640
+ <td>61.813482</td>
641
+ <td>0.389(1.21x slower)</td>
642
+ </tr>
643
+ <tr>
644
+ <td>rb_heap</td>
645
+ <td>19</td>
646
+ <td>62.191040</td>
647
+ <td>0.306(1.54x slower)</td>
648
+ </tr>
649
+ <tr>
650
+ <td>Fibonacci</td>
651
+ <td>18</td>
652
+ <td>60.062072</td>
653
+ <td>0.300(1.57x slower)</td>
335
654
  </tr>
336
655
  <tr>
337
656
  <td>lazy_priority_queue</td>
338
- <td>9</td>
339
- <td>65.986618</td>
340
- <td>0.136(2.45x slower)</td>
657
+ <td>12</td>
658
+ <td>60.860292</td>
659
+ <td>0.197(2.38x slower)</td>
341
660
  </tr>
342
661
  <tr>
343
- <th colspan="4">jruby 9.2.14.0 (2.5.7) 2020-12-08 ebe64bafb9 OpenJDK 64-Bit Server VM 15.0.2+7 on 15.0.2+7 +jit [darwin-x86_64]</th>
662
+ <th colspan="4">jruby 9.3.3.0 (2.6.8) 2022-01-19 b26de1f5c5 OpenJDK 64-Bit Server VM 16.0.1+9-24 on 16.0.1+9-24 +jit [darwin-x86_64]</th>
344
663
  </tr>
345
664
  <tr>
346
665
  <th>Library</th>
@@ -349,29 +668,100 @@ A stress test of 200 operations: starting with 10 pushes/10 change_priorities/0
349
668
  <th>Iterations per second</th>
350
669
  </tr>
351
670
  <tr>
352
- <td>pairing_heap</td>
353
- <td>21</td>
354
- <td>61.727259</td>
355
- <td>0.340</td>
671
+ <td>pairing_heap (SimplePairingHeap)</td>
672
+ <td>24</td>
673
+ <td>61.972936</td>
674
+ <td>0.387</td>
675
+ </tr>
676
+ <tr>
677
+ <td>pairing_heap (PairingHeap)</td>
678
+ <td>20</td>
679
+ <td>62.178839</td>
680
+ <td>0.322(1.20x slower)</td>
356
681
  </tr>
357
682
  <tr>
358
683
  <td>lazy_priority_queue</td>
359
684
  <td>14</td>
360
- <td>63.436863</td>
361
- <td>0.221(1.54x slower)</td>
685
+ <td>61.540058s</td>
686
+ <td>0.228(1.70x slower)</td>
687
+ </tr>
688
+ <tr>
689
+ <td>rb_heap</td>
690
+ <td>14</td>
691
+ <td>62.125831</td>
692
+ <td>0.225(1.72x slower)</td>
362
693
  </tr>
363
694
  <tr>
364
695
  <td>Fibonacci</td>
365
696
  <td>10</td>
366
- <td>62.447662</td>
367
- <td>0.160(2.12x slower)</td>
697
+ <td>62.319669</td>
698
+ <td>0.155(2.41x slower)</td>
699
+ </tr>
700
+ <tr>
701
+ <th colspan="4">jruby 9.3.3.0 (2.6.8) 2022-01-19 b26de1f5c5 OpenJDK 64-Bit Server VM 16.0.1+9-24 on 16.0.1+9-24 +indy +jit [darwin-x86_64]</th>
702
+ </tr>
703
+ <tr>
704
+ <th>Library</th>
705
+ <th>Iterations</th>
706
+ <th>Seconds</th>
707
+ <th>Iterations per second</th>
708
+ </tr>
709
+ <tr>
710
+ <td>pairing_heap (SimplePairingHeap)</td>
711
+ <td>47</td>
712
+ <td>61.192519</td>
713
+ <td>0.770</td>
714
+ </tr>
715
+ <tr>
716
+ <td>rb_heap</td>
717
+ <td>39</td>
718
+ <td>61.028398</td>
719
+ <td>0.639(1.20x slower)</td>
720
+ </tr>
721
+ <tr>
722
+ <td>pairing_heap (PairingHeap)</td>
723
+ <td>36</td>
724
+ <td>60.035760</td>
725
+ <td>0.601(1.28x slower)</td>
726
+ </tr>
727
+ <tr>
728
+ <td>Fibonacci</td>
729
+ <td>28</td>
730
+ <td>61.599202</td>
731
+ <td>0.456(1.69x slower)</td>
732
+ </tr>
733
+ <tr>
734
+ <td>lazy_priority_queue</td>
735
+ <td>22</td>
736
+ <td>60.540367</td>
737
+ <td>0.364(2.12x slower)</td>
368
738
  </tr>
369
739
  </table>
370
740
 
371
741
  ### Summary
742
+ #### Change priority required
372
743
  <table>
373
744
  <tr>
374
- <th colspan="4">ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin20]</th>
745
+ <th colspan="4">ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x86_64-darwin21]</th>
746
+ </tr>
747
+ <tr>
748
+ <th>Library</th>
749
+ <th>Slower geometric mean</th>
750
+ </tr>
751
+ <tr>
752
+ <td>pairing_heap</td>
753
+ <td>1</td>
754
+ </tr>
755
+ <tr>
756
+ <td>lazy_priority_queue</td>
757
+ <td>1.523x slower</td>
758
+ </tr>
759
+ <tr>
760
+ <td>Fibonacci</td>
761
+ <td>1.751x slower</td>
762
+ </tr>
763
+ <tr>
764
+ <th colspan="4">ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) +YJIT [x86_64-darwin21]</th>
375
765
  </tr>
376
766
  <tr>
377
767
  <th>Library</th>
@@ -383,14 +773,14 @@ A stress test of 200 operations: starting with 10 pushes/10 change_priorities/0
383
773
  </tr>
384
774
  <tr>
385
775
  <td>Fibonacci</td>
386
- <td>1.720x slower</td>
776
+ <td>1.146x slower</td>
387
777
  </tr>
388
778
  <tr>
389
779
  <td>lazy_priority_queue</td>
390
- <td>1.721x slower</td>
780
+ <td>1.482x slower</td>
391
781
  </tr>
392
782
  <tr>
393
- <th colspan="4">jruby 9.2.14.0 (2.5.7) 2020-12-08 ebe64bafb9 OpenJDK 64-Bit Server VM 15.0.2+7 on 15.0.2+7 +jit [darwin-x86_64]</th>
783
+ <th colspan="4">jruby 9.3.3.0 (2.6.8) 2022-01-19 b26de1f5c5 OpenJDK 64-Bit Server VM 16.0.1+9-24 on 16.0.1+9-24 +jit [darwin-x86_64]</th>
394
784
  </tr>
395
785
  <tr>
396
786
  <th>Library</th>
@@ -402,15 +792,145 @@ A stress test of 200 operations: starting with 10 pushes/10 change_priorities/0
402
792
  </tr>
403
793
  <tr>
404
794
  <td>lazy_priority_queue</td>
405
- <td>1.23x slower</td>
795
+ <td>1.153x slower</td>
406
796
 
407
797
  </tr>
408
798
  <tr>
409
799
  <td>Fibonacci</td>
410
- <td>1.78x slower</td>
800
+ <td>1.793x slower</td>
801
+ </tr>
802
+ <tr>
803
+ <th colspan="4">jruby 9.3.3.0 (2.6.8) 2022-01-19 b26de1f5c5 OpenJDK 64-Bit Server VM 16.0.1+9-24 on 16.0.1+9-24 +indy +jit [darwin-x86_64]</th>
804
+ </tr>
805
+ <tr>
806
+ <th>Library</th>
807
+ <th>Slower geometric mean</th>
808
+ </tr>
809
+ <tr>
810
+ <td>pairing_heap</td>
811
+ <td>1</td>
812
+ </tr>
813
+ <tr>
814
+ <td>Fibonacci</td>
815
+ <td>1.222x slower</td>
816
+ </tr>
817
+ <tr>
818
+ <td>lazy_priority_queue</td>
819
+ <td>1.394x slower</td>
411
820
  </tr>
412
821
  </table>
413
822
 
823
+ #### Change priority not required
824
+ <table>
825
+ <tr>
826
+ <th colspan="4">ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x86_64-darwin21]</th>
827
+ </tr>
828
+ <tr>
829
+ <th>Library</th>
830
+ <th>Slower geometric mean</th>
831
+ </tr>
832
+ <tr>
833
+ <td>pairing_heap (SimplePairingHeap)</td>
834
+ <td>1</td>
835
+ </tr>
836
+ <tr>
837
+ <td>pairing_heap (PairingHeap)</td>
838
+ <td>1.209</td>
839
+ </tr>
840
+ <tr>
841
+ <td>rb_heap</td>
842
+ <td>1.954</td>
843
+ </tr>
844
+ <tr>
845
+ <td>lazy_priority_queue</td>
846
+ <td>2.235x slower</td>
847
+ </tr>
848
+ <tr>
849
+ <td>Fibonacci</td>
850
+ <td>2.588x slower</td>
851
+ </tr>
852
+ <tr>
853
+ <th colspan="4">ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) +YJIT [x86_64-darwin21]</th>
854
+ </tr>
855
+ <tr>
856
+ <th>Library</th>
857
+ <th>Slower geometric mean</th>
858
+ </tr>
859
+ <tr>
860
+ <td>pairing_heap (SimplePairingHeap)</td>
861
+ <td>1</td>
862
+ </tr>
863
+ <tr>
864
+ <td>pairing_heap (PairingHeap)</td>
865
+ <td>1.273x slower</td>
866
+ </tr>
867
+ <tr>
868
+ <td>Fibonacci</td>
869
+ <td>1.590x slower</td>
870
+ </tr>
871
+ <tr>
872
+ <td>rb_heap</td>
873
+ <td>1.666x slower</td>
874
+ </tr>
875
+ <tr>
876
+ <td>lazy_priority_queue</td>
877
+ <td>2.230x slower</td>
878
+ </tr>
879
+ <tr>
880
+ <th colspan="4">jruby 9.3.3.0 (2.6.8) 2022-01-19 b26de1f5c5 OpenJDK 64-Bit Server VM 16.0.1+9-24 on 16.0.1+9-24 +jit [darwin-x86_64]</th>
881
+ </tr>
882
+ <tr>
883
+ <th>Library</th>
884
+ <th>Slower geometric mean</th>
885
+ </tr>
886
+ <tr>
887
+ <td>pairing_heap (SimplePairingHeap)</td>
888
+ <td>1</td>
889
+ </tr>
890
+ <tr>
891
+ <td>pairing_heap (PairingHeap)</td>
892
+ <td>1.296</td>
893
+ </tr>
894
+ <tr>
895
+ <td>lazy_priority_queue</td>
896
+ <td>1.607x slower</td>
897
+ </tr>
898
+ <tr>
899
+ <td>rb_heap</td>
900
+ <td>1.710</td>
901
+ </tr>
902
+ <tr>
903
+ <td>Fibonacci</td>
904
+ <td>2.452x slower</td>
905
+ </tr>
906
+ <tr>
907
+ <th colspan="4">jruby 9.3.3.0 (2.6.8) 2022-01-19 b26de1f5c5 OpenJDK 64-Bit Server VM 16.0.1+9-24 on 16.0.1+9-24 +indy +jit [darwin-x86_64]</th>
908
+ </tr>
909
+ <tr>
910
+ <th>Library</th>
911
+ <th>Slower geometric mean</th>
912
+ </tr>
913
+ <tr>
914
+ <td>pairing_heap (SimplePairingHeap)</td>
915
+ <td>1</td>
916
+ </tr>
917
+ <tr>
918
+ <td>pairing_heap (PairingHeap)</td>
919
+ <td>1.353x slower</td>
920
+ </tr>
921
+ <tr>
922
+ <td>rb_heap</td>
923
+ <td>1.522x slower</td>
924
+ </tr>
925
+ <tr>
926
+ <td>Fibonacci</td>
927
+ <td>1.730x slower</td>
928
+ </tr>
929
+ <tr>
930
+ <td>lazy_priority_queue</td>
931
+ <td>2.044x slower</td>
932
+ </tr>
933
+ </table>
414
934
  ## Development
415
935
 
416
936
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PairingHeap
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/pairing_heap.rb CHANGED
@@ -1,6 +1,43 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PairingHeap
4
+ module MergePairs
5
+ # Non-recursive implementation of method described in https://en.wikipedia.org/wiki/Pairing_heap#delete-min
6
+ def merge_pairs(heaps)
7
+ return nil if heaps.nil?
8
+ return heaps if heaps.next_sibling.nil?
9
+
10
+ # [H1, H2, H3, H4, H5, H6, H7] => [H1H2, H3H4, H5H6, H7]
11
+ stack = []
12
+ current = heaps
13
+ while current
14
+ prev = current
15
+ current = current.next_sibling
16
+ unless current
17
+ stack << prev
18
+ break
19
+ end
20
+ next_val = current.next_sibling
21
+ stack << meld(prev, current)
22
+ current = next_val
23
+ end
24
+
25
+ # [H1H2, H3H4, H5H6, H7]
26
+ # [H1H2, H3H4, H5H67]
27
+ # [H1H2, H3H45H67]
28
+ # [H1H2H3H45H67]
29
+ # return H1H2H3H45H67
30
+ while true
31
+ right = stack.pop
32
+ return right if stack.empty?
33
+
34
+ left = stack.pop
35
+ stack << meld(left, right)
36
+ end
37
+ end
38
+ end
39
+ private_constant :MergePairs
40
+
4
41
  # Pairing heap data structure implementation
5
42
  # @see https://en.wikipedia.org/wiki/Pairing_heap
6
43
  class PairingHeap
@@ -14,6 +51,18 @@ module PairingHeap
14
51
  @prev_sibling = prev_sibling
15
52
  @next_sibling = next_sibling
16
53
  end
54
+
55
+ def remove_from_parents_list!
56
+ if self.prev_sibling
57
+ self.prev_sibling.next_sibling = self.next_sibling
58
+ self.next_sibling.prev_sibling = self.prev_sibling if self.next_sibling
59
+ elsif self.parent.subheaps.equal?(self)
60
+ self.parent.subheaps = self.next_sibling
61
+ self.next_sibling.prev_sibling = nil if self.next_sibling
62
+ end
63
+ self.prev_sibling = nil
64
+ self.next_sibling = nil
65
+ end
17
66
  end
18
67
  private_constant :Node
19
68
 
@@ -46,6 +95,10 @@ module PairingHeap
46
95
  @root&.elem
47
96
  end
48
97
 
98
+ def peek_priority
99
+ [@root&.elem, @root&.priority]
100
+ end
101
+
49
102
  # Time Complexity: O(1)
50
103
  # @return [Boolean]
51
104
  def empty?
@@ -85,6 +138,12 @@ module PairingHeap
85
138
  end
86
139
  alias dequeue pop
87
140
 
141
+ def pop_priority
142
+ node = @root
143
+ pop
144
+ [node.elem, node.priority]
145
+ end
146
+
88
147
  # Changes a priority of element to a more prioritary one
89
148
  # Time Complexity: O(1)
90
149
  # Amortized Time Complexity: o(log(N))
@@ -103,13 +162,13 @@ module PairingHeap
103
162
  return if node.parent.nil?
104
163
  return if @order[node.parent.priority, node.priority]
105
164
 
106
- remove_from_parents_list(node)
165
+ node.remove_from_parents_list!
107
166
  @root = meld(node, @root)
108
167
  @root.parent = nil
109
168
  self
110
169
  end
111
170
 
112
- # Removes element from the top of the heap
171
+ # Removes element from the heap
113
172
  # Time Complexity: O(N)
114
173
  # Amortized Time Complexity: O(log(N))
115
174
  # @raise [ArgumentError] if the element heap is not in heap
@@ -122,7 +181,7 @@ module PairingHeap
122
181
  if node.parent.nil?
123
182
  @root = merge_pairs(node.subheaps)
124
183
  else
125
- remove_from_parents_list(node)
184
+ node.remove_from_parents_list!
126
185
  new_heap = merge_pairs(node.subheaps)
127
186
  if new_heap
128
187
  new_heap.prev_sibling = nil
@@ -135,20 +194,7 @@ module PairingHeap
135
194
  end
136
195
 
137
196
  private
138
-
139
- def remove_from_parents_list(node)
140
- if node.prev_sibling
141
- node.prev_sibling.next_sibling = node.next_sibling
142
- node.next_sibling.prev_sibling = node.prev_sibling if node.next_sibling
143
- elsif node.parent.subheaps.equal?(node)
144
- node.parent.subheaps = node.next_sibling
145
- node.next_sibling.prev_sibling = nil if node.next_sibling
146
- elsif node.next_sibling
147
- node.next_sibling.prev_sibling = nil
148
- end
149
- node.prev_sibling = nil
150
- node.next_sibling = nil
151
- end
197
+ include MergePairs
152
198
 
153
199
  def meld(left, right)
154
200
  return right if left.nil?
@@ -168,42 +214,118 @@ module PairingHeap
168
214
  child.parent = parent
169
215
  parent
170
216
  end
217
+ end
171
218
 
172
- # Non-recursive implementation of method described in https://en.wikipedia.org/wiki/Pairing_heap#delete-min
173
- def merge_pairs(heaps)
174
- return nil if heaps.nil?
175
- return heaps if heaps.next_sibling.nil?
219
+ class SimplePairingHeap
220
+ class Node
221
+ attr_accessor :elem, :priority, :subheaps, :parent, :next_sibling
222
+ def initialize(elem, priority, subheaps, parent, next_sibling)
223
+ @elem = elem
224
+ @priority = priority
225
+ @subheaps = subheaps
226
+ @parent = parent
227
+ @next_sibling = next_sibling
228
+ end
229
+ end
230
+ private_constant :Node
176
231
 
177
- # [H1, H2, H3, H4, H5, H6, H7] => [H1H2, H3H4, H5H6, H7]
178
- stack = []
179
- current = heaps
180
- while current
181
- prev = current
182
- current = current.next_sibling
183
- unless current
184
- stack << prev
185
- break
186
- end
187
- next_val = current.next_sibling
188
- stack << meld(prev, current)
189
- current = next_val
232
+ # @param &block Optional heap property priority comparator. `<:=.to_proc` by default
233
+ def initialize(&block)
234
+ @root = nil
235
+ @order = block || :<=.to_proc
236
+ @size = 0
237
+ end
238
+
239
+ # Pushes element to the heap.
240
+ # Time Complexity: O(1)
241
+ # @param elem Element to be pushed
242
+ # @param priority Priority of the element
243
+ # @raise [ArgumentError] if the element is already in the heap
244
+ # @return [PairingHeap]
245
+ def push(elem, priority)
246
+ node = Node.new(elem, priority, nil, nil, nil)
247
+ @root = meld(@root, node)
248
+ @size += 1
249
+ self
250
+ end
251
+ alias enqueue push
252
+
253
+ # Returns the element at the top of the heap
254
+ # Time Complexity: O(1)
255
+ def peek
256
+ @root&.elem
257
+ end
258
+
259
+ def peek_priority
260
+ [@root&.elem, @root&.priority]
261
+ end
262
+
263
+ # Time Complexity: O(1)
264
+ # @return [Boolean]
265
+ def empty?
266
+ @root.nil?
267
+ end
268
+
269
+ # Time Complexity: O(1)
270
+ # @return [Boolean]
271
+ def any?
272
+ !@root.nil?
273
+ end
274
+
275
+ # Time Complexity: O(1)
276
+ # @return [Integer]
277
+ def size
278
+ @size
279
+ end
280
+ alias length size
281
+
282
+ # Removes element from the top of the heap
283
+ # Time Complexity: O(N)
284
+ # Amortized time Complexity: O(log(N))
285
+ # @raise [ArgumEntError] if the heap is empty
286
+ # @return [PairingHeap]
287
+ def pop
288
+ raise ArgumentError, "Cannot remove from an empty heap" if @root.nil?
289
+ @size -= 1
290
+
291
+ elem = @root.elem
292
+ @root = merge_pairs(@root.subheaps)
293
+ if @root
294
+ @root.parent = nil
295
+ @root.next_sibling = nil
190
296
  end
297
+ elem
298
+ end
299
+ alias dequeue pop
191
300
 
192
- # [H1H2, H3H4, H5H6, H7]
193
- # [H1H2, H3H4, H5H67]
194
- # [H1H2, H3H45H67]
195
- # [H1H2H3H45H67]
196
- # return H1H2H3H45H67
197
- while true
198
- right = stack.pop
199
- return right if stack.empty?
301
+ def pop_priority
302
+ node = @root
303
+ pop
304
+ [node.elem, node.priority]
305
+ end
200
306
 
201
- left = stack.pop
202
- stack << meld(left, right)
307
+ private
308
+ include MergePairs
309
+
310
+ def meld(left, right)
311
+ return right if left.nil?
312
+ return left if right.nil?
313
+
314
+ if @order[left.priority, right.priority]
315
+ parent = left
316
+ child = right
317
+ else
318
+ parent = right
319
+ child = left
203
320
  end
321
+ child.next_sibling = parent.subheaps
322
+ parent.subheaps = child
323
+ child.parent = parent
324
+ parent
204
325
  end
205
326
  end
206
327
 
328
+
207
329
  # Priority queue where the smallest priority is the most prioritary
208
330
  class MinPriorityQueue < PairingHeap
209
331
  def initialize
data/pairing_heap.gemspec CHANGED
@@ -8,14 +8,15 @@ Gem::Specification.new do |spec|
8
8
  spec.authors = ["Marcin Henryk Bartkowiak"]
9
9
  spec.email = ["mhbartkowiak@gmail.com"]
10
10
 
11
- spec.summary = "Performant priority queue in pure Ruby with support for changing priority"
12
- spec.description = "Performant priority queue in pure Ruby with support for changing priority using pairing heap data structure"
11
+ spec.summary = "Performant priority queue in pure ruby with support for changing priority"
12
+ spec.description = "Performant priority queue in pure ruby with support for changing priority using pairing heap data structure"
13
13
  spec.homepage = "https://github.com/mhib/pairing_heap"
14
14
  spec.license = "MIT"
15
15
  spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
16
16
 
17
- spec.metadata["homepage_uri"] = spec.homepage
18
- spec.metadata["source_code_uri"] = spec.homepage
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = spec.homepage
19
+ spec.metadata["documentation_uri"] = "https://rubydoc.info/gems/pairing_heap"
19
20
 
20
21
  # Specify which files should be added to the gem when it is released.
21
22
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pairing_heap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marcin Henryk Bartkowiak
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-02-19 00:00:00.000000000 Z
11
+ date: 2022-02-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -38,7 +38,7 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '13.0'
41
- description: Performant priority queue in pure Ruby with support for changing priority
41
+ description: Performant priority queue in pure ruby with support for changing priority
42
42
  using pairing heap data structure
43
43
  email:
44
44
  - mhbartkowiak@gmail.com
@@ -65,6 +65,7 @@ licenses:
65
65
  metadata:
66
66
  homepage_uri: https://github.com/mhib/pairing_heap
67
67
  source_code_uri: https://github.com/mhib/pairing_heap
68
+ documentation_uri: https://rubydoc.info/gems/pairing_heap
68
69
  post_install_message:
69
70
  rdoc_options: []
70
71
  require_paths:
@@ -80,8 +81,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
80
81
  - !ruby/object:Gem::Version
81
82
  version: '0'
82
83
  requirements: []
83
- rubygems_version: 3.2.3
84
+ rubygems_version: 3.3.3
84
85
  signing_key:
85
86
  specification_version: 4
86
- summary: Performant priority queue in pure Ruby with support for changing priority
87
+ summary: Performant priority queue in pure ruby with support for changing priority
87
88
  test_files: []