議席の分配


13

前書き

総選挙では、議席ごとに定価を計算したいと思います。これは、N >= 0配布されるシートとnsパーティーごとの投票リストについて、次のdような数を見つけたいことを意味します。

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

物事を面白くするために(さらに現実の世界に近づけるために)、さらに2つの事実を追加します。

  1. 2つの政党が「連合」に集まって、その中のすべての政党の票の合計によって議席が「連合」に与えられます。次に、「連合」が獲得した座席は、同様の方法でパーティ間で分割されます(除数を見つけるなど)。

  2. 一定の割合の票(3.25%など)を渡さなかった党は自動的に0議席を獲得し、その票は「連合」にカウントされません。

チャレンジ

あなたが与えられます:

  1. リストのリスト。ネストされた各リストには整数(投票数)が含まれ、単一のパーティの場合は長さ1、「連合」の場合は長さ2です。
  2. 議席を獲得するための票の最小割合(別名「弾幕」の「バー」)、分数(したがって、3.225%は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小規模なパーティは含まれていないため、2つのパーティ間で興味深いスタンドオフが発生した場合:

[[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へようこそ!
アーナルド

CGCC(以前はPPCGとして知られていました)へようこそ!コードを読みやすくするためにPythonの強調表示を追加する自由を取り、入力と出力がより近くなるように入力をコードの下に配置しました。関連する2つのタグも追加しました。ナイスな最初の挑戦ですが、私から+1!PS:提案されたチャレンジサンドボックスを使用して、チャレンジをメインに投稿する前にフィードバックを得ることができますが、この場合、チャレンジは明確だと思います。おそらく、さらにいくつかのテストケースを追加しますか?ご滞在をお楽しみください:)
ケビンクルーッセン

確かに@KevinCruijssen、さらに2つのケースを追加しました。既存の出力に関しては、最近の選挙の正確な結果であるため、それが真実であると信じています:)
scf

@Arnauld好奇心から、そのテストケースに期待される出力は何でしょうか。
ケビンCruijssen

1
私はすでにコーナーケースに弾丸を追加しました。境界でd=7.5は19席から21席にジャンプするので、これは解決できないケースだと思います。
scf

回答:


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のシートがどのように割り当てられるかはすでにわかっており、4つのパーティに分配するための2つの「ボーナスシート」が残っています(1つ以上のボーナスシートを獲得することはできません)。これらのボーナスシートは誰に行きますか?f / (int(f) + 1)(読者への演習として残された証拠)の割合が最も高い当事者。この例では、比率は[0.98, 0.997, 0.6, 0.9]であるため、最初の2人はそれぞれボーナスシートを獲得します。


コードを見てみましょう。最初に、基準を満たしていないすべての政党の投票数を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てメインコードを2回繰り返します。最初のパスでは、連合の間で総議席を配分し、2番目のパスでは、各連合の議席を政党間で配分します。最初のパスは1回だけ実行されますが、2回目のパスは各連合に対して実行されるため、これにはかなりの手間がかかります。

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

さて、このあいまいなビットの後、スタックの最上部は投票リスト(最初のパスでの連立投票、2番目のパーティでの投票)になり、その下に割り当てる座席数です。これを使用して、部分座席のリストを計算します。

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が加算され、すべてが1バイトで無効になります。否定する理由 05AB1Eでは、0による除算は0を返し、これらを最後にソートする必要があります。

D {#比率のソートされたコピー®1%#少数票mod 1(小数部とも呼ばれます)O#上記の合計(これはボーナスシートの数です)ò#最も近い値に丸める(浮動小数点bsのために必要) è#ソートされた比率のインデックス

これにより、(n + 1)番目の最適な比率が得られます。ここで、nはボーナスシートの数です(インデックス付けが0ベースであるため、+ 1)。したがって、ボーナスシートを取得するのは、これよりも厳密に小さい比率を持つ当事者です。

‹      # 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ɗ¥ƬṪṪƲ¥¥@⁺"

オンラインでお試しください!

3つの引数を取る完全なプログラム:質問、バー、Nの順に記述された形式の投票数。座席数のリストのリストを返します。TIOのフッターは、出力のリスト構造を強調するだけです。(それ以外の場合、ゼリーは非表示にします[]単一項目リストに対してになります。)

説明

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私はそれを説明する時に私の試みへのリンクを追加しました
パッ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.