What you call "assignments" are sections with a fixed number of parts of the same size. Well, basically. You did not indicate what should happen if (# groups) * (the size of each group) is smaller or larger than the size of your population.
Creating any possible section in a non-specific order is not too complicated, but it is only suitable for small populations or for filtering and finding any section that meets some independent criteria. If you need to optimize or minimize something, you will end up looking at the entire set of sections, which may not be possible.
Based on a description of your actual problem, you want to read local search and optimization , of which the above simulated annealing is one such technique.
With all of the above, here is a simple recursive Python function that generates sections of a fixed length with equal sizes of parts in a specific order. This is a specialization of my answer to a similar section problem, and this answer itself is a specialization of this answer . This should be pretty easy to translate into JavaScript (with ES6 generators).
def special_partitions(population, num_groups, group_size): """Yields all partitions with a fixed number of equally sized parts. Each yielded partition is a list of length `num_groups`, and each part a tuple of length `group_size. """ assert len(population) == num_groups * group_size groups = []
On execution, this prints:
[('A', 'B'), ('C', 'D')] [('A', 'C'), ('B', 'D')] [('A', 'D'), ('B', 'C')] [('A', 'B', 'C'), ('D', 'E', 'F')] [('A', 'B', 'D'), ('C', 'E', 'F')] [('A', 'B', 'E'), ('C', 'D', 'F')] [('A', 'B', 'F'), ('C', 'D', 'E')] [('A', 'C', 'D'), ('B', 'E', 'F')] [('A', 'C', 'E'), ('B', 'D', 'F')] [('A', 'C', 'F'), ('B', 'D', 'E')] [('A', 'D', 'E'), ('B', 'C', 'F')] [('A', 'D', 'F'), ('B', 'C', 'E')] [('A', 'E', 'F'), ('B', 'C', 'D')]