分配议会席位


13

介绍

在大选中,人们想计算每个议会席位的不变价格。这意味着N >= 0要分配席位和ns每个政党的投票清单,我们希望找到一个数字d,以便

sum(floor(n/d) for n in ns) == N 

为了使事情变得有趣(更像现实世界),我们添加了两个事实:

  1. 两党可以聚集在一个“联盟”中,这样​​,“联盟”的席位就由该联盟中所有各方的票数之和决定。然后,“联盟”获得的席位以类似的方式在各方之间分配(找到除数等)

  2. 没有获得一定比例票数(例如3.25%)的政党自动获得0席,其票数不计入“同盟”。

挑战

给你:

  1. 列表列表,每个嵌套列表都包含整数(投票数),单方长度为1,“联盟”长度为2。
  2. 获得席位的最低票数百分比(又称“弹幕”的“巴”),即分数(因此3.25%表示为0.0325)
  3. 各方之间分配的席位总数(整数)

您将打印出相同的嵌套列表结构,并用议席数代替票数。

优胜者是字节数最少的代码。

角落案例:

  • 除数可能(通常会)有一个以上。由于它不在输出中,因此它并不重要。
  • 想象N=10ns = [[1]],所以除数可以是0.1(不是整数)
  • 有些案件不能得到解决,例如ns=[[30],[30],[100]]bar=0N=20d=7.5下限的总和从19跳到21,这是一个边界。您不希望解决这些情况。(感谢社区成员Arnauld指出此案)

输入和输出示例

一个未优化的Python3示例:

from math import floor

def main(_l, bar, N):
    # sum all votes to calculate bar in votes
    votes = sum(sum(_) for _ in _l)

    # nullify all parties that didn't pass the bar
    _l = [[__ if __ >= bar * votes else 0 for __ in _] for _ in _l]

    # find divisor for all parliament seats
    divisor = find_divisor([sum(_) for _ in _l], N)

    # find divisor for each 'coalition'
    divisors = [find_divisor(_, floor(sum(_)/divisor)) for _ in _l]

    # return final results
    return [[floor(___/_) for ___ in __] for _, __ in zip(divisors, _l)]

def find_divisor(_l, N, _min=0, _max=1):
    s = sum(floor(_ / _max) for _ in _l)
    if s == N:
            return _max
    elif s < N:
            return find_divisor(_l, N, _min, (_max + _min) / 2)
    else:
            return find_divisor(_l, N, _max, _max * 2)

print(main(l, bar, N))

输入示例:

l = [[190970, 156473], 
    [138598, 173004], 
    [143666, 193442], 
    [1140370, 159468], 
    [258275, 249049], 
    [624, 819], 
    [1125881], 
    [152756], 
    [118031], 
    [74701]]
bar = 0.0325
N = 120

及其输出:

[[6, 4], [0, 5], [4, 6], [35, 5], [8, 8], [0, 0], [35], [4], [0], [0]]

其他一些示例输出:

如果bar=0.1我们在两个政党之间产生有趣的对峙,因为较小的政党都不算在内:

[[0, 0], [0, 0], [0, 0], [60, 0], [0, 0], [0, 0], [60], [0], [0], [0]]

而且,如果N=0(角落情况)那么,当然没人会得到任何东西:

[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0], [0], [0], [0]]

5
欢迎来到PPCG!
Arnauld

欢迎来到CGCC(以前称为PPCG)!我随意添加Python高亮显示,以便使您的代码更具可读性,并且将输入放置在代码下方,以使输入输出更加紧密。我还添加了两个相关标签。不错,不过是第一个挑战,所以我+1!PS:您可以使用拟议挑战沙箱在将挑战发布到主要对象之前获得对挑战的反馈,尽管在这种情况下,我认为挑战很明确。也许添加一些其他测试用例?

肯定@KevinCruijssen,我又添加了两个案例。至于现有的输出,我相信它是真实的,因为它是最近一次选举的确切结果:)
scf

@Arnauld出于好奇,该测试用例的预期输出是什么?
凯文·克鲁伊森

1
我已经在拐角处添加了一个项目符号,我认为这是一个无法解决的情况,因为在边界处,d=7.5您可以从19个席位跃升至21个席位。
scf

