Parlamento koltuklarının dağıtımı


14

Giriş

Genel seçimlerde kişi meclis koltuğu başına sabit bir fiyat hesaplamak ister. Bu, N >= 0dağıtılacak koltukların ve nsparti başına oyların bir listesi için d,

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

İşleri ilginç hale getirmek (ve gerçek dünya gibi) için iki gerçek daha ekliyoruz:

  1. İki taraf bir 'koalisyon' içinde toplanabilir, böylece koltuklar içindeki tüm tarafların oylarının toplamı ile “koalisyona” verilir. Daha sonra 'koalisyon'un' sahip olduğu koltuklar benzer bir şekilde taraflar arasında bölünür (bölen, vb. Bulun)

  2. Oyların belirli bir yüzdesini (ör.% 3,25) geçmeyen bir taraf otomatik olarak 0 sandalye alır ve oyları 'koalisyon' için sayılmaz.

Meydan okuma

Size verilenler:

  1. Bir liste listesi, iç içe listelerin her biri tamsayıları (oy sayısı) içerir ve tek bir parti için 1 veya bir 'koalisyon' için 2 uzunluğundadır.
  2. En az oy yüzdesi ("baraj" için "bar" olarak da bilinir), kesir olarak (yani% 3.25, 0.0325 olarak verilir)
  3. Tüm taraflar arasında dağıtılacak toplam koltuk sayısı (tam sayı)

Aynı iç içe liste yapısını, parlamento koltuklarıyla değiştirilen oy sayısı ile yazdıracaksınız.

Kazanan, en az bayt içeren koddur.

Köşe kutuları:

  • Birden fazla olası bölen olabilir (ve genellikle de olacaktır). Çıktıda olmadığı için, gerçekten önemli değil.
  • Düşünün N=10ve ns = [[1]]bölen 0,1 (tamsayı değil) olabilir
  • Bazı durumlarda, örneğin çözülemez ns=[[30],[30],[100]], bar=0, N=20. d=7.5Katlanmış değerlerin toplamının 19'dan 21'e atladığı bir sınır var . Bu durumları çözmeniz beklenmiyor. (bu davayı dile getirdiği için topluluk üyesi Arnauld'a teşekkürler)

Örnek Giriş ve Çıkış

Optimize edilmemiş bir Python3 örneği:

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

Örnek girdi:

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

Ve çıktısı:

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

Bazı örnek çıktılar:

Eğer bar=0.1biz küçük partilerin hiçbirinin iki taraf arasında ilginç bir standby kurtulmak sayılır:

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

Ve eğer N=0(köşe davası) o zaman hiç kimse bir şey almazsa:

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

5
PPCG'ye Hoşgeldiniz!
Arnauld

CGCC'ye hoş geldiniz (daha önce PPCG olarak biliniyordu)! Kodunuzu daha okunabilir hale getirmek için Python vurgulama ekleme özgürlüğünü aldım ve giriş-çıkış birbirine daha yakın böylece girişi kodun altına koydum. Ayrıca alakalı iki etiket ekledim. Güzel ilk meydan okuma olsa, bu yüzden benden +1! Not: Sandbox'ı önerilen zorluklarla ilgili olarak anaya göndermeden önce geri bildirim almak için kullanabilirsiniz, ancak bu durumda zorluğun açık olduğunu düşünüyorum. Birkaç test örneği daha ekleyebilir misiniz? Enjoy your stay :)
Kevin Cruijssen

Elbette @KevinCruijssen, iki dava daha ekledim. Mevcut çıktıya gelince, son seçimlerin kesin sonuçları olduğu için doğru olduğuna inanıyorum :)
scf

@Arnauld Meraktan, bu test durumu için beklenen çıktı ne olmalıdır?
Kevin Cruijssen

1
Köşe kutusuna zaten bir mermi ekledim, bunun sınırsız bir durumda olduğunu düşünüyorum, çünkü sınırda d=7.519 koltuktan 21 koltuğa bir sıçrama yapıyorsunuz.
scf

Yanıtlar:


2

05AB1E , 42 39 bayt

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

Çevrimiçi deneyin!

05AB1E iyi bir özyinelemeden yoksundur, bu nedenle referans kodunda olduğu gibi bir ikili arama yapmak acı verici olacaktır. Neyse ki, böleni hiç bulmamıza gerek yok!

Basit bir örnek kullanalım: [600, 379, 12, 9] oy, 100 sandalye, koalisyon yok, bar yok. İlk olarak, her bir tarafın kaç tane kesirli koltuk aldığını hesaplayıp kesirli koltukları tanımlarız party_votes * seats / sum_of_votes. Örneğimizde, bu [60, 37.9, 1.2, 0.9] 'u verir.

