【问题标题】:Randomize Unique Named Groups of Arrays with no Repetition of Elements per Grouping随机化唯一命名的数组组,每个分组不重复元素
【发布时间】:2020-05-23 06:26:12
【问题描述】:

我有 149 个小组,由大约 250 个名单中的个人组成。分组(部落)组成是固定的,也就是说每组的3名成员不能互换。我需要创建一个时间表(总共 6 天),将 149 组每天分成 25 组,除了最后一天,条件是每个人每天的出现次数不得超过 1 次。

Here is a copy 我的数据集的虚拟名称。

到目前为止,这是我的 Google Apps 脚本代码:

var ui = SpreadsheetApp.getUi();
var sht = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Tribes');
var schedSht = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Schedule');
var nameSht = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Names');

var tribesArr = sht.getRange(2, 1, sht.getLastRow() - 1, 1).getValues();
var tribesOrder = tribesArr.map(function(t) { return t[0] });
tribesArr = tribesArr.map(function(t) { return t[0] });
var tribes = randomize(tribesArr);
var takenTribes = [];
var tribeSet = [];
var nameSet = [];

var LastDayTribeNum = 24;
var otherDaysTribeNum = 25;
var numDays = 6;

function makeMenu() {
  ui.createMenu('My Menu').addItem('Create Randomized Schedule', 'makeSchedule').addToUi();
}

function makeSchedule() {
  var dayNum = 1;
  while (dayNum < numDays + 1) {
    var remainingTribes = tribes.filter(function(r) { return takenTribes.indexOf(r) < 0; });
    var daySched = fillUpDay(dayNum, remainingTribes);
    tribeSet.push(daySched.tribes);
    nameSet.push(daySched.names);
    dayNum++;
  }
  remainingTribes = tribes.filter(function(r) { return takenTribes.indexOf(r) < 0; });
  if (takenTribes.length < tribes.length) {
    remainingTribes.forEach(swap);
  }
  tribeSet.forEach(function(tribe) {
    schedSht.appendRow(tribe);
  });
  nameSet.forEach(function(name) {
    nameSht.appendRow(name);
  });
}

function fillUpDay(revDay, remTribes) {
  var sched = {};
  var namesToday = [];
  var tribesToday = [];
  var currIdx = 0;
  var tribeMaxNum = (revDay === numDays) ? LastDayTribeNum : otherDaysTribeNum;
  while (tribesToday.length < tribeMaxNum && currIdx < remTribes.length) {
    var row = tribesOrder.indexOf(remTribes[currIdx]) + 2;
    var currNames = sht.getRange(row, 2, 1, 3).getValues()[0];
    var nameFound = false;
    //check if name of tribe already in today's list
    for (var t = 0; t < 3; t++) {
      if (namesToday.indexOf(currNames[t]) !== -1) { 
        nameFound = true;
      }
    }
    if (!nameFound) {
      //if names of all three not in the list, add all three to today's list and remove tribe from list of unscheduled tribes
      namesToday = namesToday.concat(currNames);
      tribesToday.push(remTribes[currIdx]);
      takenTribes.push(remTribes[currIdx]);
      currIdx++;
    } else {
      //go to next tribe
      currIdx++;
    }
  }
  sched.tribes = tribesToday;
  sched.names = namesToday;
  return sched;
}

function swap(tribeToSwap) {
  var row = tribesOrder.indexOf(tribeToSwap) + 2;
  var namesInTribe = sht.getRange(row, 2, 1, 3).getValues()[0];
  var len = nameSet.length;
  var idx = 0;
  var found = checkInSet(namesInTribe, nameSet[idx]);
  var swapped = false;
  while (!swapped && idx < len - 1) {
    if (!found) {
      // try swap
      var tempNameArr = nameSet[idx];
      tempNameArr.slice(3);
      tempNameArr.push(namesInTribe);
      var removedNames = nameSet[idx];
      removedNames.slice(0, 3);
      var lastNames = nameSet[len - 1];
      var good = true;
      for (var c = 0; c < 3; c++) {
        if (lastNames.indexOf(removedNames[c]) > -1) {
          good = false;
        }
      }
      if (good) {
        // swap
        tribeSet[idx].slice(1);
        nameSet[idx] = tempNameArr;
        tribeSet[len - 1].push(tribeToSwap);
        nameSet[len - 1].push(removedNames);
        swapped = true;
      }
    } else {
      // try next day
      idx++;
      found = checkInSet(namesInTribe, nameSet[idx]);
    }
  }
}

function checkInSet(names, setNames) {
  var found = false;
  for (var idx = 0; idx < names.length; idx++) {
    if (setNames.indexOf(names[idx]) > -1) {
      found = true;
    }
  }
  return found;
}

function randomize(array) {
  //https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array
  var currentIndex = array.length,
    temporaryValue, randomIndex;
  while (0 !== currentIndex) {
    randomIndex = Math.floor(Math.random() * currentIndex);
    currentIndex -= 1;
    temporaryValue = array[currentIndex];
    array[currentIndex] = array[randomIndex];
    array[randomIndex] = temporaryValue;
  }
  return array;
}

我所做的是每天创建一个数组并检查当前索引的相应组(部落)的成员是否存在于当天的名称数组中,如果找到则转到下一个索引直到当天的配额( 25) 到达。如果当天没有找到姓名,该部落将被标记为已被占用,并且将不再包含在随后的时间表中。结果将附加到各自的工作表中。如果因为那天已经出现了一个名字而导致部落不匹配,我会尝试在另一天交换它。

代码大部分时间都在运行(大约 5 秒内完成)。其他时候,它会运行几分钟,我会取消它。但是,我从来没有在任何跑步中得到我需要的东西。我将在日程表中得到 149 个部落,但是当我检查名称时,我没有得到 447 个名称(每天没有重复)。我无法确定在哪里调整算法。我希望有人至少可以给出提示,更好的解决方案。请原谅乱码。

【问题讨论】:

    标签: javascript arrays google-apps-script random google-sheets


    【解决方案1】:

    我发现错误来自swap() 函数。我这样修改了整个函数:

    function swap(tribeToSwap) {
      var row = tribesOrder.indexOf(tribeToSwap) + 2;
      var namesInTribe = sht.getRange(row, 2, 1, 3).getValues()[0];
      var len = nameSet.length - 1;
      var tempNames = [];
    
      for (var days = 0; days < nameSet.length; days++) {
        var currNameSet = nameSet[days];
        tempNames[days] = currNameSet.concat(namesInTribe);
      }
      var noDupIdx = [];
      tempNames.forEach(function (n) {
        if (!checkDuplicate(n)) { noDupIdx.push(tempNames.indexOf(n)) };
      });
    
      var swapped = false;
      for (var idx = 0; idx < noDupIdx.length; idx++) {
        var currDay = noDupIdx[idx];
        var allOrigNames = [nameSet[currDay], nameSet[len]];
        var removedNames = allOrigNames[0].slice(0,3);  //remove first 3 names
        var newSoonNames = tempNames[currDay].slice(3); //add the names in swapping tribe
        var newLateNames = allOrigNames[1].concat(removedNames);
    
        if (!checkDuplicate(newLateNames)) {
          nameSet[currDay] = newSoonNames;
          nameSet[len] = newLateNames;
          var swappedTribe = tribeSet[currDay][0];
          tribeSet[currDay].slice(1).concat(tribeToSwap);
          tribeSet[len] = tribeSet[len].concat(swappedTribe);
          swapped = true;
          break;
        }
      }
      success.push(swapped);
    }
    

    我添加了一个success 数组,这样当交换失败时(正如我所预料的那样,有时会这样),我只会收到一个关于它的 UI 警报。

    我删除了 checkInSet() 函数并将其替换为 checkDuplicate() 函数。

    function checkDuplicate(arr) {
      var map = {};
      var duplicates = false;
      for (var i = 0; i < arr.length; i++) {
        if (map[arr[i]]) {
          duplicates = true;
          break;
        }
        map[arr[i]] = true;
      }
      return duplicates;
    }
    

    现在已经足够了。不再永远运行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-04-18
      • 1970-01-01
      • 2015-07-11
      • 1970-01-01
      • 2022-01-16
      • 2012-05-28
      • 1970-01-01
      相关资源
      最近更新 更多