Elect the Doge of Venice

The Venetian election system was... complicated.

The Great Council came together and put in an urn the ballots of all the councilors who were older than 30. The youngest councilor went to St Mark's Square and chose the first boy he met who drew from the urn a ballot for each councillor and only those 30 who got the word ‘elector' remained in the room. The 30 ballots were then placed back in the box and only 9 contained a ticket, so the 30 were reduced to 9, who gathered in a sort of conclave, during which, with the favourable vote of at least seven of them, they had to indicate the name of 40 councillors.

With the system of ballots containing a ticket, the 40 were reduced to 12; these, with the favourable vote of at least 9 of them, elected 25 others, which were reduced again to 9 who would elect another 45 with at least 7 votes in favour. The 45, again at random, were reduced to 11, who with at least nine votes in favour elected another 41 that finally would be the real electors of Doge.

These 41 gathered in a special room where each one cast a piece of paper into an urn with a name. One of them was extracted at random. Voters could then make their objections, if any, and charges against the chosen one, who was then called to respond and provide any justification. After listening to him, they preceded to a new election, if the candidate obtained the favourable vote of at least 25 votes out of 41, he was proclaimed Doge, if they were unable to obtain these votes a new extraction took place until the outcome was positive.

Venice is no longer an independent republic, but if they were, they would be dying to automate this system! (because we all know electronic voting is the future!) Time for you to step in. To streamline the democracy, I've made a few assumptions on voter preference, group sizes, picking logic, etc. Your program is to do the following.

• Here is the list of members of the Great Council (who are all older than 30). Take this as input, by perhaps reading it from a file, or whatever you prefer. The number of councillors varied over time, so your program should not depend on the number of people.
• Take the youngest member of the council. Because there are no ages given, you'll have to guess. Pick a person at random, and print their name.
• The boy at the square will pick thirty members from an urn. So, randomly choose 30 people from the list (not including the youngest councillor). Print all their names.
• Of those thirty, nine are randomly selected to go to the next round. So randomly choose 9 from that group, and print their names.
• Those nine electors have to choose forty different councillors. So, from the list of councillors, excluding the electors (but including the twenty-one people from the previous round of thirty who did not become electors), pick 40 members. Print their names.
• The forty were reduced to twelve by lot. Pick 12 from these members at random, print their names.
• The dozen elected twenty-five councillors. You know the rules by now: pick 25 councillors excluding the 12 (but including anyone not in the 12 who partook in previous rounds), and print their names.
• The twenty-five got reduced to nine again. Pick 9 random people from the 25 and print their names.
• Those nine selected forty-five councillors. Pick 45 people not in the 9, and print their names.
• The forty-five were reduced to eleven. Pick 11 random councillors from those 45, and print their names.
• The eleven picked forty-one councillors who would elect the doge. Pick 41 people not in the 11, print their names.
• Finally, these people will elect the Doge of Venice. Pick 1 person, randomly, from the 41. Then print their name. And then you can rest and watch the sun set on a democratic universe.

This horrendously complicated system was made to reduce corruption; so however you implement it, every member of the Great Council must have an equal chance of getting elected in the end.

In tabular form:

Step From Pick New group
1 All 1 A
2 All except A 30 B
3 B 9 C
4 All except C 40 D
5 D 12 E
6 All except E 25 F
7 F 9 G
8 All except G 45 H
9 H 11 I
10 All except I 41 J
11 J 1 Doge

Other rules:

• Print the name(s) exactly as given, with the same capitalisation, etc. There's only going to be readable ascii characters, there's no Nicolò on the council.
• Print every new step of the algorithm on a new line.
• When printing a list of names, separate them in some fashion from one another (not with spaces as the names contain spaces). If you separate them with newlines, then add an extra newline between steps so those remain clearly separated as well.
• This is , so your score is the number of bytes in your code. Lowest score wins!
• Not to be confused with the doge meme, which is way less complicated than the election process Apr 30, 2023 at 13:27
• Related OEIS sequence (without the leading and trailing 1's). Apr 30, 2023 at 21:39

05AB1E, 24 bytes

•10¢–‘Þ}Ò•46вvNÉiK}.ry£=


Print each intermediate result as a list on a separated line to STDOUT.

Try it online.

Explanation:

•10¢–‘Þ}Ò•       # Push compressed integer 70284432464563859
46в             # Convert it to base-46 as list: [1,30,9,40,12,25,9,45,11,41,1]
v            # Loop over each of these integers y:
NÉi }       #  If the 0-based loop-index is odd:
K        #   Remove all names of the current list from the (implicit) input-list
.r     #  Shuffle the list of names
y£   #  Only keep the first y amount of random names
=  #  Print it with trailing newlines (without popping)


See this 05AB1E tip of mine (sections How to compress large integers? and How to compress integer lists?) to understand why •10¢–‘Þ}Ò• is 70284432464563859 and •10¢–‘Þ}Ò•46в is [1,30,9,40,12,25,9,45,11,41,1].

