【问题标题】:Create coalition combinations with Javascript array使用 Javascript 数组创建联盟组合
【发布时间】:2017-07-28 20:57:31
【问题描述】:

我有一个二维数组,如下所示:

let electionResultsData = [
    ["VVD", "vvd", 20.5, 2504948],
    ["PVDA", "pvda", 19.6, 2340750],
    ["PVV", "pvv", 15.4, 950263],
    ["CDA", "cda", 13.6, 801620],
    ["SP", "sp", 9.8, 909853],
    ["D66", "d66", 6.9, 757091],
    ["GL", "gl", 6.7, 219896],
    ["CU", "cu", 3.2, 294586],
    ["SGP", "sgp", 1.7, 196780],
    ["PVDD", "pvdd", 1.3, 182162],
    ["50PLUS", "50plus", 0.9, 177631],
    ["OVERIG", "overig", 0.2, 51463],
    ["PIRATEN", "piraten", 0.1, 30600],
    ["LP", "lp", 0.1, 3335],
    ["PVDM", "pvdm", 0.1, 3257],
    ["JEZUSLFT", "jezuslft", 0, 0],
    ["ONDRNMR", "ondrnmr", 0, 0],
    ["LOKAAL", "lokaal", 0, 0],
    ["ARTIKEL1", "artikel1", 0, 0],
    ["GEENPEIL", "geenpeil", 0, 0],
    ["VRIJP", "vrijp", 0, 0],
    ["BURGBEW", "burgbew", 0, 0],
    ["FVD", "fvd", 0, 0],
    ["VDP", "vdp", 0, 0],
    ["NIEUWEW", "nieuwew", 0, 0],
    ["DENK", "denk", 0, 0],
    ["STEMNL", "stemnl", 0, 0],
    ["VNL", "vnl", 0, 0]
]

每个数组的第一个值是政党的大写名称,第二个值是小写名称,第三个值是得票百分比,第四个值是得票数。仅供参考,这些是荷兰政党。

我现在要做的是建立一个系统来计算可能的联盟。只有政党在议会中获得超过 75 个(至少 76 个)席位,才能实现政党之间的联盟。我用这段代码做了一个循环来遍历上面的数组:

/**
 * Form coalitions of not more than six parties that exceed 75 parliament seats
 */
formCoalitions(electionResultsData) {
    let coalitions = [];
    let maxNumberOfCoalitionParties = 6;

    // Main loop to check all possible coalitions (28 parties * 28 = 784)
    for (let i = 0; i < 784; i ++) {
        let coalitionSeats = 0;
        let partySeats = 0;
        let coalitionParties = [];
        let coalition = [];

        // The inner loop to generate a combination/coalition
        for (let i = 0; i < electionResultsData.length; i++) {
            // Check if a coalition has formed yet
            if (coalitionSeats < 76) {
                partySeats = (150 / 100) * electionResultsData[i][2];
                coalitionSeats += partySeats;
                coalitionParties.push(electionResultsData[i][0]);

                // If coalition has formed
                if (coalitionSeats > 75) {
                    // Push data into a second dimension coalition array
                    coalition[0] = parseInt(coalitionSeats);
                    coalition[1] = coalitionParties;

                    // Check if the generated coalition array already exists
                    let coalitionsStringified = JSON.stringify(coalitions);
                    let coalitionStringified = JSON.stringify(coalition);
                    let coalitionExists = coalitionsStringified.indexOf(coalitionStringified);

                    if (coalitionExists === -1) {
                        coalitions.push(coalition);
                    }
                }
            }
        }
    }

    // Loop through the coalitions (the charts will be drawn here)
    for (let i = 0; i < coalitions.length; i++) {
        console.log(coalitions[i]);
    }
}

问题是,这段代码只生成一个可能的联盟,而不是所有可能的联盟。我需要以某种方式存储已生成的组合并再次运行循环而不生成相同的联盟。循环必须继续这样做,直到所有可能的不超过六个政党的联盟都已生成。之后,循环就可以停止了。

解决这个问题的最佳方法是什么?

【问题讨论】:

  • 有一点intricate system 可以将席位分配给各方。你想加入这个吗?您现在似乎在使用小数席位。
  • 不一定要合并。我解释它的方式就是我被告知如何制作它的方式(:
  • 请不要转发您的问题;我们的社区经常认为这是在纠缠。我们现在必须将您的帖子作为副本关闭,多个已关闭的问题可能会导致问题被禁止。如果您想提请注意您的问题,请参阅Getting attention for unanswered questions?
  • 请不要将问题编辑为与您的其他问题相同。答案取决于您最初提出问题的方式
  • 现在你可以更新数据了... ;)

标签: javascript arrays


【解决方案1】:

我提出以下建议。这假设各方按票数排序。

function* coalitionize(data, maxParties) {
  if (maxParties < 1)  return;
  var coalition = Array(maxParties).fill(0),
      fixedSoFar = 0;
  while (coalition[0] < data.length) {
    let actualCoalition = coalition.slice(0, fixedSoFar + 1);
    if (numSeats(data, actualCoalition) > 75) {
      yield actualCoalition.map((i) => data[i]);
      coalition[fixedSoFar]++;
    } else if (fixedSoFar < maxParties - 1) {
      // add a party to the coalition, simply the next party
      fixedSoFar++;
      coalition[fixedSoFar] = coalition[fixedSoFar - 1] + 1;
    } else {
      // cannot add more parties; quit this approach
      fixedSoFar--;
      coalition[fixedSoFar]++;
    }
    // check if we don't try anything stupid
    while (coalition[fixedSoFar] >= data.length) {
      // cannot add more parties; quit this approach
      fixedSoFar--;
      coalition[fixedSoFar]++;
    }
  }
}

