Phân phối ghế quốc hội


13

Giới thiệu

Trong một cuộc tổng tuyển cử, người ta muốn tính một mức giá không đổi trên mỗi ghế quốc hội. Điều này có nghĩa là N >= 0để phân bổ số ghế và danh sách nsphiếu bầu cho mỗi bên, chúng tôi muốn tìm một số dsao cho

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

Để làm cho mọi thứ thú vị (và giống như thế giới thực hơn), chúng tôi thêm hai sự thật nữa:

  1. Hai bên có thể tập hợp thành một "liên minh", để các ghế được trao cho "liên minh" bằng tổng số phiếu bầu cho tất cả các bên trong đó. Sau đó, các ghế mà 'liên minh' có được được phân chia giữa các bên theo kiểu tương tự (tìm ước số, v.v.)

  2. Một bên không vượt qua một tỷ lệ phiếu nhất định (ví dụ 3,25%) sẽ tự động nhận được 0 ghế và phiếu bầu của họ không được tính cho một "liên minh".

Thử thách

Bạn đã được nhận :

  1. Một danh sách các danh sách, mỗi danh sách lồng nhau chứa các số nguyên (số phiếu) và có độ dài 1 cho một bên hoặc chiều dài 2 cho một "liên minh".
  2. Tỷ lệ phiếu bầu tối thiểu (còn gọi là "bar" cho "barrage") để có chỗ ngồi, dưới dạng phân số (vì vậy 3,25% được đưa ra là 0,0325)
  3. Tổng số ghế được phân phối giữa tất cả các bên (số nguyên)

Bạn phải in ra cấu trúc danh sách lồng nhau, với số phiếu được thay thế bằng ghế quốc hội.

Người chiến thắng là mã có số lượng byte nhỏ nhất.

Trường hợp góc:

  • Có thể (và thường sẽ có) nhiều hơn một ước số có thể. Vì nó không có trong đầu ra, nên nó không thực sự quan trọng.
  • Hãy tưởng tượng N=10ns = [[1]], vì vậy số chia có thể là 0,1 (không phải là số nguyên)
  • Một số trường hợp không thể giải quyết, ví dụ ns=[[30],[30],[100]], bar=0, N=20. Có một ranh giới d=7.5trong đó tổng các giá trị thả nổi nhảy từ 19 đến 21. Bạn không cần phải giải quyết các trường hợp này. (cảm ơn thành viên cộng đồng Arnauld đã chỉ ra trường hợp này)

Ví dụ đầu vào và đầu ra

Một ví dụ Python3 không được tối ưu hóa:

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))

Ví dụ đầu vào:

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

Và đầu ra của nó:

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

Một số ví dụ đầu ra:

Nếu bar=0.1chúng tôi có một cuộc đối thoại thú vị giữa hai bên vì không có bên nào trong số các bên nhỏ hơn được tính vào:

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

Và nếu N=0(trường hợp góc) thì tất nhiên không ai nhận được gì:

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

5
Chào mừng đến với PPCG!
Arnauld

Chào mừng bạn đến với CGCC (trước đây gọi là PPCG)! Tôi đã tự do thêm phần tô sáng Python để mã của bạn trở nên dễ đọc hơn và tôi đã đặt đầu vào bên dưới mã để đầu vào-đầu ra gần nhau hơn. Tôi cũng đã thêm hai thẻ có liên quan. Mặc dù thử thách đầu tiên rất hay, nên +1 từ tôi! Tái bút: Bạn có thể sử dụng Sandbox của các thử thách được đề xuất để nhận phản hồi về các thử thách trước khi đăng chúng lên chính, mặc dù trong trường hợp này tôi nghĩ thử thách đã rõ ràng. Có lẽ thêm một vài trường hợp thử nghiệm bổ sung? Tận hưởng kì nghỉ của bạn :)
Kevin Cruijssen

Chắc chắn điều @KevinCruijssen, tôi đã thêm hai trường hợp nữa. Đối với đầu ra hiện tại, tôi tin rằng nó là sự thật vì đó là kết quả chính xác của một cuộc bầu cử gần đây :)
scf

@Arnauld Vì tò mò, đầu ra dự kiến ​​sẽ là gì cho trường hợp thử nghiệm đó?
Kevin Cruijssen

1
Tôi đã thêm một viên đạn trong trường hợp góc, tôi nghĩ rằng đây là một trường hợp không thể giải quyết vì trong ranh giới d=7.5bạn có một bước nhảy từ 19 chỗ ngồi lên 21 chỗ ngồi.
scf

Câu trả lời:


2

05AB1E , 42 39 byte

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

Hãy thử trực tuyến!

05AB1E thiếu đệ quy tốt, do đó, việc thực hiện tìm kiếm nhị phân như trong mã tham chiếu sẽ rất khó khăn. Rất may, chúng tôi không cần phải tìm ước số nào cả!

Chúng ta hãy sử dụng một ví dụ đơn giản: [600, 379, 12, 9] phiếu bầu, 100 ghế, không liên minh, không quán bar. Đầu tiên, chúng tôi tính toán có bao nhiêu ghế phân số mà mỗi bên có được, xác định số ghế phân số là party_votes * seats / sum_of_votes. Trong ví dụ của chúng tôi, điều đó mang lại [60, 37.9, 1.2, 0.9].