Answers:


2

05AB1E42 39字节

ÐOOI*@*DO¸I¸¸2Fнζε`sDO/*Щ±/D{®1%Oòè‹+ï

在线尝试!

05AB1E缺乏良好的递归,因此像参考代码中那样执行二进制搜索将很痛苦。值得庆幸的是,我们根本不需要找到除数!

让我们使用一个简单的示例:[600、379、12、9]票,100个席位,没有联盟,没有酒吧。首先,我们计算每方获得多少小数席位,将小数席位定义为party_votes * seats / sum_of_votes。在我们的示例中,结果为[60,37.9,1.2,0.9]。

有趣的一点是,如果一方获得f小数席位,它将获得int(f)int(f) + 1实际席位。这意味着我们已经知道如何分配60 + 37 + 1 = 98的席位,并且剩下2个“奖励席位”在4个政党之间分配(任何政党最多只能获得1个奖励席位)。这些奖励席位送给谁?比率最高的当事方f / (int(f) + 1)(作为练习的剩余证明供读者阅读)。在我们的示例中,比率为[0.98, 0.997, 0.6, 0.9],因此前两方各有一个额外席位。


让我们看一下代码。首先,我们将未达到标准的所有各方的投票数替换为0:

Ð          # triplicate the first input (list of votes)
 OO        # flattened sum
   I*      # multiply by the second input (bar)
     @     # greater than? (returns 0 or 1)
      *    # multiply

现在,要解决缺少递归的问题,我们使用awkard 2F将主代码重复两次。在第一次通过时,它将在联盟之间分配总席位,而在第二次通过时,它将在各党派之间分配每个联盟的席位。由于第一遍仅运行一次,但是第二遍针对每个联盟运行,因此这涉及很多繁忙的工作。

DO¸I¸¸2Fнζε`s    # i don’t want to detail this tbh

好吧,在这晦涩难懂的一点之后,现在堆栈的顶部是一个选票列表(第一张票为联盟票,第二张票为党票),而下方是分配的席位数。我们使用它来计算分数席位列表:

D        # duplicate
 O       # sum  
  /      # divide each vote count by the sum
   *     # multiply by the number of seats
    ©    # save the fractional seats in variable r

现在,我们计算比率:

Ð            # triplicate
 ±           # bitwise not
  /          # divide

在这里,按位操作效果不佳。它会在一个字节中截断为整数,加1,然后取反。为什么要否定?在05AB1E中,除以0将返回0,我们需要对它们进行最后排序。

D {#排序后的比率®1%#小数投票mod 1(又称小数部分)O#以上总和(这是奖励席位的数量)ò#四舍五入到最接近的值(由于浮点bs而需要) è#索引到已排序的比率

这给了我们第(n + 1)个最佳比率,其中n是奖励席位的数量(+1是因为索引是基于0的)。因此,获得奖励席位的一方是比率严格小于此比例的一方。

‹      # less than
 +     # add to the fractional seats
  ï    # truncate to integer

非常好。使用数学优化代码的好方法:)
scf

3

Python 2,220字节