İlginç olan, eğer bir parti fkesirli koltuklar alırsa , int(f)ya int(f) + 1gerçek koltuklar alacaktır . Bu, koltukların 60 + 37 + 1 = 98'inin nasıl tahsis edileceğini zaten bildiğimiz anlamına geliyor ve 4 taraf arasında dağıtmak için 2 "bonus koltuk" bırakılıyor (hiçbir parti 1'den fazla bonus koltuk alamaz). Bu bonus koltuklar kime gidiyor? En yüksek orana sahip taraflar f / (int(f) + 1)(okuyucuya bir alıştırma olarak kalan kanıt). Örneklerimizde, oranlar [0.98, 0.997, 0.6, 0.9], bu nedenle ilk iki tarafın her biri bonus koltuk alır.


Kodlara bir göz atalım. İlk olarak, bara ulaşmayan tüm partilerin oy sayısını 0 ile değiştiriyoruz:

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

Şimdi, özyineleme eksikliği etrafında çalışmak 2Fiçin, ana kodu iki kez tekrarlamak için bir awkard kullanıyoruz . İlk geçişte toplam koltukları koalisyon arasında dağıtır ve ikinci geçişte her koalisyonun koltuklarını partileri arasında dağıtır. İlk geçiş sadece bir kez gerçekleştiğinden, ancak her geçiş için ikinci geçiş gerçekleştiğinden, bu oldukça yoğun bir çalışma gerektirir.

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

Tamam, bu belirsiz bittikten sonra, yığının üst kısmı şimdi bir oy listesi (ilk geçişte koalisyon oyları, ikinci oylarda parti oyları) ve bunun altında ayrılacak koltuk sayısı. Bunu kesirli koltukların listesini hesaplamak için kullanırız:

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

Şimdi oranları hesaplıyoruz:

Ð            # triplicate
 ±           # bitwise not
  /          # divide

Bitwise güzel çalışmıyor, burada. Tamsayıya kısalır, 1 ekler ve tek bir baytta yok eder. Neden inkâr? 05AB1E'de, 0'a bölme 0 döndürür ve en son sıralamak için bunlara ihtiyacımız vardır.

D {# oranların sıralı kopyası ®1% # kesirli oylar mod 1 (ondalık kısımlar olarak da bilinir) Yukarıdakilerin O # toplamı (bu bonus koltuk sayısıdır) ò # en yakın tur (kayan nokta bs nedeniyle gereklidir) è # endeks sıralı oranlara

Bu bize (n + 1) en iyi oranı verir, burada n bonus koltuk sayısıdır (endeksleme 0 tabanlı olduğu için +1). Dolayısıyla, ikramiye sahibi olan taraflar, bundan kesinlikle daha az bir orana sahip olan taraflardır.

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

Çok hoş. Kodunuzu optimize etmek için matematik kullanmak için harika bir yol :)
scf

3

Python 2 , 220 bayt

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]

Çevrimiçi deneyin!

Temelde sadece bir golf uygulama referansı ...


1

Jöle , 63 36 bayt

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

Çevrimiçi deneyin!

Üç argüman alan tam bir program: soru tarafından tanımlanan biçimdeki oy sayısı, bu sırayla bar ve N. Koltuk sayımlarının bir listesini döndürür. TIO'daki altbilgi, yalnızca çıktının liste yapısını vurgulamak içindir. (Aksi takdirde Jelly [], tek öğe listelerini gizler .)

açıklama

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

Orijinal Gönderim (daha büyük ancak daha verimli)

Jöle , 63 bayt

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

Çevrimiçi deneyin!


Güzel teslim. Ben [[10]] (köşe durumlarda bullet nokta 2 bakınız) dönmek için bekliyoruz [10] giriş [[1]] 0.0 10 ile çalıştı ve zaman aşımına uğradı. Bir hata değil, son derece uzun çalışma süresi olduğunu onaylayabilir misiniz?
scf

Orijinal gönderim BTW girişi ile çalışır.
scf

@scf Oyların her zaman koltuklardan çok daha yüksek olduğunu varsaymıştım. Gözden geçirilmiş sürüm iyi çalışmalıdır (ve çok daha verimlidir).
Nick Kennedy

1
Güzel, güzel görünüyor! Kodu biraz açıklayabilirsen iyi olur.
scf

Naif soru: Tavan neden önemlidir? Doğru anlıyorsam, en az oyla tavan yaparsınız, ancak karşılaştırma için gereksizdir.
scf

1

Wolfram - golf yok

Bir golfçü adayı değil, bir soruna ilginç bir yaklaşım olan LinearProgramming'i kullanarak çözmeyi merak ediyordum :

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

Bazı açıklamaları okuyun ve deneyin!


Bir rakip olmasa da, yöntem ve kod hakkında bazı açıklamalar yapmak eğitim amaçlı harika olurdu.
scf

@scf Bunu açıklama girişimim için bir bağlantı ekledim
swish
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.