配对号码,必须一次配对,并且不能与自己或小组成员配对

时间:2018-09-01 14:32:55

标签: javascript arrays algorithm sorting

我有一个由8个数字组成的数组1-8

arr = [1, 2, 3, 4, 5, 6, 7, 8]

数组中的每个数字都是一个组的一部分

1 & 2 - group a
3 & 4 - group b
5 & 6 - group c
7 & 8 - group d

我需要做的是将数组中的每个数字与同一数组中的另一个数字进行匹配,但是它们不能在同一组中

  • 1&2可能不匹配1或2
  • 3&4可能与3或4不匹配
  • 5&6可能与5或6不匹配
  • 7和8可能与7或8不匹配

条件

  1. 可能无法预先确定,因为输入相同,必须有不同的解决方案
  2. 没有重复配对,例如,如果2个配对的8、4也不能配对8
  3. 它们必须一次配对,并且有能力完成一些配对,并在以后再次配对以完成更多配对
  4. 配对无法永久重置
  5. 所有情况下,一对配对不能同时运行。例如,如果2与3配对,则3不能总是与2配对。如果不时发生这种情况是可以接受的,但是它不是预期的功能。
  6. 我们不能假设配对将以任何特定顺序进行。例如,第一对可以是1,也可以是7或3,依此类推。任何数量的数字都可能需要随时配对。

我的问题是,如果您以正确的顺序选择它,您可能会陷入最后一个数字,而剩下的唯一配对就是与自身或它的同伴配对。

我想在这里强调一个条件,因为它总是在答案中被忽略。我希望一次配对。这意味着您应该能够尽可能地将每个配对配对。我希望能够在第0天进行配对,然后可以在第1天,第2周或2750年回来进行第二次配对。这是必要的。每个配对都必须完全独立,并且最后一个数字必须仍然能够进行有效配对。

示例...

6 with 8
7 with 6
5 with 7
3 with 5
8 with 4
2 with 3
4 with 2
1 with _

此顺序只剩下1个,只有1个。

无论最后一个数字如何总是能配对,我该怎么做?

更新:我在答案部分添加了一个相当不错的解决方案。如果您仍在努力理解我要完成的工作,请尝试在答案部分中阅读我的答案。由于我找到了当前可用的答案,因此随附的代码已过时。

