Как я могу создать словарь со значениями, представляющими голоса для альтернативной системы голосования?

Привет, ребята, я пытаюсь написать код для проблемы, и у меня возникают проблемы с выводом, как я должен это делать.

Поэтому мне нужно написать код для мгновенного повторного голосования или системы альтернативного голосования. в основном происходит то, что есть вложенный список, где каждый список внутри представляет собой бюллетень с ранжированными голосами. Так, например, если список выглядит так: ['РЕСПУБЛИКАНСКИЙ', 'ДЕМОКРАТИЧЕСКИЙ', 'ЗЕЛЕНЫЙ'] это означает, что для этого голосования первым был выбран республиканский, затем демократический, а затем зеленый. Таким образом, по сути, во вложенном списке есть несколько бюллетеней, и функция должна будет создать подсчет для всех упомянутых партий, который покажет, сколько бюллетеней было выбрано конкретной партией первым. Например, если есть 6 бюллетеней, и для трех или более из них республиканцы являются первым выбором, функция должна быть завершена. Если ни одна партия не имеет большинства, то партия с наименьшим количеством голосов исключается, и вы пересчитываете бюллетени, в которых эта партия была первым выбором, но на этот раз вы учитываете второй выбор. Вы продолжаете делать это до тех пор, пока не получите большинство и не вернете словарь со счетом для всех сторон (количество будет равно 0, если стороны будут устранены, но должны быть возвращены).

Вот пример:

>>> count_irv([[’REP’], [’DEM’, ’REP’, ’LIB’], [’GRN’,’REP’], [’GRN’], [’REP’, ’DEM’], [’LIB’, ’DEM’, ’REP’], [’LIB’, ’CON’], [’GRN’, ’DEM’], [’REP’]])

{’LIB’: 0, ’CON’: 0, ’DEM’: 0, ’GRN’: 3, ’REP’: 5}

Это код, который у меня есть до сих пор:

def count_irv(ballots)
    count = {}
    for list in ballots:
        for element in list:
            if element in count:
                count[element] += 1
            else:
                count[element] = 1

    for key in count:
        if count[key] >= len(ballots):
            return count
        else:
            count[last_place(count)] = 0

    return count

Где функция last_place просто возвращает ключ в словаре с наименьшим значением.

Используя приведенный выше код, для предоставленного примера код возвращает:

{'REP': 6, 'DEM': 4, 'LIB': 3, 'GRN': 3, 'CON': 0}

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

Кроме того, я новичок здесь и пока наслаждаюсь своим опытом. Однако кто-то сообщил о моем последнем сообщении за то, что он неправильно разместил код, и я был забанен примерно на день, поэтому я был бы признателен, если бы мне нужно было что-то делать по-другому, пожалуйста, оставьте это в комментариях ниже, и я обязательно внесите соответствующие правки и учтите это в моем следующем посте. Спасибо!


person markovv.sim    schedule 13.11.2019    source источник
comment
Добро пожаловать в StackOverflow. По теме, как задать и... идеальный вопрос применяется здесь. Мы занимаемся конкретными вопросами кодирования; ваша открытая помощь мне и ваш мета-запрос на публикацию справки не относится к теме SO.   -  person Prune    schedule 13.11.2019
comment
Может ли кто-нибудь мне помочь? недопустимый вопрос SO. Обычно это говорит о том, что вам нужно время с местным наставником или просмотр учебника, а не переполнение стека. Лучше всего то, что каждый учебник научит вас набору связанных методов, а не просто решению непосредственной проблемы. Вы запросили нецеленаправленную помощь в анализе проблемы и ее решении.   -  person Prune    schedule 13.11.2019
comment
Помощь для ваших сообщений доступна по различным ссылкам во вступительном туре; Я дал вам несколько полезных ссылок в первом комментарии. Ваш общий вопрос слишком широк; если у вас есть конкретный вопрос о том, как размещать сообщения, направляйте его в группу meta, а не в группу Python и другие технические группы.   -  person Prune    schedule 13.11.2019


Ответы (3)


Это больше похоже на логическую ошибку, чем на ошибку кодирования. Сразу же я замечаю, что этот фрагмент кода здесь:

    for element in list:
        if element in count:
            count[element] += 1
        else:
            count[element] = 1

собирается зарегистрировать голоса второго и третьего выбора каждого человека как часть итогов первого выбора.

Я мог бы предложить добавить некоторые операторы печати для целей отладки, чтобы вы могли просто прочитать трассировку того, как обновляются счетчики. Некоторые комментарии и аннотации типов, которые помогут вам понять, что должен делать ваш код, тоже не помешают!

Вот очень быстрый первый проход по функции, чтобы упростить отладку/чтение без изменения фактической логики:

from collections import defaultdict
from typing import Dict, List

def count_irv(ballots: List[List[str]]) -> Dict[str, int]:
    """Takes a list of ballots, where each ballot is a list of ranked-choice votes
    from highest to lowest priority.  Returns a dictionary of vote totals after
    applying IRV to find a majority choice."""
    count: Dict[str, int] = defaultdict(int)
    print("Counting %d ballots..." % len(ballots))
    for ballot in ballots:
        print("Applying ballot %r" % ballot)
        for element in ballot:
            count[element] += 1
        print("Vote totals: %r" % dict(count))
    count = dict(count)  # convert to plain dict for easier pretty-printing

    for key in count:
        print("Applying IRV for the %r candidate" % key)
        if count[key] >= len(ballots):
            # this candidate... got more votes than there were ballots?
            # how could this ever happen?
            return count
        else:
            # find the candidate who got the least votes and zero their total out
            count[min(count, key=count.get)] = 0
            print("Vote totals: %r" % count)

    return count

Поскольку вы не включили свою функцию last_place, я просто заменил ее встроенной функцией min, которая делает то, что вы описываете; Я также использовал defaultdict для упрощения начального подсчета.

Запустив этот код, вы увидите, что итоги голосования начинаются неправильно, и логика IRV не имеет никакой надежды на их исправление; он выбивает кандидата, занявшего последнее место, но не передает ни один из этих голосов другим кандидатам, и каждый раз в цикле он просто обнуляет одного и того же кандидата, занявшего последнее место. Лучшим подходом может быть:

  1. Подсчитайте все голоса за первое место (ТОЛЬКО голоса за первое место)
  2. Посмотрите, есть ли у кого-то большинство (обратите внимание, что большинство составляет более половины числа бюллетеней, а не больше, чем все количество бюллетеней, что невозможно) — если да, то готово!
  3. Найдите кандидата, занявшего последнее место, и удалите этого кандидата из всех бюллетеней, чтобы голоса за этого кандидата автоматически переносились на следующего кандидата (если он есть).
  4. Начать сначала.
person Samwise    schedule 13.11.2019
comment
Я ценю ответ, однако я новичок и не знаю многого из того, что вы включили в приведенный выше код. - person markovv.sim; 13.11.2019
comment
Это встроенные функции, поэтому вы можете легко найти документацию по ним; вы также можете запустить код, чтобы убедиться, что он делает то же самое, что и исходный код. :) defaultdict(int) просто создает словарь, который автоматически предоставляет вам значение int (0) при доступе к ключу, которого еще не существует. min находит минимум в коллекции (key=count.get означает, что при сравнении он должен get использовать значение из словаря, а не сравнивать сами ключи). - person Samwise; 13.11.2019

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

import random as r
from collections import Counter


def check_majority(voters):
    majority = len(voters) // 2
    print(f'Majority needed={majority}')
    votes = (vote[0] for vote in voters if vote)


    vote_count = Counter(votes).most_common()
    print(f'The counts for votes in this round: {vote_count}')
    winner = vote_count[0]

    if winner[1] > majority:
        print(f'The {winner[0]} party wins with a majority of {winner[1] - majority} votes')
        return winner[0]
    else:
        loser = vote_count[-1]
        print(f'The {loser[0]} party with only {loser[1]} votes has been eliminated')
        for vote in voters:
            if loser[0] in vote:
                vote.remove(loser[0])
        print(f'Progressing to next round of counts....\n')
        check_majority(voters)


partys = ['Labour', 'Conservative', 'Libral Democrats', 'Green party', 'SNP']
num_partys = len(partys)
num_voters = r.randint(1000,10000)
voters = [r.sample(partys, r.randint(1,num_partys)) for _ in range(num_voters)]
print(f'samples generate: {len(voters)}')
check_majority(voters)

ВЫВОД

samples generate: 7387
Majority needed=3693
The counts for votes in this round: Counter({'Labour': 1510, 'Libral Democrats': 1485, 'SNP': 1477, 'Conservative': 1475, 'Green party': 1440})
The Green party party with only 1440 votes has been eliminated
Progressing to next round of counts....

Majority needed=3693
The counts for votes in this round: Counter({'Labour': 1804, 'Libral Democrats': 1794, 'SNP': 1743, 'Conservative': 1742})
The Conservative party with only 1742 votes has been eliminated
Progressing to next round of counts....

Majority needed=3693
The counts for votes in this round: Counter({'Labour': 2228, 'Libral Democrats': 2215, 'SNP': 2170})
The SNP party with only 2170 votes has been eliminated
Progressing to next round of counts....

Majority needed=3693
The counts for votes in this round: Counter({'Labour': 2933, 'Libral Democrats': 2929})
The Libral Democrats party with only 2929 votes has been eliminated
Progressing to next round of counts....

Majority needed=3693
The counts for votes in this round: Counter({'Labour': 4436})
The Labour party wins with a majority of 743 votes

ОБНОВЛЕНИЕ

Я не уверен, почему вы не хотите использовать модуль коллекций, однако вы можете заменить его несколькими строками кода, чтобы подсчитывать каждый раз, когда вы видите вечеринку, и обновлять значение счетчика dict. Затем отсортируйте элементы в dict в виде списка в порядке от самого высокого до самого низкого.

#count and sort the votes instaed of using collections
vote_count_dict = {}
for vote in votes:
    if vote in vote_count_dict:
        vote_count_dict[vote] += 1
    else:
        vote_count_dict[vote] = 1

vote_count = sorted(vote_count_dict.items(), key=lambda items: items[1], reverse=True)
person Chris Doyle    schedule 13.11.2019
comment
Эй, мы на самом деле не узнали о модуле коллекций, есть ли способ сделать это без использования счетчика? - person markovv.sim; 13.11.2019
comment
Это больше похоже на домашнее задание. Я ожидаю, что если вы поставили перед собой эту задачу, вы должны знать, как подсчитать количество предметов.... - person Chris Doyle; 13.11.2019

позволять

ballots = [['REP'], ['DEM', 'REP', 'LIB'], ['GRN','REP'], ['GRN'], ['REP', 'DEM'], ['LIB', 'DEM', 'REP'], ['LIB', 'CON'], ['GRN', 'DEM'], ['REP']]

Короткое и простое решение будет

def count_irv(ballots):
    count = {}
    for ballot in ballots:
        for item in ballot:
            count[item] = 0

    while True:
        for ballot in ballots:
            if len(ballot) > 0:
                count[ballot[0]] += 1

        non_empty_ballots = []
        for ballot in ballots:
            if len(ballot) > 0:
                non_empty_ballots.append(ballot)

        if max(count.values()) >= len(non_empty_ballots) / 2:
            break
        else:
            pos_count = {}
            for k, v in count.items():
                if v > 0:
                    pos_count[k] = v

            min_key = min(pos_count, key=pos_count.get)
            for ballot in ballots:
                if len(ballot) > 0 and ballot[0] == min_key:
                    ballot.remove(min_key)
            for k in count:
                count[k] = 0

    return count

count_irv(ballots)
Out[114]: {'CON': 0, 'DEM': 0, 'GRN': 3, 'LIB': 0, 'REP': 5}
person Yeghishe Kerobyan    schedule 13.11.2019
comment
Эй, вы могли бы попробовать это без наборов и циклов внутри, как в одной строке, извините, я новичок, и у меня ограниченные знания. Я понимаю большую часть кода, за исключением частей, где вы определили count в начале, а также pos_count и min_key. Спасибо за помощь несмотря ни на что! - person markovv.sim; 13.11.2019
comment
упрощенно специально для вас) - person Yeghishe Kerobyan; 13.11.2019