Optimize permutations by implementing it via multiset_permutations#186
Optimize permutations by implementing it via multiset_permutations#186inkydragon merged 5 commits intoJuliaMath:masterfrom
permutations by implementing it via multiset_permutations#186Conversation
…ations` As it currently stands, `multiset_permutations` is more efficient than `permutations`; see JuliaMath#151. We can exploit it to optimize `permutations`.
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #186 +/- ##
==========================================
- Coverage 97.22% 97.17% -0.06%
==========================================
Files 8 8
Lines 829 813 -16
==========================================
- Hits 806 790 -16
Misses 23 23 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
9ebd8fa to
995a046
Compare
|
Of course I don't wish to take any credit at all for this silly PR. All the merit goes to whoever coded the fast |
Without this fix there are issues with `OffsetArray`: `eachindex` returns an `OffsetArrays.IdOffsetRange`, which causes troubles when passed to `multiset_permutations` because `f = [sum(c == x for c in a)::Int for x in m]` becomes an `OffsetArray` instead of a Vector`.
|
I merged I then had to apply a small fix ( |
natemcintosh
left a comment
There was a problem hiding this comment.
I'm glad to see this! Speedups are always great.
|
The algorithm that I used in MultisetPremutation is adopted form http://alistairisrael.wordpress.com/2009/09/22/simple-efficient-pnk-algorithm/ 10 years ago, it was the only source/algorithm that I could find over the internet that supported multisets and in lexicographic order. I guess it must be one of the algorithms on Wikipedia. |
|
Shall we merge this? :) |
Code changes
As noted in #151,
multiset_permutationsis much faster thanpermutations, so we can exploit it to optimize the latter (this was suggested in a comment here).The code does the equivalent of
but we construct the iterator manually so that we can define
eltypeor it (otherwise, withIterators.map, type inference would deduceAny).This closes #151; it possibly closes #185 too.
Simple benchmark
Benchmark code
Before
After