在我们的团队办理登机手续后,我们会与(n)参与者进行协调会议,讨论各种(唯一命名的)主题。
目前,人们试图自我组织,我们不是最大化会议的并发性,和/或尽快释放团队成员重返工作岗位。
我们列出了Google Spreadsheet中签到的所有协调会议清单:
+-----------------------------------------------------------+
| Meetings |
+-----------------------------------------------------------+
| Topic A | Topic B | Topic C | Topic D | Topic E |
+-----------------------------------------------------------+
| Billy | JD | David | Tania | JB |
| Mel | Rowan | Emily | David | Fred |
| Tracey | Mike | | Mike | Tania |
| JB | Aaron | | Fred | |
| Luke | Billy | | | |
| Aaron | | | | |
| Michael | | | | |
+-----------------------------------------------------------+
作为一个起点,我有(当然效率很低)代码迭代每次会议并返回没有参与者冲突的对数组。
来自main()
的示例输出以上输入数据(不要担心所有代码如下):
[["Topic A","Topic C"],["Topic A","Topic D"],["Topic B","Topic C"],["Topic B","Topic E"],["Topic C","Topic E"]]
理想情况下,我需要一个解决方案,它可以返回大多数会议所要求的数组,以便立即运行。 使用从{{1}返回的对数组或者使用电子表格中的数据作为输入的完全其他方法。
在我的示例中,它将采用以下形式:
main()
然后我会在Google电子表格中按颜色对这些进行分组,以便工作人员看到。
我试图创建一些代码([[Topic B, Topic C, Topic E],[Topic A, Topic D]]
)来减少基于互斥对的数组,但这是完全错误的。例如:该代码输出:
main2()
...这是不正确的,因为A和B显然有冲突。 (A和C,B和E当然会连续罚款)。
[["Topic A","Topic C","Topic B","Topic E"],"Topic D"]
我正在阅读的有关理解的一些途径如下。我的代表太低了,无法链接这些。有人可能会认为这是一个追求的方向吗?
Hopcroft-Karp算法
感谢您花费任何时间。
对于以下内容,解决方案应正确将会议顺序设置为:
1)W,X,Z
2)V,Y
function onOpen () {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Meeting Tools')
.addItem('Compute Optimal Meeting Order', 'main')
.addToUi();
}
function main() {
var ss = SpreadsheetApp.getActive();
var sss = ss.getActiveSheet();
//var sss = ss.getSheetByName("Sheet3");
var data = sss.getDataRange().getValues();
var ui = SpreadsheetApp.getUi();
var meetings = {};
var pairs = [];
var objKey;
// Structuring data to obj
for (var x=0; x < sss.getLastColumn(); x++) {
for (var y=1; y < data.length; y++) {
if (y==1) {
objKey = data[y][x];
meetings[objKey] = [];
}
else if (data[y][x]) {
meetings[objKey].push(data[y][x]);
}
}
}
var keys = Object.keys(meetings).sort();
var loc;
// Starting with "A"
for (var m in meetings) {
if (!meetings.hasOwnProperty(m)) continue;
Logger.log("-----------------------------");
Logger.log("SCANNING: " + m);
Logger.log("-----------------------------");
loc = keys.indexOf(m);
// check A, B, C, D, E
for (var m2 = 0; m2 < keys.length; m2++) {
var pointer = (m2 + loc) % keys.length;
// DO NOT CHECK SAME MEETING
if (keys[pointer] == m) {
Logger.log("||||||||| WE ARE COMPARING OURSELVES: (" + keys[pointer] + "/" + m + "). SKIPPING!");
continue;
}
// FOR EACH PARTICIPANT OF CURRENT MEETING
for (var p = 0; p < meetings[m].length; p++) {
Logger.log("");
Logger.log(" >>> COMPARING " + keys[pointer]);
Logger.log(" >>> >>> " + meetings[m][p] + " " + (p+1) +"/"+meetings[m]. length);
// SEEK THE LOCATION OF THE MEETING PARTICIPANT IN THE NEXT MEETING
var testIndex = meetings[keys[pointer]].indexOf(meetings[m][p]);
// IF THE MEETING PARTICIPANT IS ALSO IN THE NEXT MEETING
if (testIndex > -1) {
Logger.log("We have a match! Meeting "+ m + " has participant " + meetings[m][p] + " which unfortunately clashes with meeting " + keys[ pointer]);
Logger.log("Therefore Meeting " + m + " cannot run at the same time as " + keys[pointer]);
Logger.log("We need to bail out of compairing against: " + keys[pointer ]);
// WE BAIL OUT AS WE HAVE A CLASH
break;
}
// IF THE MEETING PARTICIPANT IS NOT IN THE NEXT MEETING AND WE HAVE CHECKED EVERYONE. WE SHOULD BE GOOD.
else if (testIndex == -1 && p+1 == meetings[m].length) {
Logger.log("This looks to be clear!!! Adding to pair group.");
pairs.push([m,keys[pointer]]);
}
}//loop
} //loop
} //obj_loop
// Logger.log("FINAL TALLY: " + JSON.stringify(pairs));
Logger.log("FINAL TALLY (CLEANED): " + JSON.stringify(removeRepeats(pairs.sort()) ));
// ui.alert(JSON.stringify(removeRepeats(pairs.sort())));
// ss.toast(JSON.stringify(removeRepeats(pairs.sort())));
// debugger;
return removeRepeats(pairs.sort());
}
function main2(array) {
// DEBUG
// array = [["A","C"],["A","D"],["B","C"],["B","E"],["C","E"]];
// array = [["A","C"],["A","D"],["B","C"],["B","E"],["C","E"]];
array = main();
var holdingArr = [];
for (var i = array.length - 1; i >= 1; i--) {
//DEBUG
//var pair = ["Z","X"];
var pair = array[i];
if (arrayPairCheck([array[0]], pair)) {
holdingArr.push(pair);
array.pop(i);
}
else {
array[0] = array[0].concat(array[i]);
array.pop(i);
}
} //loop
if (holdingArr && holdingArr.length > 0) {
for (var j=0; j < holdingArr.length; j++) {
var checkIndex = holdingMeetingExistsInPair(array[0],holdingArr[j]);
if (checkIndex !== false) {
array.push(holdingArr[j][checkIndex]);
}
} //loop
}
// DEBUG
debugger;
Logger.log(JSON.stringify(array));
}
function holdingMeetingExistsInPair(arrayFirstIndexGroup,holdingArrPair) {
if (arrayFirstIndexGroup && arrayFirstIndexGroup.length > 0) {
if (arrayFirstIndexGroup.indexOf(holdingArrPair[0]) === -1) {
return 0;
}
if (arrayFirstIndexGroup.indexOf(holdingArrPair[1]) === -1) {
return 1;
}
}
return false;
}
function arrayPairCheck(array,pair) {
// DEBUG
// array = [["A","C"],["A","D"],["B","C"],["B","E"],["C","E"]];
// pair = ["Z","X"];
var seen = false;
if (array && array.length > 0) {
for (var i=0; i < array.length; i++) {
array[i].map(function(item,item2) {
if (item === pair[0] || item === pair[1]) {
seen = true;
return true;
}
});
} //loop
}
if (seen) { return true; } else { return false; }
}
// http://stackoverflow.com/questions/27734661/javascript-arrays-ab-ba
function removeRepeats(list) {
var i;
var b = [];
var _c = [];
for (i = 0; i < list.length; i++) {
var a = list[i].sort();
var stra = a.join("-");
if(_c.indexOf(stra) === -1) {
b.push(a);
_c.push(stra);
}
}
return b;
}
答案 0 :(得分:1)
这肯定是一个有趣的问题。我的方法是使用矩阵运算来确定哪些主题不会发生冲突。 第一步是创建一个矩阵来表示没有碰撞的对,类似于你的方法。
因此对于此示例数据集
+-----------------------------------------------------------+
| Meetings |
+-----------------------------------------------------------+
| Topic A | Topic B | Topic C | Topic D | Topic E |
+-----------------------------------------------------------+
| Billy | JD | David | Tania | JB |
| Mel | Rowan | Emily | David | Fred |
| Tracey | Mike | | Mike | Tania |
| JB | Aaron | | Fred | |
| Luke | Billy | | | |
| Aaron | | | | |
| Michael | | | | |
+-----------------------------------------------------------+
我们可以说主题A没有与主题C和主题D发生冲突,我们可以在矩阵/数组中表示它,如下所示:
+---------------------------------------------------------------+
|Topic A|Topic B |Topic C|Topic D|Topic E|
Topic A |1 |0 |1 |1 |0 |
然而,主题C和主题D确实发生了冲突,我们将在一分钟内完成。 同样,我们填写剩余的矩阵,如此
Unique Pair Matrix
TopicA TopicB TopicC TopicD TopicE
TopicA 1 0 1 1 0
TopicB 0 1 1 0 1
TopicC 1 1 1 0 1
TopicD 1 0 0 1 0
TopicE 0 1 1 0 1
正如您可以从此表主题中注意到的,C行的0对应于主题D列,表示冲突。另外,请注意对角线设置为1,因为相同的会议不能相互冲突(对于下一步,我们需要它为1)。
接下来,只需将矩阵相乘,得到一个如下所示的矩阵。 理想情况下,您将转置矩阵然后相乘,在这种情况下,您不需要(方阵)
Number of Unique Pair Matrix
Topic A Topic B Topic C Topic D Topic E
Topic A 3 1 2 2 1
Topic B 1 3 3 0 3
Topic C 2 3 4 1 3
Topic D 2 0 1 2 0
Topic E 1 3 3 0 3
此矩阵表示会议不会彼此冲突的次数。因此,对于会议A和C,他们不会发生冲突两次,一次是从第1行计算而另一次是第3行。同样,对于主题&#34; D&#34;和&#34; A&#34;,我们得到一个值2.但是A的总唯一对(用对角线值表示)是3,这意味着会议C和D有冲突,因为它们只与A形成两次唯一对
在第2行中,您会注意到,主题B,C,E共有3对独特的对,这代表了可以一起举行的会议次数最多以及可以举行哪些会议。
使用此功能,您可以优先处理最多会议次数,然后再进行剩余会议。 以下代码就是这样:
function onOpen () {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Meeting Tools')
.addItem('Compute Optimal Meeting Order', 'Jmain')
.addToUi();
}
function Jmain() {
var ss = SpreadsheetApp.getActive();
var sss = ss.getActiveSheet();
var sss = ss.getSheetByName("Sheet2");
var data = sss.getRange(2,1,sss.getLastRow()-1,sss.getLastColumn()).getValues();
//This the matrix that will hold data for unique pairs
var shadowRel = []
var headers = data[0].slice()
data = transpose(data)
var shadowRel = []
for (var i = 0 ; i < data.length ; i ++){
shadowRel[i] = headers.slice() //Each Row is set Headers values to begin with
for (var j = 1 ; j < data[i].length ; j++){
var found = false
for(var k = 0 ; k < data.length ; k++){
if(k != i) {
if (data[k].indexOf(data[i][j]) != -1 && data[i][j] != ""){
//
shadowRel[i][k] = 0 // Whenver there is a clash the martix is set to zero
found = true
}
}
}
}
}
shadowRel = mMutliply (shadowRel) //Matrix Multiplication
sss = ss.getSheetByName("Sheet3")
sss.getRange(2,2,shadowRel.length,shadowRel[0].length).setValues(shadowRel) //Set the values of multiplied martix in sheet 3
var ui = SpreadsheetApp.getUi()
var meetingOrder = mostNoMeetings(shadowRel,headers)
Logger.log("Meeting Order: ")
Logger.log(meetingOrder)
var prompt = "Meeting Order: "
for (i = 0 ;i <meetingOrder.length; i++){
prompt += "\n"+(i+1)+") "+ meetingOrder[i]
}
ui.alert(prompt)
}
// Transpose the data so as to check match in each row.
function transpose(arr) {
return Object.keys(arr[0]).map(function (c) {
return arr.map(function (r) {
return r[c];
});
});
}
function mMutliply (arr){ //Number of row and column needs to be equal
var mMutRes = []
for (var i = 0; i < arr.length ; i++)
for(var j = 0; j<arr[0].length ; j++){
if(arr[i][j] !== 0)
arr[i][j] = 1 // Remaining Text is converted to 1, has no class
}
var ss = SpreadsheetApp.getActive()
var sss = ss.getSheetByName("Sheet3")
sss.getRange(arr.length+5,2,arr.length,arr[0].length).setValues(arr) //Set Value of unique pair to sheet 3
for (var i = 0; i < arr.length ; i++){
mMutRes[i] = []
for(var j = 0; j<arr[0].length ; j++){
var sumProd = 0
for(var k = 0 ; k < arr.length ; k ++)
{
sumProd += (arr[k][j] * arr[i][k])
}
mMutRes[i][j] = sumProd
}
}
return mMutRes
}
function mostNoMeetings(shadowRel,headers){
var UsedIndex = []
var MaxColIndex = []
var counter = 0
var MeetingGroups = []
for(var maxNo = shadowRel.length ; maxNo > 0 ; maxNo--) { //Look for most repeated pair, max possible is the numbers of topics/meetings at same time
var maxFound = false
for (var i = 0 ; i < shadowRel.length; i++){
for(var j= 0; j< shadowRel[0].length; j++){
if(i != j && UsedIndex.indexOf(i) == -1 && UsedIndex.indexOf(j) == -1) // Eliminate row / column corresponding to topics/Meetings already scheduled.
if(shadowRel[i][j] == maxNo){
MaxColIndex[counter] = j
counter++
maxFound = true
}
}
if(maxFound){
var temp = []
temp.push(headers[i])
for (var k in MaxColIndex){
if(temp.length < maxNo)
temp = temp.concat(headers[MaxColIndex[k]])
else
MaxColIndex.splice(k)
}
MaxColIndex[counter] = i
MeetingGroups.push(temp)
UsedIndex = UsedIndex.concat(MaxColIndex)
//Logger.log(UsedIndex)
MaxColIndex = []
counter = 0
maxFound = false
}
}
}
// Incase any are remaining meetings that didnt form pairs which have to be held independently
for ( i = 0 ; i < shadowRel.length; i++){
if(i != j && UsedIndex.indexOf(i) == -1 && UsedIndex.indexOf(j) == -1){
MeetingGroups.push(headers[i])
}
}
return MeetingGroups
}
最后,确保您的数据在表2中,而表3为空,上述矩阵的输出和乘法矩阵将发布到表3,以便直观地显示发生的情况。
希望这有帮助!