C# 我如何生成一个";“社交高尔夫球手”;工人座位安排矩阵?
这是一个挑战。我有一家有320人的公司。我最近实施了一个目标管理(MBO)计划,每个员工都被分配每月完成一个目标。其中一个经常出现的目标是准时上班,参加每天早上30分钟的咖啡和杜努特会议。会议在我们有50张桌子的餐厅举行。每张桌子最多可容纳8人。每个工作日正负80个座位都是空的,因为目前最多可容纳400人。我想生成一个循环的座位安排,这样每个人都可以轮流与其他人会面和合作 (编辑)规则:每个工作日需要8人的独特团队。在用尽所有可能的排列之前,一个人不能再与过去共坐过的其他人坐在一起 编辑:所需结果的示例如下:C# 我如何生成一个";“社交高尔夫球手”;工人座位安排矩阵?,c#,sql,oracle,select,permutation,C#,Sql,Oracle,Select,Permutation,这是一个挑战。我有一家有320人的公司。我最近实施了一个目标管理(MBO)计划,每个员工都被分配每月完成一个目标。其中一个经常出现的目标是准时上班,参加每天早上30分钟的咖啡和杜努特会议。会议在我们有50张桌子的餐厅举行。每张桌子最多可容纳8人。每个工作日正负80个座位都是空的,因为目前最多可容纳400人。我想生成一个循环的座位安排,这样每个人都可以轮流与其他人会面和合作 (编辑)规则:每个工作日需要8人的独特团队。在用尽所有可能的排列之前,一个人不能再与过去共坐过的其他人坐在一起 编辑:所需结
Day 1:
Table 1 will seat worker numbers 1,2,3,4,5,6,7,8.
Table 2 will seat worker numbers 9.10,11,12,13,14,15,16
...
Table 50 will seat worker numbers 313,314,315,316,317,318,319,320
**NOTE:**
(So, the next workday and thereafter, workers 1 through 8 cannot ever be seated with
any other workers from that same set until all possible permutations have been
exhausted).
Day 2:
Table 1 will seat worker numbers 1,17,18,19,20,21,22,23
Table 2 will seat worker numbers 2,10,24,25,26,27,28,29
...
Table 50 will seat worker numbers 305,306,307,308,309,310,311,312
Day N:
.
.
...
.
在用尽所有可能的唯一集合(排列)之前,每个集合(数组)中的8个辅助编号(元素)都不能重复。然后,循环又重新开始,也许会改变元素,只有这样,一个工人才会和他们以前见过的另一个工人坐在一起。然后我会给每个员工发电子邮件,告诉他们下一个工作日要坐的桌子。每个工人都不知道还有谁坐在他们指定的桌子旁,直到他们到达桌子。只有我有完整的座位安排名册。(这是一种“音乐椅”游戏)
这不是练习或学校作业。一位使用APL编程语言的朋友告诉我,她可以用一行代码生成所需的结果,但我们只使用基于SQL的DBMS(IBM Informix 11.70和Oracle 11)
因此,我有一个包含以下列的SQL表:
employee.id INT {unique primary key}
employee.FullName VARCHAR
...
以下一行APL编程代码生成矩阵置换:
pmat2←{{,[⍳2]↑(⊂⊂⎕io,1+⍵)⌷¨⍒¨↓∘.=⍨⍳1+1↓⍴⍵}⍣⍵⍉⍪⍬}
在SQL中,我能用一条SELECT语句生成所需的结果吗?我需要多条SELECT INTO TEMP语句吗?还是需要一个存储过程来获得所需的结果
我的SELECT语句或SP应该是什么样子
编辑:如果所需的结果不能用SQL实现,那么是否可以用一个称为“,”的3GL来实现呢。这实际上是一个非常困难的问题,所以我很难想象它可以通过数据库查询来完成。网上有很多关于这个主题的文献和一些在线计算器
编辑:
您的APL代码只是创建一个排列矩阵。例如,如果输入以下内容:
pmat2←{{,[⍳2]↑(⊂⊂⎕io,1+⍵)⌷¨⍒¨↓∘.=⍨⍳1+1↓⍴⍵}⍣⍵⍉⍪⍬}
pmat2 3
您可以得到以下矩阵:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
根据维基百科:
循环赛(或全场比赛)是“每个参赛者依次会见所有其他参赛者”的比赛
根据Markus Triska在其关于该主题的硕士论文中所述:
社交高尔夫问题(SGP)是一个组合优化问题。这项任务是安排g×p高尔夫球手在g组的p名球员中进行w周的比赛,这样就不会有两名球员在同一组中比赛超过一次
从数学上讲,这是一个很大的区别。循环赛是两人一组的比赛,所以如果你有9名选手,就需要在8轮中进行36场比赛。使用社交高尔夫球手,您可以将他们分成三组,需要在4轮中进行12场比赛:
6 4 8 1 8 3 1 9 6 9 5 8
3 9 7 4 2 9 4 3 5 4 7 1
5 1 2 5 7 6 8 7 2 6 3 2
我不知道这是否有效,但你可以制作一个表,用带有where子句的交叉连接将个人表(只有id就足够)插入8次,在第二个连接中排除employee.id(第二列)!=employee.id(第一列)。在第三个交叉连接中,u必须为employee.id(第三列)!=employee.id(第二列)
在我看来,这将产生所有的组合。然后你只需要随机选择一个并保存它,这样你就不会再选择它了。在SQL中,答案其实很简单,它需要两个表,一个表定义员工,另一个表定义席位。例如: 表:雇员 栏目: EmployeeID-这必须是唯一标识符。 雇员姓名 活动员工-(是/否) 等等 表:座位 栏目: SeatID-这必须是唯一标识符。 表号 表序号 等等 现在定义一个没有连接条件的查询,称为笛卡尔积,这通常是一个不希望出现的结果,但在本例和一些数据仓库实现中不是这样
Select EmployeeID, SeatID from Employees, Seating where ActiveEmployee = 'Y' order by TableSeatNumber, TableNumber;
这将为您提供每个座位的每个员工的结果。这种方法首先在不同的桌子上为整个人群提供不同的座位。如果您的员工流动率很高,则将结果与历史记录进行比较,然后从笛卡尔积中否定该实例
如果您想更多地混合座位,可以使用排序顺序的其他选项,例如唯一字段
希望这能有所帮助。解决问题 如果问题是安排会议的真正任务,那么在提出问题时会出现一些错误。
这是因为工人的数量,甚至可用的桌子和座位的数量并不是一个基本的物理常数:
- 有人可能被解雇,无法参加下次会议李>
- 人力资源部为新项目又雇佣了10名员工,所有员工都必须参加下一次会议李>
- 下周开始装修餐厅,下个月只有20张桌子可用
with params as (
select
320 n, -- number of persons
8 k, -- number of seats per table
41 p -- least prime which greather or equal n/k
from dual
),
person_set as (
select level person_id from dual connect by level <= (select n from params)
),
person_map as (
select
person_id,
mod( mod(person_id, p.k * p.p), p.k ) x,
trunc( mod(person_id, p.k * p.p) / p.k ) y
from person_set, params p
),
meetings as (
select (level-1) meeting_no
from dual
connect by level <= (select least(k*p, (n-1)/(k-1)) from params)
),
seats as (
select (level-1) seat_no
from dual
connect by level <= (select k from params)
),
tables as (
select (level-1) table_no
from dual
connect by level <= (select p from params)
),
meeting_plan as (
select --+ ordered use_nl(seats tables)
meeting_no,
seat_no,
table_no,
(
select
person_id
from
person_map
where
x = seat_no
and
y = mod(meeting_no*seat_no + table_no, p.p)
) person_id
from
meetings, seats, tables, params p
)
select
meeting_no,
table_no,
max(case when seat_no = 0 then person_id else null end) seat1,
max(case when seat_no = 1 then person_id else null end) seat2,
max(case when seat_no = 2 then person_id else null end) seat3,
max(case when seat_no = 3 then person_id else null end) seat4,
max(case when seat_no = 4 then person_id else null end) seat5,
max(case when seat_no = 5 then person_id else null end) seat6,
max(case when seat_no = 6 then person_id else null end) seat7,
max(case when seat_no = 7 then person_id else null end) seat8
from meeting_plan
group by meeting_no, table_no
order by meeting_no, table_no
-- List of persons
create table person(
person_id number not null -- Unique person identifier.
);
-- primary key
alter table person add constraint pk_person primary key (person_id) using index;
-- List of all possible unique person pairs
create table person_pair(
person1_id number not null, -- 1st person from pair, refers person table.
person2_id number not null, -- 2nd person from pair, refers person table.
-- person1_id always less than person2_id.
meet_count number -- how many times persons in pair meet each other.
);
-- primary key
alter table person_pair add constraint pk_person_pair primary key (person1_id, person2_id) using index;
-- indexes for search
alter table person_pair add constraint idx_pair2 unique (person2_id, person1_id) using index;
-- Placement information for meetings
create table meeting(
meeting_number number not null, -- sequential meeting number
table_number number not null, -- table number
person_id number not null, -- person placed on that table and meeting
seat_no number -- seat number
);
-- primary key: person can seat on the same table only once in one meeting
alter table meeting add constraint pk_meeting primary key (meeting_number, table_number, person_id) using index;
-- disallow duplicate seats on the same table during one meeting
alter table meeting add constraint miting_unique_seat unique (meeting_number, table_number, seat_no) using index;
-- person can participate in meeting only once
alter table meeting add constraint miting_unique_person unique (meeting_number, person_id) using index;
begin
-- Fill persons list with initial data
insert into person(person_id)
select level from dual connect by level <=20;
-- generate person pairs
insert into
person_pair(person1_id, person2_id, meet_count)
select
p1.person_id,
p2.person_id,
0
from
person p1,
person p2
where
p1.person_id < p2.person_id
;
end;
/
select * from person order by person_id
/
select * from person_pair order by person1_id, person2_id
/
declare
vMeetingNumber number; -- number of current meeting
vNotMeetPairCount number; -- number of pairs not meet before
vTableCapacity number := 4; -- number of places at one table
vTableCount number; -- number of tables
begin
-- get next meeting number for case of continous generation
select nvl(max(meeting_number),0) + 1 into vMeetingNumber from meeting;
-- count minimum required table number
select ceil(count(1)/vTableCapacity) into vTableCount from person;
-- number of remaining pairs who don't meet before
select count(1) into vNotMeetPairCount
from person_pair
where meet_count < 1;
-- Generate new meetings while not all persons meet each other
while (vNotMeetPairCount > 0) loop
-- select list of persons to place
for cPersons in (
with person_meets as (
select
pp.person1_id, pp.person2_id, pp.meet_count,
( row_number() over (
order by pp.meet_count desc, pp.person1_id
)
) row_priority
from
person_pair pp
)
select person_id from (
select person_id, sum(pair_meet_count*pair_meet_count) pair_meetings from (
select person1_id person_id, meet_count pair_meet_count from person_meets
union all
select person2_id person_id, meet_count pair_meet_count from person_meets
)
group by person_id
)
order by pair_meetings desc
) loop
-- add current person to most applicable table
insert into meeting(meeting_number, table_number, person_id, seat_no)
select
vMeetingNumber, table_number, cPersons.person_id, seat_no
from (
with available_tables as (
select
table_number, places_occupied
from (
select
t.table_number,
(
select count(1)
from meeting m
where
m.meeting_number = vMeetingNumber
and
m.table_number = t.table_number
) places_occupied
from (
select level table_number
from dual connect by level <= vTableCount
) t
)
where places_occupied < vTableCapacity
)
select
table_number,
seat_no,
( row_number() over ( order by
-attractor_factor*attractor_factor - decode(attractor_factor,0,0,repellent_factor/2) + repellent_factor
)
) row_priority
from (
select
t.table_number,
t.places_occupied + 1 seat_no,
(
select
count(1)
from
meeting m,
person_pair pp
where
m.table_number = t.table_number
and
m.meeting_number = vMeetingNumber
and
pp.person1_id = least(m.person_id, cPersons.person_id)
and
pp.person2_id = greatest(m.person_id, cPersons.person_id)
and
pp.meet_count = 0
) attractor_factor,
(
select
nvl(sum(meet_count),0)
from
meeting m,
person_pair pp
where
m.table_number = t.table_number
and
m.meeting_number = vMeetingNumber
and
pp.person1_id = least(m.person_id, cPersons.person_id)
and
pp.person2_id = greatest(m.person_id, cPersons.person_id)
and
pp.meet_count > 0
) repellent_factor,
1 random_factor --trunc(dbms_random.value(0,1000000)) random_factor
from
available_tables t
)
)
where
row_priority = 1
;
end loop;
-- Update number of meets
update person_pair
set meet_count = meet_count + 1
where
(person1_id, person2_id) in (
select
m1.person_id person1_id,
m2.person_id person2_id
from
meeting m1,
meeting m2
where
m1.meeting_number = vMeetingNumber
and
m2.meeting_number = vMeetingNumber
and
m1.table_number = m2.table_number
and
m1.person_id < m2.person_id
)
;
-- advice to next meeting
vMeetingNumber := vMeetingNumber + 1;
-- count pairs who don't meet before
select count(1) into vNotMeetPairCount
from person_pair
where meet_count < 1;
end loop;
end;