Соединяющиеся номера, должны составлять пары по одному и не могут быть соединены с собой или членом группы - PullRequest
0 голосов
/ 01 сентября 2018

У меня есть массив с 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 без выбора, кроме самого себя.

Что я могу сделать, чтобы это было так, независимо от того, какое последнее число всегда имеет жизнеспособное соединение?

обновление: я добавил довольно хорошее решение в разделе ответов. Если вы все еще пытаетесь понять, чего я хочу достичь, попробуйте прочитать мой ответ в разделе ответов. Прикрепленный код устарел, так как я нашел подходящий для использования ответ.

устаревший код ниже

 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 голосов
/ 08 сентября 2018

Если каждая из групп содержит 2 значения точно (я буду обращаться, если не слишком), то мы можем сохранить числа с нечетным индексом, чтобы продолжать создавать стену между ними. например,

Группы

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

Начинается с [1, 2] исходного массива

обработать следующий -> [3, 4] удар по индексу 1 и 4 по индексу 3, чтобы он стал [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, поэтому определенно ей нужно 2 больше элементов, поэтому, когда мы начнем обработку следующую группу после размещения компонентов текущей группы в индексе 1, 3, 5, мы должны начать размещение в 7, 9, ...

  1. Как только он отклонил первый массив, мы можем начать с индекса 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));

Что ж, теперь, чтобы иметь разные комбинации пар (очевидно, в пределах одной из возможных пар), вам просто нужно управлять groups и членом групп во всех возможных комбинациях (просто используйте рекурсию, чтобы получить все комбо) и обрабатывать каждую иметь все разные возможные пары. Вы можете генерировать все выходные данные и выбирать один из них каждый день с индексом или некоторым фактором, который будет отличать ваш вызов, может быть метка времени, и рассчитать его для числа в пределах диапазона индекса всех возможных выходных массивов.

0 голосов
/ 01 сентября 2018

Давайте рассмотрим наши группы как один длинный список:

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]);
0 голосов
/ 02 сентября 2018

Итак, вот метод, который я придумал, чтобы ответить на мой собственный вопрос. Что я сделал, так это сначала разделил группы на две отдельные группы. Это означает, что группы 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 специально заблокировано, так что это невозможно, поэтому это оставляет только группу А на выбор, так что 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);
0 голосов
/ 01 сентября 2018

Ваши числа - это узлы графа, и ребра существуют от каждого узла ко всем остальным, кроме соседа по группе. Вам нужно покрыть все n узлов с n / 2 ребрами, в то время как ни один узел не используется двумя ребрами. Это покрытие называется идеальное соответствие (не максимальное , как я писал ранее, но идеальное максимальное)

Пример Python с использованием библиотеки networkx находит максимальное совпадение (здесь также идеально, но мы не можем ожидать этого факта всегда):

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)}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...