Python, 117 113 112 106 105 bytes

from random import*
def f(l,i=1,x=()):
for y in b"	(	-)":print(x:=sample(i*x or{*l}-{*x},y));i=1-i


Attempt This Online!

Port of Kevin Cruijssen's 05AB1E answer.

-6 thanks to @CursorCoercer
Fixed thanks to @Neil

• 106 bytes by using sample instead of shuffle. May 2, 2023 at 14:33
• @CursorCoercer thanks, updated May 2, 2023 at 14:40
• Your program never worked properly, but I was able to come up with a fix that doesn't change the byte count.
– Neil
May 3, 2023 at 7:51
• @Neil thanks. I'm not completely sure about this, but wouldn't x=[] in the function definition make the function non-reusable? I've changed it to x=() just in case. May 3, 2023 at 15:27
• @TheThonnu That's only a problem if you mutate the array (e.g. via append or pop), which we're not doing here (since otherwise () wouldn't even work), but I guess explicitly using () makes that clearer anyway.
– Neil
May 3, 2023 at 16:41

Charcoal, 49 bytes

ＷＳ⊞υιＦＩ⪪”)¶∨9⊘%ÀsＦ<¦Ｊ”²«≔⎇›ιＬω⁻υωωθ≔⟦⟧ωＦι⊞ω‽⁻θω⟦ω


Try it online! Link is to verbose version of code. Takes input as a list of newline-terminated strings. Explanation:

ＷＳ⊞υι


Input the names.

ＦＩ⪪”)¶∨9⊘%ÀsＦ<¦Ｊ”²«


Loop over the values 01, 30, 09, 40, 12, 25, 09, 45, 11, 41 and 01, extracted from a compressed string split into pairs of digits.

≔⎇›ιＬω⁻υωωθ


If there are more names needed than exist in the previously randomised group, choose them from all of the names not in the previous group instead. (Note that the first time around ω is actually the predefined empty string; its length is still zero and vectorised subtracting it from the list has no effect, so the initial name is effectively selected from the entire list.)

≔⟦⟧ωＦι⊞ω‽⁻θω


Create a new group with the desired number of names randomly selected from the list without replacement.

⟦ω


Output that group. (I have actually double-spaced the groups from each other to make the output easier to read at the cost of a byte.)

Vyxalo, 220 bits1, 27.5 bytes

»ac±†ġq⟑»₆τ2ẇ(Þ℅nhẎ…?$FÞ℅ntẎ…)℅  Try it Online! Prints names in a list, except for the final doge. So wow. Much election. Such obvious joke. Explained (old) »ac±†ġq⟑»₆τyZ(:Þ℅nhẎ…□$FÞ℅ntẎ…)℅
»ac±†ġq⟑»₆τ                      # The list ⟨ 1 | 30 | 9 | 40 | 12 | 25 | 9 | 45 | 11 | 41 ⟩ (26500992315347689 in base 64)
yZ                    # uninterleaved and zipped (creates a list of [next_round, count_electors]) (the first choice - the youngest is technically a special case of next round picks)
(                )  # for each pair n:
:Þ℅                #   push a random permutation of the top of the stack
nhẎ…            #   take the first next_round names from that and print without popping
□$F # remove these names from the list of potential electors Þ℅ntẎ… # take the first count_electors names from a random permutation of the remaining names, printing without popping ℅ # choose a random name from the last pick of 41 and print it with the o flag. This last name is the doge.  • I spent a while looking for the footnote corresponding to "bits<sup>1</sup>", until I clicked on the link for "bytes". Apr 30, 2023 at 13:53 • Also, intentionally using □ is evil, I thought it was a font error at first. Apr 30, 2023 at 13:54 • @Bbrk24 the superscript 1 is the vyncode version, and the □ was supposed to represent taking all inputs and putting them in a box/list - it's not at all meant to look like a font error :p May 1, 2023 at 2:18 Jelly, 27? 28 bytes 27 if we may write a function that produces a list of lists of names: ¹©W;“ċ pr⁻Ṛṫ’ḃ45¤¹®ḟ$ƭẊḣɗ\Ḋ


Try it online!

45“ċ pr⁻Ṛṫ’ḃ¹³ḟ$ƭẊḣY;⁷ṄȧƲʋƒḟ  A full program that accepts a list of strings and prints the results with newline and double newline separators. Try it online! How? 45“ċ pr⁻Ṛṫ’ḃ¹³ḟ$ƭẊḣY;⁷ṄȧƲʋƒḟ - Main Link: list of strings, Councilors
“ċ pr⁻Ṛṫ’                  - base 250 integer = 56917435399420996
45         ḃ                 - bijective base 45 -> [1,30,9,40,12,25,9,45,11,41,1]
ƒ  - starting with current=Councilors reduce that by:
ƭ            -     alternate between:
¹                -       a) no-op -> current
³               -            program input -> Councilors (i.e. "all")
ḟ              -            filter discard (current) -> all except current
Ẋ           -     shuffle           }
ḣ          -     head to index (n) } choose n of them
Y         -       join with newlines
;⁷       -       concatenate a newline
Ṅ      -       print that plus a newline, and yield the result
ȧ     -       logical AND (chosen) -> chosen
ḟ - filter discard all -- avoids printing the Doge again


