-1

Given two integers n and r, I want to generate all possible combinations with the following rules:

  • There are n distinct numbers to choose from, 1, 2, ..., n;
  • Each combination should have r elements;
  • A combination may contain more than one of an element, for instance (1,2,2) is valid;
  • Order matters, i.e. (1,2,3) and (1,3,2) are considered distinct;
  • However, two combinations are considered equivalent if one is a cyclic permutation of the other; for instance, (1,2,3) and (2,3,1) are considered duplicates.

Examples:

n=3, r=2
11 distinct combinations
(1,1,1), (1,1,2), (1,1,3), (1,2,2), (1,2,3), (1,3,2), (1,3,3), (2,2,2), (2,2,3), (2,3,3) and (3,3,3)

n=2, r=4
6 distinct combinations
(1,1,1,1), (1,1,1,2), (1,1,2,2), (1,2,1,2), (1,2,2,2), (2,2,2,2)

What is the algorithm for it? And how to implement it in c++? Thank you in advance for advice.

Stef
  • 13,242
  • 2
  • 17
  • 28
  • *What is the algorithm for it? And how to implement it in c++?* Please ask a single question. Maybe remove the C++ tag and ask the algorithm question first? You can't write code for something you have no plan for. – PaulMcKenzie May 05 '22 at 16:15
  • In your n=2, r=4 example, (1,1,2,2) and (1,2,1,2) are the same combination. Right? If yes, please correct it in the description. – Bharat S May 05 '22 at 16:23
  • So, you want to find the cartesian product of {1, 2, ..., n} with itself r times, but filtering duplicates under the equivalence relation "two combinations are equivalent if one can become the other by rotating it"? – Stef May 05 '22 at 20:28

1 Answers1

1

Here is a naive solution in python:

  • Generate all combinations from the Cartesian product of {1, 2, ...,n} with itself r times;
  • Only keep one representative combination for each equivalency class; drop all other combinations that are equivalent to this representative combination.

This means we must have some way to compare combinations, and for instance, only keep the smallest combination of every equivalency class.

from itertools import product

def is_representative(comb):
    return all(comb[i:] + comb[:i] >= comb
               for i in range(1, len(comb)))

def cartesian_product_up_to_cyclic_permutations(n, r):
    return filter(is_representative,
                  product(range(n), repeat=r))

print(list(cartesian_product_up_to_cyclic_permutations(3, 3)))
# [(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 1, 1), (0, 1, 2), (0, 2, 1), (0, 2, 2), (1, 1, 1), (1, 1, 2), (1, 2, 2), (2, 2, 2)]

print(list(cartesian_product_up_to_cyclic_permutations(2, 4)))
# [(0, 0, 0, 0), (0, 0, 0, 1), (0, 0, 1, 1), (0, 1, 0, 1), (0, 1, 1, 1), (1, 1, 1, 1)]

You mentioned that you wanted to implement the algorithm in C++. The product function in the python code behaves just like a big for-loop that generates all the combinations in the Cartesian product. See this related question to implement Cartesian product in C++: Is it possible to execute n number of nested "loops(any)" where n is given?.

Stef
  • 13,242
  • 2
  • 17
  • 28
  • 1
    [More efficient version](https://tio.run/##lc9NasQwDAXgfU6hpUOzKdmUwPQiZTCuR2kFjiRkZWBOn3qSWfRn1bcSRjx/0pt/Co8vats2myxAjuYipQItKuagJpc1e9ddcAaq0VANK7InpyuGLMt7P3XQYuirMaRS9tc3ms7wBPs40RleT/u8r37LLAYExGCJPzA8D1CQj9q@P37NyRwrJY4PTFw1usR8y4VyVLRlvXOEa@AB7KdnptJuCn/ow29Jy6M/HBbuWxkqJj/Z3aJG7KFQ9fAf0jjA2G7Zti8) (especially regarding memory). – Kelly Bundy May 05 '22 at 22:58
  • @KellyBundy Much better indeed! As for runtime, `is_representative` is still quadratic. I wonder if there is some property of the representatives that can be tested in linear time. – Stef May 06 '22 at 08:25
  • Or even a way to enumerate them directly. E.g. All divisors of `r` give the possible cyclic symmetries. Or if `m – Sebastian May 06 '22 at 09:37
  • 1
    @Stef It *might* still be quadratic, but the `all` *can* stop early. In the two examples you're testing, it stops at i=1 almost half of the time. – Kelly Bundy May 06 '22 at 14:37