订购组合以获得最大效率

时间:2011-06-19 03:37:54

标签: php computer-science combinations

所以最近我遇到了一个问题,我一直在考虑,但仍然无法解决;我想知道这里是否有人可以通过向我提供这个问题的伪代码(或者至少是伪代码的粗略轮廓)来指出我正确的方向。 PS如果有所作为,我将用PHP构建......

功能

有大约50个人(对于这个例子,我只称他们为a,b,c ......)并且用户将他们分成三个一组(组中的人可能重叠),并且最后将有50-100个组(即{a,b,c}; {d,e,f}; {a,d,f}; {b,c,l} ......)。 *

到目前为止很容易,这是建立一个html表单并将其处理成多维数组的问题


白天有大约15个时段(例如上午9点,上午9点20分,上午9点40分......)。这些团体中的每一个都需要在白天见面一次。并且在一个时间段内,该人不能被双重预订(即'a'在上午9:40不能在两个不同的组中)。

这里变得棘手,但并非不可能,我最好的猜测是如何做到这一点就是强行(挑出没有重叠的组(例如{a,b,c}; { l,f,g}; {q,n,d} ...)然后将每个放入一个时间段


最后,我输出的时间表需要'优化',我的意思是'a'应该在会议之间的时间最短(所以如果他的第一次会议是在上午9:20,他的第二次会议应该't 是在下午2:00)。

这里是我迷路的地方,我唯一的猜测是建立许多很多时间表,然后根据一个人从一次会议到下次会议的平均等待时间对他们进行排名


然而,我的“解决方案”(我毫不犹豫地称之为“解决方案”)需要太多的蛮力,并且创建时间太长。是否有更简单,更优雅的解决方案?

3 个答案:

答案 0 :(得分:1)

这些是为您的场景

修改的表格
+----User_Details------+  //You may or may not need this
| UID | Particulars... |
+----------------------+

+----User_Timeslots---------+  //Time slots per collumn
| UID | SlotNumber(bool)... |  //true/false if the user is avaliable
+---------------------------+  //SlotNumber is replaced by s1, s2, etc

+----User_Arrangements--------+  //Time slots per collumn
| UID | SlotNumber(string)... |  //Group session string
+-----------------------------+

注意:Arrangement表中的字符串采用以下格式:JSON

  

'[12,15,32]'//从最小到最大!

所以在排列表中发生的事情是,脚本[或者EXCEL列公式]会在每个会话中通过每个插槽,并随机创建一个可能的会话。检查所有先前的会话是否存在冲突。

/**
* Randomise a session, in which data is not yet set
**/
function randomizeSession( sesionID ) {
    for( var id = [lowest UID], id < [highest UID], id++ ) {
        if( id exists ) {
            randomizeSingleSession( id, sessionID );
        } //else skips
    }
}

/**
* Randomizes a single user in a session, without conflicts in previous sessions
**/
function randomizeSingleSession( id, sessionID ) {

    convert sessionID to its collumns name =)
    get the collumns name of all ther previous session

    if( there is data, false, or JSON ) {
        Does nothing (Already has data)
    }

    if( ID is avaliable in time slot table (for this session) ) {
        Get all IDs who are avaliable, and contains no data this session
        Get all the UID previous session
        while( first time || not yet resolved ) {
            Randomly chose 2
            if( there was conflict in UID previous session ) {
                try again (while) : not yet resolved
            } else {
                resolved
            }
        }

        Registers all 3 users as a group in the session

    } else {
        Set session result to false (no attendance)
    }
}

您将意识到分组的主要部分是通过随机化。但是,随着会话数量的增加。将有越来越多的数据来检查冲突。导致性能下降得慢得多。无论大而且大得可笑,几乎是完美的排列/组合配方。

编辑:

此设置还有助于确保只要用户可用,他们就会在一个组中。虽然你可能有一些用户,没有用户组(少数)。这些通常通过重新计算(对于小会话数)来补救。或者只是手动将它们组合在一起,即使它是重复的。 (这里有一些,没有伤害)。或者在您的情况下,与剩余部分一起,加入几个3组,形成4个组。=)

如果这可以用于大约100多个人的EXCEL,大约10个会话。我不知道这在SQL + PHP中是如何工作的。只是计算实际上可能需要相当长的时间。

