4

I have an array with 8 numbers, 1-8

arr = [1, 2, 3, 4, 5, 6, 7, 8]

each number in the array is part of a group

1 & 2 - group a
3 & 4 - group b
5 & 6 - group c
7 & 8 - group d

What I need to do is match each number in my array, with another number from the same array, but they can not be in the same group

  • 1 & 2 may not be matched with 1 or 2
  • 3 & 4 may not be matched with 3 or 4
  • 5 & 6 may not be matched with 5 or 6
  • 7 & 8 may not be matched with 7 or 8

Conditions

  1. May not be predetermined, given the same inputs, different solutions must be possible
  2. no repeat pairings for example if 2 pairs with 8, 4 can not also pair with 8
  3. They must be paired one at a time, with the ability to leave some pairings unfinished and come back at a later date to complete more pairings
  4. Pairings cannot be reset or undone, once paired it is permanent
  5. One pairing can not run both ways in all cases. For example, if 2 is paired to 3 then 3 can not always pair with 2. It is acceptable if this happens from time to time, but it can not be an intended feature.
  6. We can not assume the pairings will be made in any specific order. For example, the first pairing may be made by 1, or maybe 7 or 3 and so on. Any number may need to be paired at any time.

My problem is that if you pick it in just the right order, you can get stuck on the last number where the only pairing left would be to pair with itself or it's groupmate.

I want to stress a condition here because it keeps being overlooked in answers. I want pairings to be made one at a time. This means that you should be able to space making each pairing out as far as you want. I want to be able to make a pairing on day 0, and then I can come back day 1, week 2, or year 2750 to make the second pairing. This is necessary. Each and every single pairing must be made completely independent of each other, and at the end, the last number must be still able to make a valid pairing.

example...

6 with 8
7 with 6
5 with 7
3 with 5
8 with 4
2 with 3
4 with 2
1 with _

This order leaves 1 with no option but itself.

What can I do to make it so no matter what the last number always has a viable pairing?

update: I have added a fairly good solution in the answers section. If you are still struggling to understand what I want to accomplish try reading my answer in the answers section. The attached code is outdated since I have found a currently usable answer.