下面的过时代码

 function selectGiftee(req, res, db){
        const {user_id, group_id} = req.body
        db.select('name', 'user_id', 'giftee_id', 'group_id').from('users')
        .then (data => {
            if (data.length) {
    // only sending available giftees

                const taken = [];
                const fullList = [];
                let available = [];

    // figure out which giftees are taken
                data.forEach( user => {
                    if (user.giftee_id !== null){
                        taken.push(user.giftee_id)
                    }
                    if (user.group_id === group_id) {
                        taken.push(user.user_id)
                    }
                })
    // add all giftees to a list
                data.forEach( user => {
                    fullList.push(user.user_id)
                })

    // only add available giftees to the available list
                available = fullList.filter(val => !taken.includes(val));

    // respond with only giftees that are not taken
                res.json(available)

4 个答案:

答案 0 :(得分:1)

让我们的小组成为一长串名单:

1 2|3 4|5 6

现在让我们在中间将其分开,然后将一个部分移到另一个下面:

1 2|3
4|5 6

现在,每个元素都有一对(每个列),而不是来自组本身,您可以通过将所有列附加到一个来将其变成连续列表:

(1 -> 4) -> (2 -> 5) -> (3 -> 6) -> 

现在要获得不同的组合,我们只需要对组数组和组本身进行随机组合即可。

// stolen from https://stackoverflow.com/questions/6274339/how-can-i-shuffle-an-array
function shuffle(a) {
    for (let i = a.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [a[i], a[j]] = [a[j], a[i]];
    }
    return a;
}


const groups = [[1, 2], [3, 4], [5, 6], [7, 8, 9]];

groups.forEach(shuffle);
shuffle(groups);

console.log("shuffled:", groups.join(" | "));

const list = [].concat(...groups);

console.log("list:", list);

// Now pair every element by matching the first and the second half:
const pairs = [];

for(let i = 0; i < Math.floor(list.length / 2); i++) {
  pairs.push([
   list[i],
   list[i + Math.floor(list.length / 2)]
  ]);
 }

 if(list.length % 2)
   pairs.push([list.pop()]);
 console.log("pairs:", pairs.join(" | "));

 const result = [].concat(...pairs);

 for(let i = 0; i < result.length; i++)
   console.log(result[i] + " -> " + result[(i + 1) % result.length]);

答案 1 :(得分:1)

这是我想出自己的问题的方法。我要做的是首先将这些组分成两个单独的组。这意味着组a和b在元组1中,组c和d在元组2中。

第二秒我添加了一个加权系统。因此,当尝试制作一对时,我会收集已被使用的一对中的所有辅助编号,然后将权重添加到它们的组中。例如,如果4已与6配对,则组c的权重为+1。不过,这只是加权的第一步。

现在,在当前示例中,4已经与6配对,因此组c的权重为1。现在我们要配对3。3与4属于同一组,即b组。因此,现在第3组将查看4,并看到它已经有一对,即6。6是元组2的一部分,因此现在c和d组的权重都加了+10。这使c组具有11,d具有10。

编辑:添加了这两个条件是为了清除我发现的一些较不常见的错误。首先,我为尚未配对的任何数字添加了负权重(-1)。这样就可以在没有对的数字之前选择没有对的数字。我之所以必须这样做,是因为我仍然很少遇到被一个无法配对的数字卡住的情况。其次,我改变了处理同一组数字的方式。以前,我只是从可用列表中删除了它们。但是,如果他们的小组体重最低,则会引起问题。该算法建议从该组中选择一个数字,因为它的权重最低,但是该组中没有数字,因此会导致死锁。现在,我将20个权重添加到该数字所在的组中,以使其永远不会成为最低权重。

因此,现在我们设定了权重,而3仍在尝试配对。我们查看了所有权重后,发现组a和b的得分为0,c的得分为11,d的得分为10。3是b组的一部分,并且与self的配对被特定地阻止,因此这是不可能的,因此这只剩下a组可供选择,因此3将与1或2配对。

这是我能够找到的唯一一种方法,该方法可以让我按需形成1对。下面是我的代码,可能会有点混乱,因为我只是将其直接从程序中拉出来,但是如果有人需要澄清它的工作方式,我将很乐于解释。

function chooseGiftee(avail){
    const int = avail.length;
    const index = Math.floor((Math.random() * int) + 1);
    return avail[index-1];
}

function getCandidates(weights){
        return candidates;
}



function selectGiftee(req, res, db){
    const {user_id, spouse_id, group_id} = req.body;
    if (!user_id || !spouse_id || !group_id){
        return res.status(400).json('Missing Credentials')
    }

    db.select('user_id', 'spouse_id', 'giftee_id', 'group_id').from('users')
    .then (data => {

        if (data.length) {
// only sending available giftees


            let newGiftee;
            const taken = [];
            const fullList = [];
            let available = [];
            let filteredAvailable = [];
            let nullCount = 0;
            let nullArr = [];
            let a = 0;
            let b = 0;
            let c = 0;
            let d = 0;

//for the love of god man please refactor all this stuff!!!!!!!


// figure out which giftees are taken and set weight for already picked groups 
            data.forEach( user => {

                if (user.giftee_id === null){
                    switch (user.user_id){
                        case 1:
                        case 2:
                            a--;
                            break;
                        case 3:
                        case 4:
                            b--;
                            break;
                        case 5:
                        case 6:
                            c--; 
                            break;
                        case 7:
                        case 8:
                            d--;
                            break;
                    }
                }

                if (user.giftee_id !== null){
                    taken.push(user.giftee_id);
                }
                switch (user.giftee_id){
                        case 1:
                        case 2:
                            a++;
                            break;
                        case 3:
                        case 4:
                            b++;
                            break;
                        case 5:
                        case 6:
                            c++; 
                            break;
                        case 7:
                        case 8:
                            d++;
                            break;
                    }

                if (user.group_id === group_id) {
                    switch(user.giftee_id){
                        case 1:
                        case 2:
                        case 3:
                        case 4:
                            a += 10;
                            b += 10;
                            break;
                        case 5:
                        case 6:
                        case 7:
                        case 8:
                            c += 10;
                            d += 10;
                            break;
                    }
                    switch(user.group_id){
                        case 1:
                            a += 10;
                            break;
                        case 2:
                            b += 10;
                            break;
                        case 3:
                            c += 10;
                            break;
                        case 4:
                            d += 10;
                            break;
                    }
                }
            })


// add all giftees to a list
            data.forEach( user => {
                fullList.push(user.user_id)
            })

// only add available giftees to available list

            available = fullList.filter(val => !taken.includes(val));


// Choose from what is available based on groupWeight
            let lowWeight = Math.min(a, b, c, d);
            let candidates = [];
            if(lowWeight === a){
                    candidates.push(1, 2);
            }
            if(lowWeight === b){
                    candidates.push(3, 4);
            }
            if(lowWeight === c){
                    candidates.push(5, 6);
            }
            if(lowWeight === d){
                    candidates.push(7, 8);
            }

            filteredAvailable = available.filter(val => candidates.includes(val));



// check if there is three or less choices left, and if so we need to prevent a deadlock

            if (nullCount <= 4){
                filteredAvailable = filteredAvailable.filter(val => !nullArr.includes(val))
            }
            newGiftee = chooseGiftee(filteredAvailable);

答案 2 :(得分:1)

如果组中的每一个都精确地包含2个值(如果不太合适,我会解决),那么我们可以继续在奇数索引处打数字,以在它们之间继续创建墙。例如

[ [1,2], [3,4], [5, 6], [7, 8] ]

[1, 2]开头的初始数组

接下来处理-> [3, 4]在索引1处打孔,在索引3处进行4次打孔,因此它变成[1, 3, 2, 4]

现在继续处理下一个([5,6])并执行相同的操作,因此它变为:[1, 5, 3, 6, 2, 4]

然后处理下一个并继续。

现在,如果有可以任意长度的组!所以,这就是问题所在(Bang On!这就是我来这里的原因)

首先,我们需要了解何时(在哪种情况下)可以完成!如果存在一个长度为L的组,而所有其他组的长度之和为M,则 N - M should be <= 1

为什么?

让我们假设一个小组有3个成员,为了将他们分开,您至少需要2个墙。因此,所有其他组合并的长度必须为2

因此,如果现在满足条件,我们需要创建对(因为现在可以)

  1. 第一步,根据长度对desc组进行排序
  2. 我们需要从第一个开始,但是我们需要继续 如果第二组没有破坏第一组,则为奇数索引 完全。

假设为:

[[1, 2, 3, 4, 5, 6], [7,8,9], [10, 11, 12], [...], [.....]]

由于第一组的长度为6,并且需要5个元素才能完全消除它,因此下一组[7, 8, 9]的长度为3,因此肯定需要{{1} }更多元素,因此当我们将当前组组件放置在2索引之后开始处理下一个组时,我们需要开始放置在1, 3, 5

  1. 一旦解散了第一个数组,我们就可以从索引7, 9, ...开始,然后 保持奇数索引。

我可以按需生成有效的代码,但是一旦我们清楚必须要做的编码部分,我们所有人都可以轻松完成。主要集中在算法部分。但是,如果有帮助,我可以编写代码!

添加的实施示例

1

好吧,现在要有不同的对组合(显然是在可能的一对中),您只需要管理var groups1 = [ [7, 8, 9], [10, 11], [1, 2, 3, 4, 5, 6], [12, 13] ], groups2 = [ [1, 2], [3, 4], [5, 6], [7, 8] ]; function punchGroups(groups) { groups.sort((a, b) => b.length - a.length); let firstLen = (groups[0] || []).length, oic = 0, //odd index increment counter covered = false; //covered the largest array flag return groups.reduce((acc, e) => { if (!acc.length) { return acc.concat(e) } e.forEach((n, i) => { let idx = (covered ? (i * 2) : (oic++) * 2) + 1; acc.splice(idx, 0, n); covered = oic >= (firstLen - 1) }) return acc; }, []); } console.log('Groups: ', groups2); console.log('Pairs: ', punchGroups(groups2)); console.log('-------------------------------'); console.log('Groups: ', groups1); console.log('Pairs: ', punchGroups(groups1));和所有可能组合中的组成员(只需使用递归即可拥有所有组合),并且处理每个具有所有可能的对。您可以生成所有输出,并每天使用索引或某种因素从中选择一个输出,这会使您的调用有所不同,可能是时间戳记,并将其计算为所有可能的输出数组的索引范围内的数字。

答案 3 :(得分:0)

您的数字是图节点,从每个节点到除组邻居之外的所有其他节点都存在边。您需要用n / 2个边缘覆盖所有n个节点,而两个边缘均不共享任何节点。

这种覆盖被称为完美匹配(不是我之前写的 maximum ,但是完美是maximum)

使用import networkx as nx lst = [1, 2, 3, 4, 5, 6, 7, 8] G = nx.Graph() for i in range(0, len(lst)): for j in range(i + 1, len(lst)): if ((i // 2) != (j // 2)): //make edge for items from different pairs G.add_edge(lst[i], lst[j]) #print(G.edges) m = nx.maximal_matching(G) print(m) >>> {(4, 2), (1, 3), (6, 8), (5, 7)} 库的Python示例找到了最大的匹配项(这里也是完美的,但是我们不能总是期望这个事实):

def sum_n(n):
    addition=0
    for x in range(1,2*n,2):
        addition+=x
    return addition
s=sum_n(5)
print(s)