def d(l,n,a=0,b=1.):s=sum(x//b for x in l);return s-n and d(l,n,*[a,b,(a+b)/2,b*2][s>n::2])or b
def f(l,b,n):l=[[x*(x>=b*sum(sum(l,[])))for x in r]for r in l];return[[v//d(x,sum(x)//d(map(sum,l),n))for v in x]for x in l]

在线尝试!

基本上只是参考实现的一部分...


1

果冻63 36字节

F×S<ḷ×ḷµ§⁵:,1_×¥:@"§IṠʋ÷9ɗ¥ƬṪṪƲ¥¥@⁺"

在线尝试!

一个完整的程序,包含三个参数:问题描述的格式的投票数,小节和N顺序。返回座位计数列表。TIO的页脚仅是突出显示输出的列表结构。(否则,Jelly会隐藏[]单项列表。)

说明

F×S<ḷ×ḷµ§⁵:,1_×¥:@"§IṠʋ÷9ɗ¥ƬṪṪƲ¥¥@⁺"

F                                   | Flatten vote counts
 ×                                  | Multiply by bar
  S                                 | Sum
   <ḷ                               | Less than original vote counts (vectorises and respects input list structure)
     ×ḷ                             | Multiply by original vote counts
       µ                            | Start a new monadic link with processed vote counts as input
        §                           | Vectorised sum

         ⁵                      ¥@  | Apply the following as a dyad with the number of seats as the right argument and the vectorised sum of votes as left

           ,                  Ʋ¥    |(*)- Pair vote counts with seat sum and find divisor using the following as a monad:
            1             ¥Ƭ        |     - Starting with 1 as a guess for divisor, and using the paired vote counts and seat sum as the right argument, apply the following as a dyad, collecting intermediate results, until the results repeat
                         ɗ          |       - Following as a dyad:
                      ʋ             |         - Following as a dyad:
                :@"                 |           - Integer divide with arguments zipped and reversed, i.e. divide cote counts by current divisor guess and leave total seats alone
                   §                |           -  Vectorised sum (will sum vote counts but leave seat number alone)
                    I               |           - Find differences i.e. desired total seats minus current calculation based on current divisor guess. Will return a list.
                     Ṡ              |           - Sign of this (-1, 0 or 1)
                       ÷9           |         - Divide by 9 (-0.111, 0 or 0.111)
             _×¥                    |     - Now multiply the current divisor guess by this and subtract it from that guess to generate the next guess. If the current guess is correct, the guess will be unchanged and so the Ƭ loop will terminate
                            ṪṪ      |     - Take the last item twice (first time to get the final
                               output of the Ƭ loop and second to remove the list introduced by I
         :                          | - Integer divide the vote counts by the output of the above

                                  ⁺"| Apply the above dyad from the step labelled (*) again, this time with the output of the previous step (total votes per coalition) as right argument and the vote counts as left argument, zipping the two together and running the link once for each pair

原始提交(更大但更有效)

果冻,63个字节

:S_3ƭƒṠ©ḢḤ;$;ṪƲṖÆm;ḊƲ®‘¤?ߥ/}ṛ¹?,
1,0;çḢḢ
FS×Ċ’<ḷ×ḷµ:"§:⁵ç$$ç"Ɗ

在线尝试!


不错的提交。我用输入[[1]] 0.0 10进行了尝试,我希望它返回[[10]](在极端情况下,请参见项目符号要点2)并超时。您能确认这是非常长的运行时间,而不是错误吗?
scf

原始提交与该输入的BTW一起使用。
scf

@scf我错误地认为票数总是比座位高。修改后的版本应该可以正常工作(并且效率更高)。
尼克·肯尼迪

1
不错,看起来不错!如果可以稍微解释一下代码,那就太好了。
scf

朴素的问题:天花板为什么重要?如果我理解正确,您将以最少的票数进行上限,但是,进行比较是不必要的。
scf

1

Wolfram-禁止高尔夫

只是好奇地使用LinearProgramming解决了这个问题,而不是打高尔夫球的候选人,但也许是解决问题的一种有趣方法:

findDivisor[l_, n_] := Quiet@Module[{s, c, r, m, b, cons, sol},
   s = Length[l];
   c = Append[ConstantArray[0, s], 1];
   r = Thread[Append[IdentityMatrix[s], -l]];
   m = Append[Join[r, r], Append[ConstantArray[1, s], 0]];
   b = Append[Join[ConstantArray[{0, -1}, s], ConstantArray[{-1, 1}, s]], {n, 0}];
   cons = Append[ConstantArray[Integers, s], Reals];
   sol = LinearProgramming[c, m, b, 0, cons];
   {1/sol[[-1]], Most@sol}
   ]
solve[l_, bar_, n_] := 
 With[{t = l /. x_ /; x <= bar Total[l, 2] -> 0},
  With[{sol = findDivisor[Total /@ t, n]}, 
   {First@sol, MapThread[findDivisor, {t, Last@sol}]}]
  ]

阅读一些说明并尝试一下!


即使它不是竞争对手,但对方法和代码进行一些解释对于教育目的还是很有用的。
scf

@scf我已在尝试解释的过程中添加了链接
swish
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.