function numSeats(data, coalition) {
  return coalition
    .map((i) => data[i][2] * (150 / 100))
    .reduce((a, v) => a + v, 0);
}

这使用了一个Generatoryield 的联盟,一旦被发现就会被发现。它稍微简化了其余代码,因为联盟不需要存储在数组中。 Browser support 不错,但 IE 不支持。如果需要,您当然可以将其更改为使用数组。

请注意,联盟中的政党数量是此处的一个参数,因此您可以根据需要对其进行调整。全局的想法是这个函数只考虑最小的联盟。也就是说,当VVD、PVDA和PVV可以组成一个联盟的时候,那么那些家伙加上CDA也是一个联盟,但是我们不考虑那些。现在,我们从单一政党联盟开始。如果有效,那就太好了,报告联盟并继续下一个政党。如果没有,如果可以的话,请在联盟中增加一个政党。如果我们不能,请删除最后添加的一方并继续尝试下一方。

我们可以在数组中跟踪我们正在尝试的各方,这是上面coalitionize函数中的coalition。数组的长度是可以参加联盟的最大政党数。每个值都是参与方数组中的索引,指示我们目前正在为该插槽尝试哪个参与方。为了跟踪数组中实际有多少参与方,我们有变量fixedSoFar。从技术上讲,您可以不使用此变量并执行一系列数组操作(pushpop),但这对我来说似乎更清晰、更快。

我在小提琴中实现了一个稍微完整的demo。它报告在页面上以文本形式找到的联盟,并显示该联盟拥有多少(部分)席位。

编辑。如果输入数据是按票数排序的,那么算法就不需要像我最初想象的那样考虑尽可能多的潜在联盟。我在updated demo 中实现了这一点。

唯一的变化是外部 while 循环中的最后一个 else 子句变为以下内容。

do {
  fixedSoFar--;
  if (fixedSoFar < 0)  return;
    coalition[fixedSoFar]++;
} while ((fixedSoFar === maxParties - 2 &&
    coalition[fixedSoFar] === coalition[fixedSoFar + 1]) ||
    coalition[fixedSoFar] + 1 === coalition[fixedSoFar + 1]);

这里的关键见解是,当一个联盟没有足够席位有效时,我们可以排除所有保证席位更少的联盟,因为要考虑的政党仅在当前考虑的政党之后出现。

这对考虑的潜在联盟数量产生了巨大影响。

【讨论】:

  • 目前正在发生的meta discussion:您已经多次表示我的回答对您没有帮助。你介意解释为什么不吗?我很乐意更新我的答案,以便对您有所帮助。
  • 只是confirmed Nina's code 给出与此答案中的代码相同的结果。我还设置了一个jsperf,这表明,有趣的是,Nina 的代码在您的另一个问题中的数据上表现更好,而我的代码在这个问题中的数据上表现更好(很多)。 Nina 的代码返回一个数组,而我的代码使用生成器,因此可能会改变结果。很有趣。
  • 我猜 Nina 的代码现在移到了here
  • 是的,没错。我记得当我在比此处发布的更大的阵列上运行浏览器时,有人回答我的浏览器完全崩溃。 Nina 的代码运行得非常快,因此我选择了那个。不过,有趣的是看到不同阵列的性能差异。
【解决方案2】:

如果计数小于最大项目数和座位总和,您可以使用搜索和测试。

function getCombinations(array, sum, max) {

    function fork(i, t) {
        var s = t.reduce(function (r, a) { return r + a[2]; }, 0);
        if (s >= sum) {
            result.push([s, t.map(function (a) { return [a[1], a[2]]; })]);
            return;
        }
        if (i < array.length && t.length < max) {
            fork(i + 1, t.concat([array[i]]));
            fork(i + 1, t);
        }
    }

    var result = [];
    fork(0, []);
    return result;
}

var electionResultsData = [["VVD", "vvd", 50, 2504948], ["PVDA", "pvda", 40, 2340750], ["PVV", "pvv", 35, 950263], ["CDA", "cda", 33, 801620], ["SP", "sp", 29, 909853], ["D66", "d66", 26, 757091], ["GL", "gl", 26, 219896], ["CU", "cu", 23, 294586], ["SGP", "sgp", 21, 196780], ["PVDD", "pvdd", 21, 182162], ["50PLUS", "50plus", 21, 177631], ["OVERIG", "overig", 20, 51463], ["PIRATEN", "piraten", 20, 30600], ["LP", "lp", 16, 3335], ["PVDM", "pvdm", 15, 3257], ["JEZUSLFT", "jezuslft", 14, 0], ["ONDRNMR", "ondrnmr", 14, 0], ["LOKAAL", "lokaal", 13, 0], ["ARTIKEL1", "artikel1", 11, 0], ["GEENPEIL", "geenpeil", 11, 0], ["VRIJP", "vrijp", 9, 0], ["BURGBEW", "burgbew", 9, 0], ["FVD", "fvd", 8, 0], ["VDP", "vdp", 8, 0], ["NIEUWEW", "nieuwew", 6, 0], ["DENK", "denk", 5, 0], ["STEMNL", "stemnl", 4, 0], ["VNL", "vnl", 2, 0]],
    result = getCombinations(electionResultsData, 76, 6);
	
document.getElementById('out').appendChild(document.createTextNode(JSON.stringify(result, 0, 4)));
&lt;pre id="out"&gt;&lt;/pre&gt;

【讨论】:

  • 只是对措辞的一点评论:这个答案中没有二进制搜索。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-03-17
  • 2015-11-15
  • 2010-09-19
  • 2015-12-13
  • 2017-06-13
  • 2017-11-14
  • 2012-12-23
相关资源
最近更新 更多