pairing_heap 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []