如何创建一个字典,其中包含代表备选投票系统投票的值?

2024-05-13 13:27:50 发布

您现在位置:Python中文网/ 问答频道 /正文

嘿,伙计们,我正试图写一个问题的代码,我有麻烦推断我应该如何做到这一点。你知道吗

所以我必须为即时决选投票或替代投票系统编写代码。基本上,这里有一个嵌套的列表,其中每个列表都是一张有排名的选票。举个例子,如果这个名单看起来像:[‘共和党’,‘民主党’,‘绿色’],这意味着在这次投票中,第一选择是共和党,然后是民主党,然后是绿色。因此,基本上在嵌套列表中有多张选票,函数需要为所提到的所有政党创建一个计数,该计数将显示特定政党的第一选择是多少张选票。例如,如果有6张选票,而其中3张或更多的选票是共和党人的首选,那么选举就应该结束。如果没有一个政党获得多数票,那么得票最低的政党将被淘汰,你重新计算该政党是第一选择的选票,但这次你算作第二选择。你一直这样做,直到你有一个多数,并返回一个字典与计数为所有各方(计数将是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}

所以本质上我需要帮助的是弄清楚如何使我的代码保持循环,直到有一个政党获得多数票。你知道吗

另外,我是新来的,到目前为止我很享受我的经历。然而,有人报告了我没有把代码在正确的方式上的最后一篇文章,我被禁止了一天,所以我会很感激如果有什么我应该做不同的,请留下评论下面,我一定会作出适当的编辑,并为我的下一篇文章考虑它。谢谢!你知道吗


Tags: 代码in列表forlibcountelementcon
3条回答

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}

您可以使用递归函数来计算选票,检查是否有赢家,如果没有赢家,则删除输家,并使用更新的选票再次调用该函数。你知道吗

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

更新

我不知道为什么您不想使用collections模块,但是您可以用几行代码来替换它,以便在每次看到一个party时计数并更新dict counter值。然后按照从高到低的顺序对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)

这看起来更像是一个逻辑错误而不是一个编码错误。马上,我注意到这段代码:

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

将每个人的第二和第三选择选票登记为第一选择总数的一部分。你知道吗

我建议添加一些print语句以进行调试,这样您就可以读回计数如何更新的跟踪。一些注释和类型注释可以帮助您遵循代码应该执行的操作,这也不会有什么坏处!你知道吗

下面是对函数的一个非常快速的第一步,以便在不更改实际逻辑的情况下更易于调试/读取:

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. 重新开始。你知道吗

相关问题 更多 >