การกระจายที่นั่งรัฐสภา


13

บทนำ

ในการเลือกตั้งทั่วไปเราต้องการคำนวณราคาคงที่ต่อที่นั่งของรัฐสภา ซึ่งหมายความว่าสำหรับN >= 0ที่นั่งที่จะแจกจ่ายและรายชื่อnsคะแนนต่อปาร์ตี้เราต้องการค้นหาหมายเลขdดังกล่าว

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

เพื่อให้สิ่งต่าง ๆ น่าสนใจ (และเหมือนโลกแห่งความเป็นจริง) เราได้เพิ่มข้อเท็จจริงสองประการ:

  1. ทั้งสองฝ่ายสามารถรวมตัวกันเป็น 'พันธมิตร' เพื่อให้ที่นั่งได้รับ 'พันธมิตร' ด้วยคะแนนรวมสำหรับทุกฝ่ายในนั้น จากนั้นที่นั่งที่ 'พันธมิตร' ได้รับจะถูกแยกออกระหว่างฝ่ายในแบบเดียวกัน (ค้นหาตัวหาร ฯลฯ )

  2. ปาร์ตี้ที่ไม่ผ่านการลงคะแนนเสียงในระดับที่แน่นอน (เช่น 3.25%) จะได้รับ 0 ที่นั่งโดยอัตโนมัติและการโหวตจะไม่นับสำหรับ 'การรวม'

ท้าทาย

คุณจะได้รับ:

  1. รายการของรายการแต่ละรายการซ้อนกันประกอบด้วยจำนวนเต็ม (จำนวนโหวต) และมีความยาว 1 สำหรับงานปาร์ตี้เดี่ยวหรือยาว 2 สำหรับ 'พันธมิตร'
  2. เปอร์เซ็นต์การโหวตน้อยที่สุด (aka "บาร์" สำหรับ "เขื่อนกั้นน้ำ") เพื่อรับที่นั่งเป็นเศษส่วน (ดังนั้น 3.25% จะได้รับเป็น 0.0325)
  3. จำนวนที่นั่งทั้งหมดที่จะแจกจ่ายระหว่างทุกฝ่าย (จำนวนเต็ม)

คุณจะต้องพิมพ์โครงสร้างรายการซ้อนเดียวกันด้วยจำนวนคะแนนที่แทนที่ด้วยที่นั่งของรัฐสภา

ผู้ชนะคือรหัสที่มีจำนวนไบต์น้อยที่สุด

กรณีมุม:

  • อาจมี (และโดยปกติจะ) มากกว่าหนึ่งตัวหารที่เป็นไปได้ เนื่องจากมันไม่ได้อยู่ในเอาต์พุตมันจึงไม่สำคัญ
  • ลองนึกภาพN=10และns = [[1]]ดังนั้นตัวหารอาจเป็น 0.1 (ไม่ใช่จำนวนเต็ม)
  • บางกรณีไม่สามารถแก้ไขได้เช่นns=[[30],[30],[100]], ,bar=0 N=20มีขอบเขตd=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: คุณสามารถใช้Sandbox ของความท้าทายที่เสนอเพื่อรับข้อเสนอแนะเกี่ยวกับความท้าทายก่อนที่จะโพสต์ไปยังหน้าหลักแม้ว่าในกรณีนี้ฉันคิดว่าความท้าทายชัดเจน อาจเพิ่มกรณีทดสอบเพิ่มเติมอีกสองสามกรณีหรือไม่ เพลิดเพลินกับการเข้าพักของคุณ :)
เควิน Cruijssen

แน่นอน @KevinCruijssen ฉันได้เพิ่มอีกสองกรณี สำหรับผลลัพธ์ที่มีอยู่ฉันเชื่อว่ามันจะเป็นจริงเพราะมันเป็นผลลัพธ์ที่แน่นอนของการเลือกตั้งเมื่อเร็ว ๆ นี้ :)
scf

@Annauld ออกจากความอยากรู้สิ่งที่คาดหวังที่ควรจะเป็นสำหรับการทดสอบกรณีที่?
Kevin Cruijssen

1
ฉันเพิ่มกระสุนเข้ามุมแล้วฉันคิดว่านี่เป็นกรณีที่แก้ไม่ได้เหมือนในขอบเขตที่d=7.5คุณกระโดดจาก 19 ที่นั่งเป็น 21 ที่นั่ง
scf

คำตอบ:


2

05AB1E , 42 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

Bitwise ใช้งานไม่ได้อย่างสวยงามที่นี่ มันตัดเป็นจำนวนเต็มเพิ่ม 1 และลบทั้งหมดในไบต์เดียว ทำไมจึงปฏิเสธ? ใน 05AB1E การหารด้วย 0 จะส่งคืน 0 และเราต้องการสิ่งเหล่านี้เพื่อเรียงลำดับสุดท้าย

D {# สำเนาเรียงลำดับของอัตราส่วน® 1% # คะแนนเศษส่วน mod 1 (aka ส่วนทศนิยม) O # ผลรวมข้างต้น (นี่คือจำนวนที่นั่งโบนัส) round # รอบที่ใกล้ที่สุด (จำเป็นเนื่องจากจุดลอยตัว bs) è # ดัชนีเป็นอัตราส่วนที่เรียงลำดับแล้ว

สิ่งนี้ทำให้เรามีอัตราส่วนที่ดีที่สุด (n + 1) th โดยที่ 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 เพียงเพื่อเน้นโครงสร้างรายการของเอาต์พุต (มิฉะนั้นซ่อนเยลลี่[]สำหรับรายการเดียว)

คำอธิบาย

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 ฉันคิดว่าการโหวตไม่ถูกต้องมักจะสูงกว่าที่นั่งมาก รุ่นที่แก้ไขควรใช้งานได้ (และมีประสิทธิภาพมากกว่า)
Nick Kennedy

1
ดีดูดี! มันคงจะดีถ้าคุณสามารถอธิบายรหัสได้เล็กน้อย
scf

คำถาม Naiive: ทำไมเพดานจึงสำคัญ หากฉันเข้าใจอย่างถูกต้องว่าคุณแสดงจำนวนคะแนนน้อยที่สุด แต่ก็ไม่จำเป็นสำหรับการเปรียบเทียบ
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.