Find most efficient groups of pairs Find most efficient groups of pairs python python

Find most efficient groups of pairs


This is an implementation of the algorithm described in the Wikipedia article Round-robin tournament.

from itertools import cycle , islice, chaindef round_robin(iterable):    items = list(iterable)    if len(items) % 2 != 0:        items.append(None)    fixed = items[:1]    cyclers = cycle(items[1:])    rounds = len(items) - 1    npairs = len(items) // 2    return [        list(zip(            chain(fixed, islice(cyclers, npairs-1)),            reversed(list(islice(cyclers, npairs)))        ))        for _ in range(rounds)        for _ in [next(cyclers)]    ]


I generate just the indices (because I have trouble coming up with 1000 names =), but for 1000 numbers the runtime is about 4 seconds.

The main problem of all other approaches -- they use pairs and work with them, there are plenty of pairs, and the run time is getting much longer. My approach differs in working with people, not pairs. I have a dict() that maps the person to the list of other persons (s)he has to meet, and these lists are at most N items long (not N^2, as with pairs). Hence the time savings.

#!/usr/bin/env pythonfrom itertools import combinationsfrom collections import defaultdictpairs = combinations( range(6), 2 )pdict = defaultdict(list)for p in pairs :    pdict[p[0]].append( p[1] )while len(pdict) :    busy = set()    print '-----'    for p0 in pdict :        if p0 in busy : continue        for p1 in pdict[p0] :            if p1 in busy : continue            pdict[p0].remove( p1 )            busy.add(p0)            busy.add(p1)            print (p0, p1)            break    # remove empty entries    pdict = { k : v for k,v in pdict.items() if len(v) > 0 }'''output:-----(0, 1)(2, 3)(4, 5)-----(0, 2)(1, 3)-----(0, 3)(1, 2)-----(0, 4)(1, 5)-----(0, 5)(1, 4)-----(2, 4)(3, 5)-----(2, 5)(3, 4)'''


When you need fast lookups hashes/dicts are the way to go. Keep track of whose been in each round in a dict rather than a list and it will be much faster.

Since you are getting in algorithms, studying big O notation will help you out and knowing what data structures are good at what kind of operations is also key. See this guide: https://wiki.python.org/moin/TimeComplexity for time complexity of Python built-ins. You'll see that checking for an item in a list is O(n) meaning that it scales linearly with the size of your inputs. So since it is in a loop you end up with O(n^2) or worse. For dicts, lookups are generally O(1) meaning that it doesn't matter the size of your input.

Also, don't override built-ins. I'd changed round to round_

from itertools import combinations# test if person already exists in any pairing inside a round of pairsdef person_in_round(person, people_dict):    return people_dict.get(person, False)def make_rounds(people):    people_pairs = set(combinations(people, 2))    people_in_round = {}    # we will remove pairings from people_pairs whilst we build rounds, so loop as long as people_pairs is not empty    while people_pairs:        round_ = []        people_dict = {}        # make a copy of the current state of people_pairs to iterate over safely        for pair in set(people_pairs):            if not person_in_round(pair[0], people_dict) and not person_in_round(pair[1], people_dict):                round_.append(pair)                people_dict[pair[0]] = True                people_dict[pair[1]] = True                people_pairs.remove(pair)        yield round_