JavaScript (Node.js), 131 bytes

Returns an array of string arrays.

Contains unprintable characters.

a=>[...Buffer("	(	-)")].map(n=>p=[...Array(n)].map(g=_=>p.includes(s=a[Math.random()*a.length|0])+n/25&!g[s]?g[s]=s:g()),p=a)


Try it online!

Commented

a =>                  // a[] = input array
[...Buffer("...")]    // list of group sizes
.map(n =>             // for each size n:
p =                 //   update p[] to
[...Array(n)]       //   an array of size n where each entry is
.map(g = _ =>       //   the result of the recursive function g:
p.includes(       //     test whether p[] includes:
s =             //       the string s
a[              //       picked from a[]
Math.random() //       at a random location
* a.length    //       (with uniform distribution)
| 0           //
]               //     we invert the parity of the result if ...
) + n / 25        //     ... the group size is ≥ 25
& !g[s]           //     make sure that s was not already selected
?                 //     if this is a valid selection:
g[s] = s        //       return s and mark it as selected
:                 //     else:
g()             //       try again
),                  //   end of inner map()
)                     // end of outer map()


Wolfram Language (Mathematica), 113 bytes

x:=#1~RandomChoice~#2&
c:=l~Complement~%
l~x~1
l~x~30
%~x~9
c~x~40
%~x~12
c~x~25
%~x~9
c~x~45
%~x~11
c~x~41
%~x~1


Now, unfortunately, Try It Online does not have the same implementation of the Mathematica Global Loop as a Mathematica notebook does, so the built-in % operator, which yields the output of the previous line, does not work. But, you can download the Mathematica notebook online.

• Welcome to Code Golf, and nice answer! May 8, 2023 at 1:31

Pyth, 33 bytes

Ou
<.S-Q
<.SGhHeHc5jC"m£*’OŸ"46Q


Try it online!

Explanation

                               # implicitly assign Q = eval(input())
C"..."       # convert string to int using base 256 and codepoints, gives us 1527922444881823
j      46     # convert int to list of ints using base 46, gives us [1, 30, 9, 40, 12, 25, 9, 45, 11, 41]
c5              # chop into five pieces of equal size
u                        Q    # reduce this list on lambda GH, starting with Q
.SG                    #   shuffle G
<   hH                  #   first n elements of, where n is the first value in the pair H
#   print this
-Q                        #   complement in Q
.S                          #   shuffle
<          eH                #   first n elements of, where n is the second value in the pair H
#   print this
O                              # print a random element of the result


Desmos, 306 bytes

Desmos doesn't have strings, so I hope this answer's valid! (Also, I only pasted half the people into it, but copying and pasting was DRIVING ME INSANE, so I just did half.)

C=[1...L]
Y=floor(Lrandom())
A=s(C,30,Y)
B=s(A,9,0)
D=r(40,B)
E=s(D,9,0)
F=r(25,e)
G=s(F,9,0)
H=r(45,G)
I=s(H,11,0)
J=r(41,I)
l(n,a)=shuffle(a)[[1...n]]
r(b,c)=l(b,C[[max({C[i]=c,0})fori=[1...L]]=0])
s(a,b,c)=l(b,a[{a<c,0}+{a>c,0}>0])
Z=[0,0,0]
R=join(Y,Z,A,Z,B,Z,C,Z,D,Z,E,Z,F,Z,G,Z,H,Z,I,Z,J,Z,l(1,J)[1])


(Pasting it in will not work, as [ is not defined normally without typing.)

The graph contains some extra interface formatting. I only counted the stuff outside the Interface folder, as Desmos doesn't have strings and I felt that it was fair.

l returns n random elements from the list a. s is designed to remove an element of value c if it exists (for the first case), and r removes elements in a list. Then, both c and r return b random elements.

Click the button to the right of the plus sign to randomize it again.

R, 88 bytes

Edit: +6 bytes and change of approach to fix bug in previous version (below)

\(m){g=o=seq(m);for(i in utf8ToInt("!>)H,9)M+I!")-32)show(m[g<-sample(o[g*-(T=-T)],i)])}


Attempt This Online!

Function that prints a vector of names for each step of the algorithm; outputted vectors are separated by newlines.

Previous approach (now fixed at a cost of +7 bytes):

R, 89 bytes

\(m){s=1;g=o=seq(m);Map(\(i)m[g<<-sample(o[g*-(s<<--s)],i)],utf8ToInt("!>)H,9)M+I!")-32)}


Attempt This Online!

Function that returns a list with each element containing a vector of the names from one step of the algorithm.