Điều thú vị là nếu một bữa tiệc có fghế phân số, nó sẽ có được ghế int(f)hoặc int(f) + 1thực. Điều này có nghĩa là chúng tôi đã biết 60 + 37 + 1 = 98 số ghế sẽ được phân bổ như thế nào và chúng tôi còn lại với 2 ghế thưởng Phần thưởng để phân phối giữa 4 bên (không bên nào có thể nhận được nhiều hơn 1 ghế thưởng). Ai làm những ghế thưởng này đi đến? Các bên có tỷ lệ cao nhất f / (int(f) + 1)(bằng chứng còn lại là một bài tập cho người đọc). Trong các ví dụ của chúng tôi, các tỷ lệ là [0.98, 0.997, 0.6, 0.9], vì vậy hai bên đầu tiên nhận được một ghế thưởng mỗi bên.


Chúng ta hãy xem mã. Đầu tiên, chúng tôi thay thế số phiếu của tất cả các bên không đáp ứng thanh bằng 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

Bây giờ, để giải quyết vấn đề thiếu đệ quy, chúng tôi sử dụng một awkard 2Fđể lặp lại mã chính hai lần. Trên đường chuyền đầu tiên, nó sẽ phân phối tổng số ghế giữa liên minh và trên đường chuyền thứ hai, nó sẽ phân phối từng ghế của liên minh giữa các bên. Vì đường chuyền đầu tiên chỉ chạy một lần, nhưng đường chuyền thứ hai chạy cho mỗi liên minh, điều này liên quan đến khá nhiều công việc bận rộn.

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

Được rồi, sau bit tối nghĩa này, đầu ngăn xếp hiện là danh sách phiếu bầu (phiếu liên minh ở lượt đầu tiên, phiếu bầu của đảng thứ hai), và dưới đó là số ghế cần phân bổ. Chúng tôi sử dụng điều này để tính toán danh sách các ghế phân số:

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

Bây giờ, chúng tôi tính toán các tỷ lệ:

Ð            # triplicate
 ±           # bitwise not
  /          # divide

Bitwise không hoạt động đẹp, ở đây. Nó cắt thành số nguyên, thêm 1 và phủ định, tất cả trong một byte đơn. Tại sao lại phủ nhận? Trong 05AB1E, phép chia cho 0 trả về 0 và chúng ta cần những thứ này để sắp xếp sau cùng.

D {# đã sắp xếp bản sao các tỷ lệ ®1% # phiếu bầu phân số mod 1 (còn gọi là phần thập phân) O # tổng của phần trên (đây là số ghế thưởng) ò # tròn đến gần nhất (bắt buộc do bs dấu phẩy động) Chỉ số # vào các tỷ lệ được sắp xếp

Điều này mang lại cho chúng tôi tỷ lệ tốt nhất (n + 1), trong đó n là số lượng ghế thưởng (+1 vì lập chỉ mục là 0 dựa trên). Do đó, các bên nhận được một ghế thưởng là những bên có tỷ lệ thấp hơn mức này.

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

Rất đẹp. Cách tuyệt vời để sử dụng toán học để tối ưu hóa mã của bạn :)
scf

3

Python 2 , 220 byte

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]

Hãy thử trực tuyến!

Về cơ bản chỉ là một sân golf của việc thực hiện tham khảo ...


1

Thạch , 63 36 byte

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

Hãy thử trực tuyến!

Một chương trình đầy đủ có ba đối số: số phiếu theo định dạng được mô tả bởi câu hỏi, thanh và N theo thứ tự đó. Trả về một danh sách các danh sách đếm chỗ ngồi. Chân trang trên TIO chỉ để làm nổi bật cấu trúc danh sách của đầu ra. (Nếu không, Jelly ẩn []cho danh sách một mục.)

Giải trình

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

Đệ trình gốc (lớn hơn nhưng hiệu quả hơn)

Thạch , 63 byte

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

Hãy thử trực tuyến!


Trình tốt đẹp. Tôi đã thử nó với đầu vào [[1]] 0,0 10, mà tôi dự kiến ​​sẽ trả về [[10]] (xem dấu đầu dòng 2 trong các trường hợp góc) và đã hết thời gian. Bạn có thể xác nhận nó chỉ là thời gian chạy rất dài và không phải là một lỗi không?
scf

Trình ban đầu hoạt động với BTW đầu vào đó.
scf

@scf Tôi cho rằng phiếu bầu không chính xác luôn cao hơn nhiều so với số ghế. Phiên bản sửa đổi sẽ hoạt động tốt (và hiệu quả hơn nhiều).
Nick Kennedy

1
Đẹp, có vẻ tốt! Thật tuyệt nếu bạn có thể giải thích mã một chút.
scf

Câu hỏi ngây thơ: tại sao trần quan trọng? Nếu tôi hiểu chính xác, bạn thực hiện mức trần với số phiếu tối thiểu, tuy nhiên không cần thiết phải so sánh.
scf

1

Wolfram - không chơi gôn

Chỉ tò mò giải quyết nó bằng cách sử dụng chương trình tuyến tính , không phải là một ứng cử viên chơi gôn, nhưng có lẽ là một cách tiếp cận thú vị cho một vấn đề:

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}]}]
  ]

Đọc một số lời giải thích và thử nó!


Mặc dù nó không phải là đối thủ cạnh tranh, nhưng có một số giải thích về phương pháp và mã sẽ rất tốt cho mục đích giáo dục.
scf

@scf Tôi đã thêm một liên kết đến nỗ lực giải thích của mình
swish
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.