Skip to content

Change EnumeratorOfCombinations(mset) to make it equal to Combinations(mset) - #6797

Open
fingolfin wants to merge 2 commits into
masterfrom
mh-ai/combinations-enumerator-sorted
Open

fingolfin wants to merge 2 commits into
masterfrom
mh-ai/combinations-enumerator-sorted

Conversation

@fingolfin

Copy link
Copy Markdown
Member

EnumeratorOfCombinations( mset ) numbered the combinations by their multiplicities in mixed radix, an order that matched neither Combinations( mset ) nor EnumeratorOfCombinations( mset, k ). It now lists them in the lexicographic order of Combinations( mset ) and lies in the same family, so the two compare equal. Like the enumerator with k, it is known to be strictly sorted, prints as a call, and lets dictionaries hash lists of small integers by content. Position returns fail for unsorted lists, which it used to accept.

Ranking and unranking use the number of sub-multisets of each suffix of Set( mset ). Sets and Position get faster; A[i] for a multiset gets slower, most with many distinct elements, as each step divides by such a count rather than by a small radix.

gap> s:=EnumeratorOfCombinations([1..18]);;
gap> m:=EnumeratorOfCombinations(Concatenation([1..9],[1..9],[1..9]));;
gap> M:=EnumeratorOfCombinations(Concatenation([1..1000],[1..1000]));;

Before:

gap> l:=List([1..Length(s)],i->s[i]);; time;
653
gap> List(l,c->Position(s,c));; time;
899
gap> l:=List([1..Length(m)],i->m[i]);; time;
476
gap> List(l,c->Position(m,c));; time;
765
gap> l:=List([1..1000],i->M[QuoInt(i*Length(M),1000)]);; time;
299
gap> List(l,c->Position(M,c));; time;
299

After:

gap> l:=List([1..Length(s)],i->s[i]);; time;
345
gap> List(l,c->Position(s,c));; time;
258
gap> l:=List([1..Length(m)],i->m[i]);; time;
505
gap> List(l,c->Position(m,c));; time;
454
gap> l:=List([1..1000],i->M[QuoInt(i*Length(M),1000)]);; time;
557
gap> List(l,c->Position(M,c));; time;
192

Assisted-by: Generative AI

EnumeratorOfCombinations( mset ) numbered the combinations by their
multiplicities in mixed radix, an order that matched neither
Combinations( mset ) nor EnumeratorOfCombinations( mset, k ). It now
lists them in the lexicographic order of Combinations( mset ) and lies
in the same family, so the two compare equal. Like the enumerator with
k, it is known to be strictly sorted, prints as a call, and lets
dictionaries hash lists of small integers by content. Position returns
fail for unsorted lists, which it used to accept. Suggested by Reinis
Cirpons.

Ranking and unranking use the number of sub-multisets of each suffix of
Set( mset ). Sets and Position get faster; A[i] for a multiset gets
slower, most with many distinct elements, as each step divides by such
a count rather than by a small radix.

    gap> s:=EnumeratorOfCombinations([1..18]);;
    gap> m:=EnumeratorOfCombinations(Concatenation([1..9],[1..9],[1..9]));;
    gap> M:=EnumeratorOfCombinations(Concatenation([1..1000],[1..1000]));;

Before:

    gap> l:=List([1..Length(s)],i->s[i]);; time;
    653
    gap> List(l,c->Position(s,c));; time;
    899
    gap> l:=List([1..Length(m)],i->m[i]);; time;
    476
    gap> List(l,c->Position(m,c));; time;
    765
    gap> l:=List([1..1000],i->M[QuoInt(i*Length(M),1000)]);; time;
    299
    gap> List(l,c->Position(M,c));; time;
    299

After:

    gap> l:=List([1..Length(s)],i->s[i]);; time;
    345
    gap> List(l,c->Position(s,c));; time;
    258
    gap> l:=List([1..Length(m)],i->m[i]);; time;
    505
    gap> List(l,c->Position(m,c));; time;
    454
    gap> l:=List([1..1000],i->M[QuoInt(i*Length(M),1000)]);; time;
    557
    gap> List(l,c->Position(M,c));; time;
    192

Assisted-by: Generative AI
@fingolfin fingolfin added kind: enhancement Label for issues suggesting enhancements; and for pull requests implementing enhancements topic: library release notes: use title For PRs: the title of this PR is suitable for direct use in the release notes labels Oct 29, 2026
@codecov

codecov Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.81%. Comparing base (595340d) to head (aa135c0).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #6797      +/-   ##
==========================================
+ Coverage   79.80%   79.81%   +0.01%     
==========================================
  Files         684      684              
  Lines      295788   295862      +74     
  Branches     8695     8672      -23     
==========================================
+ Hits       236048   236141      +93     
+ Misses      57923    57904      -19     
  Partials     1817     1817              

☔ View full report in Codecov by Harness.
📢 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.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@james-d-mitchell james-d-mitchell left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Other than the two minor comments indicated, this looks good to me!

Comment thread lib/combinat.gd Outdated
## <P/>
## The ordering of combinations from these functions can be different and also
## different from the list returned by <Ref Func="Combinations"/>.
## for the same combinations; it is equal to the strictly sorted list

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
## for the same combinations; it is equal to the strictly sorted list
## for the same combinations; the enumerator is equal to the strictly sorted list

Comment thread lib/combinat.gi Outdated
end);

# All combinations are numbered in lexicographic order, in which a list
# comes before its extensions. Let S[i] = Product(max{[i..l]} + 1) be the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Be good to say what "max" is here Max.

Say that the enumerator, not the combinations, equals the sorted list,
and define the notation used by the ranking comment.

Assisted-by: Generative AI

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind: enhancement Label for issues suggesting enhancements; and for pull requests implementing enhancements release notes: use title For PRs: the title of this PR is suitable for direct use in the release notes topic: library

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants