10

For the following problem I'm wondering if there is a known algorithm already as I don't want to reinvent the wheel.

In this case it's about hotel rooms, but I think that is rather irrelevant:

name   | max guests | min guests
1p     | 1          | 1
2p     | 2          | 2
3p     | 3          | 2
4p     | 4          | 3

I'm trying to distribute a certain amount of guests over available rooms, but the distribution has to satisfy the 'min guests' criteria of the rooms. Also, the rooms need to be used as efficiently as possible.

Let's take 7 guests for example. I wouldn't want this combination:

3 x 3p ( 1 x 3 guests, 2 x 2 guests )

.. this would satisfy the minimum criteria, but would be inefficient. Rather I'm looking for combinations such as:

1 x 3p and 1 x 4p
3 x 2p and 1 x 1p
etc...

I would think this is a familiar problem. Is there any known algorithm already to solve this problem?

To clarify:
By efficient I mean, distribute guests in such a way that rooms are filled up as much as possible (guests preferences are of secondary concern here, and are not important for the algorithm I'm looking for).
I do want all permutations that satisfy this efficiency criteria though. So in above example 7 x 1p would be fine as well.

So in summary:
Is there a known algorithm that is able to distribute items as efficiently as possible over slots with a min and max capacity, always satisfying the min criteria and trying to satisfy the max criteria as much as possible.

Decent Dabbler
  • 22,532
  • 8
  • 74
  • 106
  • "the rooms need to be used as efficiently as possible" it is not clear what your measure of efficeincy is. – Raedwald Nov 03 '11 at 17:08
  • Should we assume that the guests come in groups (each group with a specific number of people) and that each room can only host a single group, like in a real hotel? – han Nov 03 '11 at 17:10
  • 1
    Your question is confusing. What do you mean by "efficient"? For example, why is 3x3p (3 rooms total) inefficient, but 3x2p + 1x1p (4 rooms total) efficient? It seems that the question is missing a cost or efficiency function. – Andrew Schulman Nov 03 '11 at 17:11
  • @AndrewSchulman: please see my addition, for further clarification. – Decent Dabbler Nov 03 '11 at 17:18
  • @han: no, the human aspect I not really relevant in this case. The most important thing for me is: how do I distribute items as efficiently as possible over slots with a `min` and `max` capacity, always satisfying the `min` criteria and trying to satisfy the `max` criteria where possible. – Decent Dabbler Nov 03 '11 at 17:25
  • @DecentDabbler did you ever figure it a solution for this?, I need to solve this exact scenario. – General Electric Nov 12 '19 at 17:41
  • 1
    @GeneralElectric If I remember correctly, the project I needed this for terminated prematurely, so I don't think I ever did, no. Perhaps you could open a bounty on this question to rekindle it? – Decent Dabbler Nov 13 '19 at 16:35
  • @DecentDabbler sure, I can do that. Can you add an edit to say, I'm looking for something like booking.com. they only ask for # adults, #kids # rooms and give a result based on that instead of doing what expedia does where it ask the combination for each room.?? Thanks. – General Electric Nov 14 '19 at 05:03
  • @GeneralElectric Done! – Decent Dabbler Nov 14 '19 at 15:59
  • Several questions: 1. Are there infinite rooms for every kind of hotel rooms? 2. Are we solving the exact hotel rooms setting just like this? (Has 1p~4p) Or should we solve other kinds of hotel rooms settings? – AnnieFromTaiwan Nov 18 '19 at 04:02

5 Answers5

1

You need to use dynamic programming, define a cost function, and try to fit people in possible rooms having a cost function as small as possible.

Your cost function can be something like :

Sum of vacancy in rooms + number of rooms


It can be a bit similar to the least rageness problem : Word wrap to X lines instead of maximum width (Least raggedness)

You fit people in room, as you fit words in line.

The constraints are the vacancies in the rooms instead of being the length of the lines. (infinite cost if you don't fullfil the constraints)

and the recursion relation is pretty much the same .

Hope it helps

Community
  • 1
  • 1
Ricky Bobby
  • 7,490
  • 7
  • 46
  • 63
0

This can be done as a fairly straightforward modification of the recursive algorithm to enumerate integer partitions. In Python:

import collections

RoomType = collections.namedtuple("RoomType", ("name", "max_guests", "min_guests"))


def room_combinations(count, room_types, partial_combo=()):
    if count == 0:
        yield partial_combo
    elif room_types and count > 0:
        room = room_types[0]
        for guests in range(room.min_guests, room.max_guests + 1):
            yield from room_combinations(
                count - guests, room_types, partial_combo + ((room.name, guests),)
            )
        yield from room_combinations(count, room_types[1:], partial_combo)


for combo in room_combinations(
    7,
    [
        RoomType("1p", 1, 1),
        RoomType("2p", 2, 2),
        RoomType("3p", 3, 2),
        RoomType("4p", 4, 3),
    ],
):
    print(combo)
David Eisenstat
  • 64,237
  • 7
  • 60
  • 120
0
  1. we should select a list of room (maybe more than one time for each room) in a way that sum of the min value of selected room get equal or smaller than the number of guests and sum of the max value get equal or bigger than the number of guests.

  2. I defined cost as total free space in selected rooms. (cost = max - min)

  3. We can check for the answer with this code and find all possible combinations with the minimum cost. (c# code)

    class FindRooms
    {
        // input
        int numberOfGuest = 7; // your goal
        List<Room> rooms;

        // output
        int cost = -1; // total free space in rooms. 
        List<String> options; // list of possible combinations for the best cost

        private void solve()
        {
            // fill rooms data
            // fill numberOfGuest
            // run this function to find the answer
            addMoreRoom("", 0, 0);
            // cost and options are ready to use
        }

        // this function add room to the list recursively

        private void addMoreRoom(String selectedRooms, int minPerson, int maxPerson)
        {
            // check is it acceptable
            if (minPerson <= numberOfGuest && maxPerson >= numberOfGuest)
            {
                // check is it better than or equal to previous result
                if(maxPerson - minPerson == cost)
                {
                    options.Add(selectedRooms);
                }
                else if (maxPerson - minPerson < cost)
                {
                    cost = maxPerson - minPerson;
                    options.Clear();
                    options.Add(selectedRooms);
                }
            }

            // check if too many room selected
            if (minPerson > numberOfGuest)
                return;

            // add more room recursively
            foreach (Room room in rooms)
            {
                // add room and min and max space to current state and check
                addMoreRoom(selectedRooms + "," + room, minPerson + room.min, maxPerson + room.max);
            }
        }
        public class Room
        {
            public String name;
            public int min;
            public int max;
        }
    }
shafaghi
  • 53
  • 1
  • 5
  • Notice: from the question: "So in above example 7 x 1p would be fine as well. " that means the number of rooms aren't important, the free space in rooms is important, and there isn't a single answer, there are a set of answer with same efficiency. I defined the cost equal to the free space. – shafaghi Nov 21 '19 at 06:18
0

For efficint = minimum rooms used, perhaps this would work. To minimise the number of rooms used you want to put max guests in the large rooms.

So sort the rooms in descending order of max guests, then allocate guests to them in that order, placing max guests in each room in turn. Try to place all remaining guests is any remaining room that will accept that many min guests; if that is impossible, back-track and try again. When back tracking, hold back the room with the smallest min guests. Held back rooms are not allocated guests in the max guests phase.


EDIT

As Ricky Bobby pointed out, this does not work as such, because of the difficulty of the back-tracking. I'm keeping this answer for now, more as a warning than as a suggestion :-)

Community
  • 1
  • 1
Raedwald
  • 46,613
  • 43
  • 151
  • 237
  • aha exact same answer at exact same time :D. But I don't think the backtracking is trivial. – Ricky Bobby Nov 03 '11 at 17:37
  • As @Kevin noticed on my deleted answer it's not optimal : "(from @Kevin) I'm not certain about the smallest number assertion. What if you have rooms with capacities 13,8, and 1; and you want to accommodate 24 guests? The greedy algorithm allocates them as (13,8,1,1,1), but the real smallest solution is (8,8,8). " – Ricky Bobby Nov 03 '11 at 17:40
  • 1
    @Ricky Bobby: you should have kept your deleted answer, but edited to indicate the problem in that approach. I'll keep mine, but edited. – Raedwald Nov 04 '11 at 10:32
0
$sql = "SELECT * 
FROM rooms 
WHERE min_guests <= [$num_of_guests]
ORDER BY max_guests DESC
LIMIT [$num_of_guests]";

$query = $this->db->query($sql);
$remaining_guests = $num_of_guests;
$rooms = array();
$filled= false;
foreach($query->result() as $row)
{
 if(!$filled)
 {
  $rooms[] = $row;
  $remaining_guests -= $row->max_guests;
  if(remaining_guests <= 0)
  {
   $filled = true;
   break;
  }
 }
}

Recursive function:

public function getRoomsForNumberOfGuests($number)
{
  $sql = "SELECT * 
  FROM rooms 
  WHERE min_guests <= $number
  ORDER BY max_guests DESC
  LIMIT 1";

  $query = $this->db->query($sql);
  $remaining_guests = $number;
  $rooms = array();
  foreach($query->result() as $row)
  {
    $rooms[] = $row;
    $remaining_guests -= $row->max_guests;
    if($remaining_guests > 0)
    {
     $rooms = array_merge($this->getRoomsForNumberOfGuests($remaining_guests), $rooms);
    }
  }
  return $rooms;
}

Would something like this work for ya? Not sure what language your in?

anthony.c
  • 481
  • 4
  • 15
  • You could also, place this into a recursive function to get the most efficient results however, it would require multiple database calls? but if you limit the results to one result per query, getting the most efficient result based on the number of people left over? it wouldn't be to bad. – anthony.c Nov 03 '11 at 17:47
  • Note: The code isn't tested and was just typed on the fly. No debug has been ran. Use provided code at your own risk. :) – anthony.c Nov 03 '11 at 18:00