Skip to content

Derangements #150

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 10, 2025
Merged

Derangements #150

merged 5 commits into from
May 10, 2025

Conversation

FedericoStra
Copy link
Contributor

Add a function to generate all derangements:

julia> map(join, derangements("abbac"))
4-element Vector{String}:
 "baacb"
 "bacba"
 "bcaba"
 "caabb"

Copy link

codecov bot commented Dec 15, 2023

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 97.22%. Comparing base (edb9c2c) to head (4944526).
Report is 1 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #150   +/-   ##
=======================================
  Coverage   97.22%   97.22%           
=======================================
  Files           8        8           
  Lines         828      829    +1     
=======================================
+ Hits          805      806    +1     
  Misses         23       23           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@FedericoStra FedericoStra force-pushed the derangements branch 3 times, most recently from 9d04c8e to 0a79d3e Compare May 1, 2025 16:30
@inkydragon inkydragon added the feature Indicates new feature / enhancement requests label May 2, 2025
@inkydragon inkydragon added this to the v1.1.0 milestone May 2, 2025
@FedericoStra FedericoStra force-pushed the derangements branch 2 times, most recently from 6d654f3 to ef83525 Compare May 2, 2025 14:49
@FedericoStra FedericoStra force-pushed the derangements branch 3 times, most recently from 2cfa0e2 to 3db33bb Compare May 2, 2025 15:52
Copy link
Member

@inkydragon inkydragon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this error message better?
Maybe we could tell people to use a typed vector.

julia> collect(derangements([]))
ERROR: MethodError: no method matching multiset_permutations(::Vector{Any}, ::Vector{Any}, ::Int64)
The function `multiset_permutations` exists, but no method is defined for this combination of argument types.

@FedericoStra
Copy link
Contributor Author

FedericoStra commented May 10, 2025

Can we make this error message better? Maybe we could tell people to use a typed vector.

julia> collect(derangements([]))
ERROR: MethodError: no method matching multiset_permutations(::Vector{Any}, ::Vector{Any}, ::Int64)
The function `multiset_permutations` exists, but no method is defined for this combination of argument types.
  • I fixed a type instability in multiset_permutations that allows to call it with a=[]. Now the local variable f is always a Vector{Int}. Without the type annotation ::Int Julia wasn't able to infer that sum(<iterator of booleans>) returns an Int.

  • I added also a test collect(derangements([])) == [[]].

@@ -287,7 +287,7 @@ julia> collect(multiset_permutations([1,1,2], 3))
"""
function multiset_permutations(a, t::Integer)
m = unique(collect(a))
f = [sum([c == x for c in a]) for x in m]
f = [sum(c == x for c in a)::Int for x in m]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I also removed the square brackets to turn the comprehension into a generator expression, which is ever so slightly more efficient, as can be verified by benchmarking.

Benchmark
julia> a = repeat(1:10, 3);

With comprehension:

julia> @benchmark multiset_permutations($a)
BenchmarkTools.Trial: 10000 samples with 10 evaluations per sample.
 Range (min  max):  1.284 μs   1.236 ms  ┊ GC (min  max):  0.00%  99.62%
 Time  (median):     1.350 μs              ┊ GC (median):     0.00%
 Time  (mean ± σ):   1.921 μs ± 16.533 μs  ┊ GC (mean ± σ):  12.11% ±  1.41%

  ▆█▇▄▂                   ▁   ▁▂ ▁▂▂▁▁▂▃▂▁▂▂▂▁▁▁▁▁           ▁
  █████▇▆▅▅▄▅▇▇▇▅▆▄▅▅▇▆▅▅▇██▇█████████████████████████▇▇▇▇▆▅ █
  1.28 μs      Histogram: log(frequency) by time     3.07 μs <

 Memory estimate: 3.39 KiB, allocs estimate: 55.

With generator expression:

julia> @benchmark multiset_permutations($a)
BenchmarkTools.Trial: 10000 samples with 22 evaluations per sample.
 Range (min  max):  943.545 ns  462.770 μs  ┊ GC (min  max):  0.00%  99.37%
 Time  (median):     976.227 ns               ┊ GC (median):     0.00%
 Time  (mean ± σ):     1.251 μs ±   5.907 μs  ┊ GC (mean ± σ):  13.38% ±  3.54%

  ▇█▄▁   ▁              ▁ ▂▂▁▂▁▂▁▁▁                             ▂
  ████▇▇████▇▆▅▄▃▁▁▁▁▄▆▆█████████████▇▇▆▆▆▄▄▄▄▁▄▄▄▃▅▄▃▃▁▄▄▁▃▁▁▅ █
  944 ns        Histogram: log(frequency) by time       2.54 μs <

 Memory estimate: 2.45 KiB, allocs estimate: 35.

@@ -286,7 +286,7 @@ julia> collect(multiset_permutations([1,1,2], 3))
```
"""
function multiset_permutations(a, t::Integer)
m = unique(collect(a))
m = unique(a)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This call to collect is unnecessary because unique already collects the items into a new vector.

Benchmark Benchmark improves to
julia> @benchmark multiset_permutations($a)
BenchmarkTools.Trial: 10000 samples with 27 evaluations per sample.
 Range (min  max):  930.074 ns  160.078 μs  ┊ GC (min  max):  0.00%  98.59%
 Time  (median):     956.333 ns               ┊ GC (median):     0.00%
 Time  (mean ± σ):     1.094 μs ±   3.083 μs  ┊ GC (mean ± σ):  10.17% ±  4.00%

  ▅█▇▅▃▁     ▁                                                  ▂
  ███████▇▆▇████▇▆▇▅▅▅▅▄▄▁▁▃▃▃▄▁▁▄▁▃▄▃▃▁▅▅▁▃▄▅▄▇▇▆▅▃▅▅▆▅▅▅▅▄▅▅▄ █
  930 ns        Histogram: log(frequency) by time       1.75 μs <

 Memory estimate: 2.16 KiB, allocs estimate: 33.

@inkydragon inkydragon merged commit e005ee0 into JuliaMath:master May 10, 2025
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Indicates new feature / enhancement requests
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants