匹配用户在配对数组中,但避免之前的匹配。

huangapple go评论72阅读模式
英文:

Match users in pair array but avoid previous matches

问题

我会翻译代码部分,以下是翻译好的内容:

/**
 * @see https://stackoverflow.com/a/12646864
 * @param {string[]} array
 */
const shuffleArray = (array) => {
    for (let i = array.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [array[i], array[j]] = [array[j], array[i]];
    }

    return array;
};

/**
 * @see https://stackoverflow.com/a/44996257
 * @param {string[]} array
 */
const pairArray = (array) => {
    return array.reduce(function (result, value, index, array) {
        if (index % 2 === 0) result.push(array.slice(index, index + 2));
        return result;
    }, []);
};

const getRandomUsers = async () => {
    let userIDSet = [];
    const users = await svc.findAll({ enrolled: true });

    if (!Object.keys(users).length) {
        console.log("糟糕!还没有用户报名。真让人沮丧!");
    }

    for (const user of users) {
        userIDSet.push(user.discordId);
    }

    const shuffledUserIDs = shuffleArray(userIDSet);
    const pairedUserIDs = pairArray(shuffledUserIDs);

    return pairedUserIDs;
};

在运行getRandomUsers()之后,有一个方法检查非配对(奇数)元素。如果有的话,通知用户并从匹配数组中删除。

问题是:这些匹配会在一段时间内重复。但我不希望相同的用户再次匹配。

例如:

const userArray = [1, 2, 3, 4, 5, 6];

const shuffleForFirst = shuffleArray(userArray);
const firstMatchmaking = pairArray(shuffleForFirst);
console.log("第一次匹配:", firstMatchmaking);

const shuffleForSecond = shuffleArray(userArray);
const secondMatchmaking = pairArray(shuffleForSecond);
console.log("第二次匹配:", secondMatchmaking);
英文:

I shuffle and pair the user ids as you can see below.

/**
 * @see https://stackoverflow.com/a/12646864
 * @param {string[]} array
 */
const shuffleArray = (array) => {
    for (let i = array.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [array[i], array[j]] = [array[j], array[i]];
    }

    return array;
};

/**
 * @see https://stackoverflow.com/a/44996257
 * @param {string[]} array
 */
const pairArray = (array) => {
    return array.reduce(function (result, value, index, array) {
        if (index % 2 === 0) result.push(array.slice(index, index + 2));
        return result;
    }, []);
};

const getRandomUsers = async () => {
    let userIDSet = [];
    const users = await svc.findAll({ enrolled: true });

    if (!Object.keys(users).length) {
        console.log("DAMN! There are no users enrolled yet. What a bummer!");
    }

    for (const user of users) {
        userIDSet.push(user.discordId);
    }

    const shuffledUserIDs = shuffleArray(userIDSet);
    const pairedUserIDs = pairArray(shuffledUserIDs);

    return pairedUserIDs;
};

After running getRandomUsers(), a method checks for non-pair (odd) elements. If any, notify the user and delete from matchmaking array.

The problem is: These matches will repeat in a certain time. But I don't want the same users to match again.

For example:

const userArray = [1, 2, 3, 4, 5, 6];

    const shuffleForFirst = shuffleArray(userArray);
    const firstMatchmaking = pairArray(shuffleForFirst);
    console.log("First Matchmaking:", firstMatchmaking);

    const shuffleForSecond = shuffleArray(userArray);
    const secondMatchmaking = pairArray(shuffleForSecond);
    console.log("Second Matchmaking:", secondMatchmaking);

答案1

得分: 0

你可以使用循环赛的调度算法,该算法采用前一轮的配对,然后将所有用户向后“移动”一个位置,除了第一个位置,就像这样(列出配对):

以下是执行该轮换的生成函数:

function* pairings(users) {
    for (let round = 1; round < users.length; round++) {
        yield Array.from({ length: users.length / 2 }, (_, i) => [users[i], users.at(-1 - i)]);
        // 执行循环赛轮换,保持第一个选手在原位:
        users = [users[0], ...users.slice(2), users[1]];
    }
}

const shuffleArray = (array) => {
    for (let i = array.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [array[i], array[j]] = [array[j], array[i]];
    }
    return array;
};

// 示例运行:
const userArray = [1, 2, 3, 4, 5, 6];

const shuffled = shuffleArray(userArray);
for (const pairs of pairings(shuffled)) {
    console.log("matchmaking:", JSON.stringify(pairs));
}

如果要消除出现的模式,你还可以打乱配对:

function* pairings(users) {
    for (let round = 1; round < users.length; round++) {
        yield Array.from({ length: users.length / 2 }, (_, i) => [users[i], users.at(-1 - i)]);
        // 执行循环赛轮换,保持第一个选手在原位:
        users = [users[0], ...users.slice(2), users[1]];
    }
}

const shuffleArray = (array) => {
    for (let i = array.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [array[i], array[j]] = [array[j], array[i]];
    }
    return array;
};

// 示例运行:
const userArray = [1, 2, 3, 4, 5, 6];

const shuffled = shuffleArray(userArray);
for (const pairs of pairings(shuffled)) {
    shuffleArray(pairs);
    console.log("matchmaking:", JSON.stringify(pairs));
}
英文:

You could use the the scheduling algorithm for round robin, which takes the previous pairing and then rotates all of the users one "position" except the first one, like this (columns are pairings):

匹配用户在配对数组中,但避免之前的匹配。

Here is a generator function that performs that rotation:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

function* pairings(users) {
    for (let round = 1; round &lt; users.length; round++) {
    	yield Array.from({length: users.length / 2}, (_, i) =&gt; [users[i], users.at(-1-i)]);
        // Perform round-robin shift, keeping first player in its spot:
    	users = [users[0], ...users.slice(2), users[1]];
    }
}

const shuffleArray = (array) =&gt; {
    for (let i = array.length - 1; i &gt; 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [array[i], array[j]] = [array[j], array[i]];
    }
    return array;
};

// Example run:
const userArray = [1, 2, 3, 4, 5, 6];

const shuffled = shuffleArray(userArray);
for (const pairs of pairings(shuffled)) {
    console.log(&quot;matchmaking:&quot;, JSON.stringify(pairs));
}

<!-- end snippet -->

If you want to remove the pattern that emerges, then you could also shuffle the pairings:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

function* pairings(users) {
    for (let round = 1; round &lt; users.length; round++) {
    	yield Array.from({length: users.length / 2}, (_, i) =&gt; [users[i], users.at(-1-i)]);
        // Perform round-robin shift, keeping first player in its spot:
    	users = [users[0], ...users.slice(2), users[1]];
    }
}

const shuffleArray = (array) =&gt; {
    for (let i = array.length - 1; i &gt; 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [array[i], array[j]] = [array[j], array[i]];
    }
    return array;
};

// Example run:
const userArray = [1, 2, 3, 4, 5, 6];

const shuffled = shuffleArray(userArray);
for (const pairs of pairings(shuffled)) {
    shuffleArray(pairs);
    console.log(&quot;matchmaking:&quot;, JSON.stringify(pairs));
}

<!-- end snippet -->

答案2

得分: 0

考虑这样一种情况,你有一个包含4个用户ID的数组。用户ID在索引0处与其他索引的可能配对是:[0,1][0,2][0,3]。一旦使用了其中任何一对,它们就不能再在同一配对排列或任何其他配对排列中出现。

因此,关键的洞察是,如果你有n个用户,你不能有超过n-1个可能的配对排列。例如,如果你有4个用户,只有4-1=3种配对排列:[1,2][3,4][1,3][2,4][1,4][2,3]

正如trincot指出的那样,有一种Circle Method用于找到所有可能的配对排列(例如,在有4个用户时的所有3种可能的配对排列)。

首先,你需要做的是选择一个之前未选择过的配对排列。如果有3种可能的排列,你应该打乱数组[0,1,2]以获取你随机选择的可能排列的顺序。

例如,如果你随机决定使用的排列顺序是[2,0,1],那么从那时开始你唯一需要追踪的就是数组和迄今为止访问到的位置。你不需要提前生成所有可能的配对排列,也不需要追踪你曾经使用过的每一对。

然后,你按照那个顺序找到每个配对排列,当你用尽所有可能的配对排列时,它将变得很明显。

function* rotatedSequence(start, leftRotation, len) {
  for(let i=0; i<len; i++) yield start+(leftRotation+i)%len
}

function getPairingPermutation(n, i) {
  return pair([0, ...rotatedSequence(1, i, n-1)])
}

function pair(a) {
  return a.slice(0, a.length/2).map((e, i, r) => [e, a[r.length+i]])
}

function* shuffle(arr) {
  arr = [...arr];
  while(arr.length) yield arr.splice(Math.random()*arr.length|0, 1)[0]
}

function sequence(n) {
  return [...rotatedSequence(0, 0, n)]
}

function shuffledSequence(n) {
  return [...shuffle(sequence(n))]
}

let users = sequence(6).map(i => String(Math.random()).substring(2, 6))

console.log('users:', users)

let possibleParingPermutations = users.length - 1

let seq = shuffledSequence(possibleParingPermutations)

console.log('pairing permutation sequence', seq)

for(let i of seq) {
  // 将索引对映射到用户数组中的ID
  // 这也将在排列内对配对进行随机洗牌 - 根据你的情况可能不是必要的
  let pairingPermutation = [...shuffle(getPairingPermutation(users.length, i))]
    .map(j => [...shuffle(j)].map(k => users[k]))

  console.log(`permutation ${i}:`, 
    pairingPermutation.map(j => `[${j.join()}]`).join())
}
英文:

Consider the situation where you have an array of 4 user ids. The possible pairings of the user id at index 0 with other indices are: [0,1],[0,2],[0,3]. Once any of those pairs have been used, they cannot appear again in the same or in any other pairing permutation.

The key insight, therefore, is that if you have n users, you cannot have more than n-1 possible pairing permutations. E.g. if you have 4 users, there are only 4-1=3 pairing permutations: [1,2],[3,4], [1,3],[2,4], [1,4],[2,3].

As trincot pointed out, there is a Circle Method for finding all possible pairing permutations (e.g. all 3 possible pairing permutations when there are 4 users).

The first thing you need to do is pick a pairing permutation that you have not chosen before. If there are 3 possible permutations, you should shuffle the array [0,1,2] to get your random order of possible permutations.

E.g. if you randomly decide the order of pairing permutations you will use is [2,0,1], then the only thing you need to keep track of from then on is that array and how far into that array you've visited so far. You do not need to generate all possible pairing permutations up front or keep track of every single pairing you've ever used.

Then, you find each pairing permutation in that order, and it'll be obvious when you've used up all possible pairing permutations.

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

function* rotatedSequence(start, leftRotation, len) {
for(let i=0; i&lt;len; i++) yield start+(leftRotation+i)%len
}
function getPairingPermutation(n,i) {
return pair([0,...rotatedSequence(1,i,n-1)])
}
function pair(a) {
return a.slice(0,a.length/2).map((e,i,r)=&gt;[e,a[r.length+i]])
}
function* shuffle(arr) {
arr = [...arr];
while(arr.length) yield arr.splice(Math.random()*arr.length|0, 1)[0]
}
function sequence(n) {
return [...rotatedSequence(0,0,n)]
}
function shuffledSequence(n) {
return [...shuffle(sequence(n))]
}
let users = sequence(6).map(i=&gt;String(Math.random()).substring(2,6))
console.log(&#39;users:&#39;, users)
let possibleParingPermutations = users.length - 1
let seq = shuffledSequence(possibleParingPermutations)
console.log(&#39;pairing permutation sequence&#39;, seq)
for(let i of seq) {
// map index pairs to ids in the users array
// this will also shuffle the pairs within the permutation - this may not
// be necessary depending on your situation
let paringPermutation = [...shuffle(getPairingPermutation(users.length, i))]
.map(j=&gt;[...shuffle(j)].map(k=&gt;users[k]))
console.log(`permutation ${i}:`, 
paringPermutation.map(j=&gt;`[${j.join()}]`).join())
}

<!-- end snippet -->

huangapple
  • 本文由 发表于 2023年2月19日 03:15:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/75495784.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定