答案 1 :(得分:1)

好的,对于那些刚加入这篇文章的人,请在考虑这个问题的内容之前仔细阅读问题的所有评论,因为这很可能会超出你的想法。

这是PHP'ish风格的一些伪代码:

/* Array with profs (this is one dimensional here for the show, but I assume
it will be multi-dimensional, filled with availability and what not;
For the sake of this example, let me say that the multi-dimensional array
contains the following keys: [id]{[avail_from],[avail_to],[last_ses],[name]}*/
$profs = array_fill(0, $prof_num, "assoc_ids");

// Array with time slots, let's say UNIX stamps of begin time
$times = array_fill(0, $slot_num, "time");

// First, we need to loop through all the time slots
foreach ($times as $slot) {

    // See when session ends
    $slot_end = $slot + $session_time;

    // Now, run through the profs to see who's available
    $avail_profs = array(); // Empty
    foreach ($profs as $prof_id => $data) {

        if (($data['avail_from'] >= $slot) && ($data['avail_to'] >= $slot_end)) {

            $avail_prof[$prof_id] = $data['last_ses'];

        }

    }

    /* Reverse sort the array so that the highest numbers (profs who have been
    waiting the longest) will be up top */
    arsort($avail_profs);
    $profs_session = array_slice($avail_profs, 0, 3);
    $profs_session_names = array(); // Empty

    // Reset the last_ses counters on those profs
    foreach ($profs_session as $prof_id => $last_ses) {


        $profs[$prof_id]['last_ses'] = 0;
        $profs_session_names[0] = $profs[$prof_id]['name'];

    }

    // Now, loop through all profs to add one to their waiting time
    foreach ($profs as $prof_id = > $data) {

       $profs[$prof_id]['last_ses']++;

    }

    print(sprintf('The %s session will be held by: %s, $s, and %s<br />', $slot,
                   $profs_session_names[0], $profs_session_names[1],
                   $profs_session_names[2]);

    unset ($profs_session, $profs_session_names, $avail_prof);

}

那应该打印如下:

The 9:40am session will be held by: C. Hicks, A. Hole, and B.E.N. Dover

答案 2 :(得分:0)

我看到一个由以下组成的对象模型:

  • 小组成员:小组成员的固定存储库(Tom,Dick,Harry等)
  • 面板:由X个小组成员组成(在您的情况下X = 3)
  • Timeslots:您的时间段的固定存储库。假设固定的持续时间并且仅在一天内发生,那么您需要的只是跟踪是开始时间。
  • 会议:由Panel和Timeslot组成
  • 时间表:由许多会议组成

现在,正如您所观察到的,优化是关键。对我来说,问题是:“根据什么标准进行优化?”。汤姆的最佳选择可能意味着他所在的小组没有大的差距。但哈利的小组可能全部都是。因此,也许对于给定的时间表,我们计算类似totalMemberDeadTime(=时间表中所有死区时间成员间隙的总和)。最佳时间表是与此总和相关的最小时间表

如果我们有兴趣在所有时间表的范围内计算技术上最佳的时间表,我实际上并没有看到蛮力的替代方案。

或许,计划表的范围不需要像最初出现的那样大。听起来首先构成面板然后问题是将它们分配给会议,这些会议构成了一个时间表。因此,我们消除了面板组成的可变性;可变性的全部范围在会议和附表中。不过,那里看起来似乎有很多变化。

但就所有可能的时间表而言可能是最优的,而不是我们真正需要的。

如果没有专门小组成员的死亡时间超过X,我们是否可以将时间表定义为可接受的?或者如果不成功,如果不超过X小组成员的死时间超过X(不能满足所有人,但要保持最低限度)?然后,用户可以首先为包含更多“重要”小组成员的面板分配会议,而不那么重要的人只需要拿走他们得到的内容。那么我们所要做的就是单个可接受的时间表

您的目的是否足以比较任何两个附表?结合界面(我看到拖放界面,但显然超出了这一点),允许用户构成计划,将其克隆到第二个计划,并调整第二个计划,寻求减少聚合死的时间,直到我们找到一个可以接受的时间。

无论如何,不​​是一个完整的答案。只是大声思考。希望它有所帮助。