I have x guests for my wedding and y tables with z seats. Guest A can sit on the same table as guest B and guest C can not sit on the same table as guest D, ....
Given
I know this question was asked years ago, but still, for those who encounter similar problems.
Definition of the problem
My problem was similar: where to seat people at a wedding taking into account some preferences? It's not exactly the same as the initial problem, but setting the preferences correctly, you can formulate the initial problem.
I did not read about those complex problems and the ways to solve it. I just thought of another way to do it. I'm sure it's not the most optimal way to solve the problem, but it works and it is easy to implement.
The approach
I chose a model in which 1 person can state its desire to sit or not at the same table of another person. Let's call them weights. So, Sara can express her desire by introducing a weight (say from -5 to 5) to sit (5) or not to sit (-5) at the same table of Michael. Maybe she doesn't care (0), or maybe Michael is handsome but a little small (2) or maybe he's nice but smells weird (-3).
Let w_ij be the weight associated to the desire of person i wanting to sit at the same table as person j. Note that w_ij does not necessarily equal w_ji (Sara may like Michael, but maybe Michael hates Sara). Then, let's define the rating of a table as follows:
rating_table = 1/N * sum_i sum_j w_ij
where N is the number of persons at the table. Note that the sum of the weights is a sum of Nx(N-1)/2 terms (including 0 terms). Imagine 3 persons at 1 table, then there are 6 weights, not 9 (one cannot desire to sit at the same table as himself...).
The average rating is then the sum of each rating_table divided by the number of tables. That average rating is the one I intend to maximize.
The idea of the algorithm is as follows. Imagine you have an initial solution (for instance, the people are randomly seated at the tables). Then, what happens if you switch one person i of a table with a person j of another table? The ratings of both tables would change. We save those potential changes in an ordered vector. At the end, we would have a vector of structures, each structure containing the change of rating (sum of change of ratings of the two tables) when person i switches with person j. When you have all the potential changes, we iterate over the vector, starting with the greatest change in rating, and switch persons. The steps to be taken are detailed below.
The algorithm
1) Calculate the vector of structures which contains a reference to the persons being switched, the tables they come from, and the sum of the differences in rating each table would undergo during the switch. Let's call this the overall rating change.
2) Iterate over the vector of structures: if the overall rating change is positive, switch person i with j. Say person i came from table A and person j came from table B. As the setup of tables A and B has changed, we cannot switch more people from or to those tables. So, if the vector contains another switch related to one of those tables, we have to skip it. When the loop is finished, we can start over again. So, maximum 1 switch per table in 1 loop.
3) Continue the switching, as long as the change in rating is positive and tables A and B haven't been used during a switch yet. Note that one table might increase by 2 while the second decreases by 1. The overall change is still positive.
4) Go back to step 1. Now we can switch again between all tables. Again, we can switch persons from table A and B only once during 1 loop.
5) Continue this until all the overall rating changes are negative or 0.
So, you intelligently and selectively select the persons i and j from tables A and B, respectively, such that the best overall rating change occurs, and you continue until no better solution exists.
The downside of the algorithm is step 1: you need to calculate all the overall rating changes. But for an event of even 1000 tables, that shouldn't be a problem.
Unique solution?
Many times, you could find multiple solutions to this problem. Imagine you found one solution with the algorithm described above. Then, any switch between 2 persons which produces a 0 overall change is another solution. So, having found 1 solution, it is easy to find all the solutions.
If you have found 1 solution, and you find a switch with 0 overall rating change and therefore a new solution, you can use that new solution to find another solution, and so on. Imagine everybody sets the same weights (or for example, nobody cares and all weights are 0), then any setup is a solution.
I hope this helps. I know it's a long explanation, and I hope it's clear enough. If you would like more details, please let me know.
If you require an exact solution, formulate it as an 0-1 integer program and use GLPK to solve it.
Let x_ij be 1 if person i is assigned to table j and 0 otherwise. Consider the following constraint set:
(i) sum_{j=1...y} x_ij = 1 for i=1...x
(ii) sum_{i=1...x} x_ij <= z for j=1...y
(iii) x_ij + x_kj <=1 for j=1...y
(iv) x_ij is binary
Constraints (i) make sure everyone is assigned. Constraints (ii) prevents overloading a table. Constraints (iii) are defined for each person pair (i,k) that can't sit together.
Plug it into GLPK, CPLEX, or GUROBI and you're in business, provided that the problem is not too large. As the others have mentioned, NP-hardness means things could get ugly.
This is an NP-hard problem, so you won't find a general solution. In fact, even finding z guests that are able to sit together at a single table is NP-hard.
However, if you don't have too many guest conflicts, then a heuristic will probably work. For example:
Pick an unseated guest G with a maximal number of incident edges (conflicts)
If G has a conflict with someone seated at each table, then fail
Else assign G at random to an available table
Repeat until all guests are seated
A slightly better heuristic involves keeping track of all of the possible tables for each guest. At the outset, each guest can sit at any table.
Pick an unseated guest G such that the size of G.availableTables is minimal
If G.availableTables is empty, then fail
Assign G at random to a table T from G.availableTables
For each guest G2 who is in conflict with G
remove T from the set G2.availableTables
Repeat until all guests are seated.
You could also modify this heuristic to make it even stronger, keeping track of, for each table T, how many unseated guests are able to fill the remaining seats. Then, when you assign a guest to a table, instead of choosing at random, you would preferentially choose tables with a lot of remaining seats and fewer people who are able to sit in them.
In practice, if a heuristic like this doesn't work after trying a few hundred randomized attempts, then it's probably going to be a difficult problem to solve.
This problem is NP-hard in the general case, so you shouldn't expect to find a general solution to it that's efficient if the number of tables or guests is large. This problem is a variation on this earlier problem which asks about divvying people up into different houses based on their preferences, and you can reduce that problem (which is NP-hard) to this one by simply giving each table enough capacity to hold every single guest.
If the number of people per table is small and the number of guests is small, you could just brute-force the solution by trying every possible assignment. Another option would be to try randomly guessing a few solutions and then incrementally modifying them to try to find a solution that works (for example, using a hill-climbing algorithm).
Hope this helps!