antiquated code below

 function selectGiftee(req, res, db){
        const {user_id, group_id} = req.body
        db.select('name', 'user_id', 'giftee_id', 'group_id').from('users')
        .then (data => {
            if (data.length) {
    // only sending available giftees

                const taken = [];
                const fullList = [];
                let available = [];

    // figure out which giftees are taken
                data.forEach( user => {
                    if (user.giftee_id !== null){
                        taken.push(user.giftee_id)
                    }
                    if (user.group_id === group_id) {
                        taken.push(user.user_id)
                    }
                })
    // add all giftees to a list
                data.forEach( user => {
                    fullList.push(user.user_id)
                })

    // only add available giftees to the available list
                available = fullList.filter(val => !taken.includes(val));

    // respond with only giftees that are not taken
                res.json(available)
Chance
  • 347
  • 2
  • 7
  • 2
    Welcome. Please show your code and state as precisely as possible what you'd like to obtain from a given input (which, as far as I understood, is an array and some "groups"). – Jeto Sep 01 '18 at 14:36
  • `1 with ?` ... *`?`* should be all but 1 or 2(same group). So? Is there any rule on the best pair (one of `3,4,5,6,7,8`) ? This will collide with your second condition anyways as far as I can see. – Roko C. Buljan Sep 01 '18 at 14:43
  • Sorry that I was not more clear to begin with. I have updated my post with my current code which is not working because it can leave a remainder in it's current state. Roko there is no rule for a best pair, the only rules it can not pair with itself or group mate and two items may not share a pair. – Chance Sep 01 '18 at 14:53
  • Are the number of items in each *group* predictably similar? For example, can you depend on every group having the same number of items? – CertainPerformance Sep 08 '18 at 05:32
  • @Chance can a group contain more than 2 numbers? – Koushik Chatterjee Sep 08 '18 at 14:06
  • Sorry am i missing something..? What is wrong with `[1, 2, 3, 4, 5, 6, 7, 8].map((e,i,a) => [e,a[(i+3) % a.length]]);` which gives pairs like `[[1,4],[2,5],[3,6],[4,7],[5,8],[6,2],[7,2],[8,3]]` – Redu Sep 08 '18 at 14:37
  • @Redu your solution is violating condition number 1, since the pairings should not be predetermined. – Alexander Moser Sep 10 '18 at 14:41
  • @CertainPerformance the preference for the solution would be to solve it where a group can contain any amount of numbers, however my current solution only accounts for groups of always 2. So a better solution for groups that are always grouped in 2's would be acceptable, but not the preference – Chance Sep 10 '18 at 14:58
  • No repeating of pairings doesn't mean that 3 can't be paired with 2, if 2 is already paired with 3? Having the pairings `[2, 3]` and `[3, 2]` would be valid? – Alexander Moser Sep 10 '18 at 15:01
  • @AlexanderMoser it would be acceptable if what you suggested happens at random from time to time. What would not be acceptable is if you made all pairs in that manner. for example, the solution can not be to pair 1 to 3 and 3 to 1 then pair 2 to 4 and 4 to 2. – Chance Sep 10 '18 at 15:05

4 Answers4

1

Lets take our groups as one long list:

1 2|3 4|5 6

Now lets divide that in the middle, and move one part below the other one:

1 2|3
4|5 6

Now every element got a pair (each column) that is not from the group itself, you could turn it into a continous list by appending all the columns to one:

(1 -> 4) -> (2 -> 5) -> (3 -> 6) -> 

Now to get different combinations, we just shuffle the array of groups and the groups itself before.

// stolen from https://stackoverflow.com/questions/6274339/how-can-i-shuffle-an-array
function shuffle(a) {
    for (let i = a.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [a[i], a[j]] = [a[j], a[i]];
    }
    return a;
}


const groups = [[1, 2], [3, 4], [5, 6], [7, 8, 9]];

groups.forEach(shuffle);
shuffle(groups);

console.log("shuffled:", groups.join(" | "));

const list = [].concat(...groups);

console.log("list:", list);

// Now pair every element by matching the first and the second half:
const pairs = [];

for(let i = 0; i < Math.floor(list.length / 2); i++) {
  pairs.push([
   list[i],
   list[i + Math.floor(list.length / 2)]
  ]);
 }

 if(list.length % 2)
   pairs.push([list.pop()]);
 console.log("pairs:", pairs.join(" | "));

 const result = [].concat(...pairs);

 for(let i = 0; i < result.length; i++)
   console.log(result[i] + " -> " + result[(i + 1) % result.length]);
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • Ok so, if I take this method, and then mix it with my method mentioned in he comments of weighting each group when it is picked the combination of the two seem to produce a method that works. – Chance Sep 01 '18 at 17:40
  • @chance why weight at all? – Jonas Wilms Sep 01 '18 at 17:41
  • there is an answer that will break it I believe. perhaps I'm wrong though, please correct me if I am. 1 to 3, 2 to 5, 3 to 1, 4 to 6, 5 to 2, 6 to 7, 7 to 4, and 8 is left only to pair with itself. – Chance Sep 01 '18 at 17:49
  • @chance that won't occur – Jonas Wilms Sep 01 '18 at 17:51
  • oh ok good, can you help me understand why. As a disclaimer, I've only been coding about 3-4 months, so complex problems are often difficult for me to grasp. thanks for all your help thus far. – Chance Sep 01 '18 at 17:58
  • oh wait nvm, I get it now. 8 can't pair with itself because you're only dealing with one array not two like I have been. – Chance Sep 01 '18 at 17:59
  • @chance if 1 pairs to 3, then 2 pairs to 4, 5 pairs to 7 and 6 pairs to 8 – Jonas Wilms Sep 01 '18 at 17:59
  • @chance exactly – Jonas Wilms Sep 01 '18 at 18:00
  • @chance to get the other pairing you could just shift the list by one – Jonas Wilms Sep 01 '18 at 18:02
  • @chance glad to help, I'm planning to open a bounty on this question (as it deserves more attention as it is actually a very complicated problem and i guess there are some other solutions to it) so just to clarify: if 1 is matched to 2, can 2 be matched to 1 or not? – Jonas Wilms Sep 01 '18 at 18:24
  • well it can, however, it is not optimal for what I'm doing make the pairs all go both ways. for example, 4 to 1 and 1 to 4. it's ok if this does happen sometimes, however, it is not preferred if this is the case. I actually have a pretty good solution in place now that I wrote myself after getting ideas from you two guys. Only problem is I'm not sure what the best way to share it is, should I answer my own question or edit my post? I've learned rather quickly here that doing something wrong gets you a downvote – Chance Sep 02 '18 at 18:08
  • @chance if it really answers the question, its an answer :) self-answering is appreciated – Jonas Wilms Sep 02 '18 at 19:04
  • the return I get is a list of all numbers already paired, instead of just a single number ready for a pair. also, there was only 7 numbers returned to be paired, the number 1 didn't have a pair when I tried it. Maybe I am doing something wrong here? To clarify, what I need is to be able to pass to a function I'm user1 and I have no pair. A function will run and then return a single number to be it's pair and then it will stop. No other pairs will be made. I need there to be the option of sometimes days, weeks or even months between making one pairing and another. – Chance Sep 02 '18 at 20:32
  • 1
    @chance shuffle once, store the shuffled version in db. Then you can easily get each pair. And you are right it doesnt work yet for an uneven number of particupants, will edit that in tomorrow. – Jonas Wilms Sep 02 '18 at 20:35
  • 1
    That was fallback if I could not find a way to do it individually, but my goal was to pick one a time, in case I needed to add a group. Also it was a personal goal of mine to make this possible. before I came to overstack I had already found a way to pair people if I did them all at once and stored the data(not as elegant as your solution though). You could randomize the pairings, check a few conditions and if they fail randomize until they don't. The true problem I wanted solved was pairing on the fly, one at time, with no remainder. My method (while sloppy) does accomplish that goal. – Chance Sep 02 '18 at 20:47
1

So here is the method I came up with to answer my own question. What I've done is to split the groups into two separate groups first. This means that group a and b are in metagroup 1 and group c and d are in metagroup 2.

second I have added in a weighting system. so when a pair is trying to be made i collect all the secondary numbers in the pairs that have already been taken and I add a weight to their group. for example, if 4 has been paired with 6 then group c gets +1 to their weight. This is only the first step of the weighting though.

Now, in the current example, 4 has already paired with 6, and therefore group c has a weight of 1. Now we want to pair 3. 3 is in the same group as 4, which is group b. So now group 3 will look at 4, and see that it has already got a pair, which is 6. 6 is part of metagroup 2, and so now both group c and d are given +10 to their weight. This leaves group c with 11, and d with 10.

Edit: these two conditions were added to clear up some less common errors I found. first I added a negative weight (-1) for any number that has not been paired yet. This makes it so that numbers without pairs are chosen before numbers with pairs. I had to do this because I was still on rare occasion getting stuck with one number that could not pair at the end. Second I changed the way numbers in the same group were handled. previously I simply removed them from the available list. This however caused an issue if their group had the lowest weight. The algorithm would suggest picking a number from that group because it's weight was lowest, but there were no numbers in the group so it resulted in a deadlock. Now i add 20 weight to the group a number is in so that it can never be the lowest weight.

So now we have our weights set and 3 is still trying to pair. we have a look at all our weights and see that group a and b have a 0 for their score and c has 11 and d has 10. 3 is part of group b and pairing with self is specifically blocked so that is not possible, so this leaves only group a to choose from, so 3 will pair with either 1 or 2.

this is the only method I was able to find that would allow me to form pairs 1 at a time on demand. Below is my code, it may be a bit confusing since I'm just pulling it straight out of a program, but if anyone needs clarificaion on how it works I'll be happy to explain.

function chooseGiftee(avail){
    const int = avail.length;
    const index = Math.floor((Math.random() * int) + 1);
    return avail[index-1];
}

function getCandidates(weights){
        return candidates;
}



function selectGiftee(req, res, db){
    const {user_id, spouse_id, group_id} = req.body;
    if (!user_id || !spouse_id || !group_id){
        return res.status(400).json('Missing Credentials')
    }

    db.select('user_id', 'spouse_id', 'giftee_id', 'group_id').from('users')
    .then (data => {

        if (data.length) {
// only sending available giftees


            let newGiftee;
            const taken = [];
            const fullList = [];
            let available = [];
            let filteredAvailable = [];
            let nullCount = 0;
            let nullArr = [];
            let a = 0;
            let b = 0;
            let c = 0;
            let d = 0;

//for the love of god man please refactor all this stuff!!!!!!!


// figure out which giftees are taken and set weight for already picked groups 
            data.forEach( user => {

                if (user.giftee_id === null){
                    switch (user.user_id){
                        case 1:
                        case 2:
                            a--;
                            break;
                        case 3:
                        case 4:
                            b--;
                            break;
                        case 5:
                        case 6:
                            c--; 
                            break;
                        case 7:
                        case 8:
                            d--;
                            break;
                    }
                }

                if (user.giftee_id !== null){
                    taken.push(user.giftee_id);
                }
                switch (user.giftee_id){
                        case 1:
                        case 2:
                            a++;
                            break;
                        case 3:
                        case 4:
                            b++;
                            break;
                        case 5:
                        case 6:
                            c++; 
                            break;
                        case 7:
                        case 8:
                            d++;
                            break;
                    }

                if (user.group_id === group_id) {
                    switch(user.giftee_id){
                        case 1:
                        case 2:
                        case 3:
                        case 4:
                            a += 10;
                            b += 10;
                            break;
                        case 5:
                        case 6:
                        case 7:
                        case 8:
                            c += 10;
                            d += 10;
                            break;
                    }
                    switch(user.group_id){
                        case 1:
                            a += 10;
                            break;
                        case 2:
                            b += 10;
                            break;
                        case 3:
                            c += 10;
                            break;
                        case 4:
                            d += 10;
                            break;
                    }
                }
            })


// add all giftees to a list
            data.forEach( user => {
                fullList.push(user.user_id)
            })

// only add available giftees to available list

            available = fullList.filter(val => !taken.includes(val));


// Choose from what is available based on groupWeight
            let lowWeight = Math.min(a, b, c, d);
            let candidates = [];
            if(lowWeight === a){
                    candidates.push(1, 2);
            }
            if(lowWeight === b){
                    candidates.push(3, 4);
            }
            if(lowWeight === c){
                    candidates.push(5, 6);
            }
            if(lowWeight === d){
                    candidates.push(7, 8);
            }

            filteredAvailable = available.filter(val => candidates.includes(val));



// check if there is three or less choices left, and if so we need to prevent a deadlock

            if (nullCount <= 4){
                filteredAvailable = filteredAvailable.filter(val => !nullArr.includes(val))
            }
            newGiftee = chooseGiftee(filteredAvailable);
Chance
  • 347
  • 2
  • 7
  • @JonasWilms Here is what I came up with – Chance Sep 02 '18 at 19:38
  • that only works for 4 groups right? And are you sure that it works in all cases? – Jonas Wilms Sep 02 '18 at 19:49
  • it works in all cases for 4 groups, I have tested every way I can think of. Now what I can say is it would not work for 3 groups, but I think it my code could be altered to work for all number of groups 4 and above. For sure it would work for all even number of groups even 2 groups. I have not thought much about odd groups because in my case it isn't a problem, I for sure only have 4 groups and always will only have 4. – Chance Sep 02 '18 at 19:58
  • there is a `fun` in your code in the middle of nowhere ... And I still think that I provided the more elegant version, however +1 for your effort. – Jonas Wilms Sep 02 '18 at 20:00
  • @JonasWilms the fun was a pasting error, thanks for pointing that out. Yours is definitely a more elegant solution. However you came up with a stipulation I had not thought of. Can all the pairs reciprocate? This is something I did not really want. The reason being I am making a website for my family that will automatically do a secret santa drawing. It's no fun if you get someone and they also get you. besides that part of secret santa is the secret, and if anyone figured out it worked that way they would know who their secret santa is. and I would know for sure who was getting me a gift – Chance Sep 02 '18 at 20:08
  • @JonasWilms I did run some tests and this method does work on all even amount of groups. – Chance Sep 02 '18 at 20:11
  • true, thats why my answer prevents that case. The only thing is that it is more likely that the group you get the present from is also the group of your giftee (but its not always the case) – Jonas Wilms Sep 02 '18 at 20:11
  • I think perhaps I just can't fully comprehend what you code is doing then. I'm still new to this, so I may need to go back and read through it again to get a better grasp on it. – Chance Sep 02 '18 at 20:14
1

If each of the group contains 2 values exactly (I will address if not too) then we can keep punch the numbers at odd index to keep creating a wall in between them. for eg

Groups

[ [1,2], [3,4], [5, 6], [7, 8] ]

Starts with [1, 2] the initial array

process next -> [3, 4] punch at index 1 and 4 at index 3 so it became [1, 3, 2, 4]

Now process next ([5,6]) and do the same, so it became: [1, 5, 3, 6, 2, 4]

and then process next and continue.

Now, If there are groups that can have arbitrary length! SO, now here is the problem (Bang On! this is why I came here)

First we need to understand when (under which condition) it can be done! If there exists a group having length L and summation of length of all other groups is M then N - M should be <= 1

WHY?

lets assume a group have 3 members, in order to separate them out you need minimum 2 wall. so, all other group combining must have length 2.

So, If condition satisfied now we need to create pairs (because now possible)

  1. First step, sort the array of groups desc with respect to their length
  2. We need to start with the first one, but we need a continuation of odd indexes if the second group did not spoiled the first group completely.

Let's assume Groups are:

[[1, 2, 3, 4, 5, 6], [7,8,9], [10, 11, 12], [...], [.....]]

As the first group have length 6 and 5 elements is needed to dismiss it completely the next group [7, 8, 9] have length 3 so definitely it needs 2 more elements, so when we will start processing the next group after placing the current group components at 1, 3, 5 index, we need to start placing at 7, 9, ...

  1. Once it dismissed the first array we can start from index 1 and keeping odd indexes.

I can produce a working code on demand, but once it's clear what we have to do the coding part all of us can do easily. Focused mainly in the algorithm part. However I can write a code if that helps!

ADDED example of implementation

var groups1 = [
    [7, 8, 9],
    [10, 11],
    [1, 2, 3, 4, 5, 6],
    [12, 13]
  ],
  groups2 = [
    [1, 2],
    [3, 4],
    [5, 6],
    [7, 8]
  ];

function punchGroups(groups) {
  groups.sort((a, b) => b.length - a.length);
  let firstLen = (groups[0] || []).length,
    oic = 0, //odd index increment counter
    covered = false; //covered the largest array flag
  return groups.reduce((acc, e) => {
    if (!acc.length) {
      return acc.concat(e)
    }
    e.forEach((n, i) => {
      let idx = (covered ? (i * 2) : (oic++) * 2) + 1;
      acc.splice(idx, 0, n);
      covered = oic >= (firstLen - 1)
    })
    return acc;
  }, []);
}

console.log('Groups: ', groups2);
console.log('Pairs: ', punchGroups(groups2));
console.log('-------------------------------');
console.log('Groups: ', groups1);
console.log('Pairs: ', punchGroups(groups1));

Well, Now to have different combinations of pairs (obviously within one of the possible pairs) you just need to manage groups and member of groups in all possible combinations (just use recursion to have all the combo) and process each to have all different possible pairs. You can generate all the outputs and pick one from them each day with a index or some factor which will make your call different may be timestamp and calculate it to a number within index range of all possible output array.

Koushik Chatterjee
  • 4,106
  • 3
  • 18
  • 32
  • Koushik, if I am understanding correctly I believe this would violate my first rule of not be predetermined because what I believe you are suggesting means that for example, if I am always given the same groups with the same numbers the result will always be the same. – Chance Sep 10 '18 at 15:01
  • @Chance You can shuffle each group once before process in order to make different output. – Koushik Chatterjee Sep 10 '18 at 16:44
  • @koushikmagic Thereare quite a few problems with that. If you select all pairs, then you shuffle at the end then you are not meeting condition #3 they must be paired one at a time. This would be making all pairs at the end. In addition to that, when you shuffle what is to stop 1 from pairing with 2 etc? – Chance Sep 10 '18 at 17:25
  • A output either can be logical (and predetermined by dry running the logic) else it can be derived from random (% a random number, or generating random index and fetch from a array) but cannot be both at a same time. If your result is not guessable then you need to do shuffle (which is a operation based on random number) and in future no guarantee of having diff output. So you can create all the combinations of the group contents (and not shuffle) then process the same algo for all and produce all the outputs. And pick one randomly (or send a index as input arg and return value that index) – Koushik Chatterjee Sep 10 '18 at 17:55
  • And about always different pair and "not predetermined" you need to understand that, if a set of input cannot be processed (I have also showed the condition) then there will be no output at all. If a set of input can generate 3output combinations, then you have pick within those 3 (which is guessable) and you can maximim pick one of them randomly or sequentially. If you run it 4times (with input having 3output combinations) it must have to repeat. – Koushik Chatterjee Sep 10 '18 at 18:01
  • @Chance I have posted a code snippet to explain further. You can have a look! – Koushik Chatterjee Sep 14 '18 at 11:10
0

Your numbers are graph nodes, and edges exist from every node to all others but group neighbor. You need to cover all n nodes with n/2 edges while no node is shared by two edges. This covering is called perfect matching (not maximal as I wrote before, but perfect is maximal)

Python example using networkx library finds maximal matching (here is also perfect, but we cannot expect this fact always):

import networkx as nx
lst = [1, 2, 3, 4, 5, 6, 7, 8]
G = nx.Graph()
for i in range(0, len(lst)):
    for j in range(i + 1, len(lst)):
        if ((i // 2) != (j // 2)): //make edge for items from different pairs
            G.add_edge(lst[i], lst[j])
#print(G.edges)
m = nx.maximal_matching(G)
print(m)
>>> {(4, 2), (1, 3), (6, 8), (5, 7)}
MBo
  • 77,366
  • 5
  • 53
  • 86
  • how to shuffle this? – Jonas Wilms Sep 01 '18 at 16:00
  • @Jonas Wilms Is it needed? Perhaps there are algoritms for enumeration of all possible matchings (while quick searching shows articles about enumeration mainly for bipartite graphs) – MBo Sep 01 '18 at 16:09
  • thats how i read the question `One number is chosen from the array available at random, and that number is then paired` – Jonas Wilms Sep 01 '18 at 16:26
  • Yes @Jonas Wilms is right. I need to pair the items one at a time and need to be able to leave some numbers unpaired so that they can be paired at a later date. for example, if I am currently pairing the number 1, I need returned only a list of possible pairs for the number 1 that will not cause a conflict. this means I can not return 1 or 2, but it also means that it can not return any number that would cause a separate pairing later to be unable to be completed. – Chance Sep 01 '18 at 16:51
  • @chance does it have to be completely random? Or is it okay if it leaves out some combinations? – Jonas Wilms Sep 01 '18 at 16:57
  • @JonasWilms It can leave out some combinations. I've been thinking about this all morning and I think I may have found a solution myself. I think I may force each group of 2 to select from two different groups of two. for example if 1 (group a) pairs with 3 (group b) then 2 (group a) will not be allowed to pair with 4 (group b) it will be forced to choose from either group c or d. I thnik this works, but I have no checked all scenarios yet. – Chance Sep 01 '18 at 17:03
  • @JonasWilms ok so my last solution does not work, however, what does work is if you divide the 8 into just two large groups and force everyone to pick their first pairing from the first half of the group, and their second pairing from the second half. – Chance Sep 01 '18 at 17:16
  • @chance I just came up with exactly that approach – Jonas Wilms Sep 01 '18 at 17:16
  • @chance and that is? – Jonas Wilms Sep 01 '18 at 17:19
  • My answer was inexact, perfect matching is needed rather than maximal. While algorithm for maximal is simple and perhaps allows to generate different matchings easily, algorithms for perfect (Edmond's one etc) are quite complex. – MBo Sep 01 '18 at 17:21
  • @MBo doesnt matter as randomness is a requirement, which is a bit more complex with this example – Jonas Wilms Sep 01 '18 at 17:22
  • @Jonas Wilms So we can randomly separate nodes into two parts of bipartite graph and look for matching (if exists) – MBo Sep 01 '18 at 17:24
  • @Jonas Wilms Perhaps your approach with group shuffling is simple and quite effective implementation of this idea – MBo Sep 01 '18 at 17:27
  • @JonasWilms 1:3, 2:5, 3:1, 4:6, 5:2, 6:7, 7:4 8:? I think what I am going to do now is weight each group, as in every time a group is paired from, it goes up in count. then I will only make possible pairings from groups with the lowest weight. for example 1 pairs with 3, now group b gets +1 and when it is 2 turn to pair group b is excluded because it has weight + 1. then 2 pairs with 5, now group c gets plus 1. now when 3 goes to pair, it can only choose from group a or d. it pairs to 1 now group a gets plus 1 and when 4 pairs it can only pair with group d. I think this solves it. – Chance Sep 01 '18 at 17:32
  • @MBo lol, I hate to admit this, but i believe your methods are just too far above my head. I am having trouble comprehending it. – Chance Sep 01 '18 at 17:33