So lets do some Maths. Because Maths is fun and helps us think things through.
We have two groups, A and B. We’re trying to match things such that:
- Everyone gets matched
- Noone gets matched to someone in the same group if possible, and
- Noone gets matched in a 2-chain.
Well, the first one’s pretty easy: As long as there’s more than two people in the list, we’re going to be able to match everyone.
The second one is pretty easy too, it just requires a little bit of thinking. How can I guarantee that there will be enough people in A to match with B?
You can try drawing groups A and B standing in a line, and then draw arrows between them to see how this works.
The simple answer is: If the number of people in group A equals the number of people in group B, we can do this with no problem.
If they don’t match up, how many people get matched with a member of their own group? Well, the answer to that is equal to the difference between the groups. You’ve already proven that with your example output: We have 3 “B”'s, and 2 “A”'s, so there’s a difference of 1, and there’s 1 person who has a pairing with their own group. Try it with 5 "B"s and 2 "A"s, and check my statement.
The third condition can be handled roughly the same way as before; by ensuring everyone ends up in a single, looping, chain. As long as your group has more than 2 members, this chain will have a length greater than 2.
How do we do this? Well, there’s a couple of different ways. I’m going to come up with a couple, but i’m sure others can and will do better.
First, if you want to shuffle the array, shuffle them. Separate your group members into separate arrays. A and B.
Option 1: Remerging
Interleave your arrays, so that the combined array goes ABABAB… until you get to the end. (If the arrays arnt the same length, then you’ll end up with an ending that looks like BA, AA or BB).
As in the single-array structure before, each person is matched with the person after them, with the last person in the array looping back around.
Option 2: Bouncing
As before, we have two arrays, A and B. This time, bounce back and forth between the arrays, so that $a matches to $b, $b matches to $a, $a matches to $b, $b matches to $a, etc. This gets a little more tricky to do than Option 1, because you have to keep track of when you get to the end of one of the arrays, and instead match to their own array. The final person, which will either be $a[n] or $b[m], matches to $a, completing the chain.