I came up with two solutions, which while inherently different, give the same result. This instils confidence in me that they are correct.
Solution 1
This solution is the one I started working on before I read Sandwich's answer. You can try it here. The idea is to use Buckles algorithm for mapping an lexicographical index (from a die roll) to a selection from my sequence. Let's look at the code:
set "maximum function depth" to 100
function: factorial A:n {
RESULT: 1
loop N over {1..A} { RESULT: RESULT*N }
result: RESULT
}
function: N:n choose K:n {
result: [factorial N]/[factorial K]/[factorial N-K]
}
function: buckles inner CURRENT:n N:n K:n LIMIT:n INDEX:n {
NEWLIMIT: [N-CURRENT choose K] + LIMIT
if NEWLIMIT >= INDEX { result: {CURRENT,LIMIT}}
else { result: [buckles inner CURRENT+1 N K NEWLIMIT INDEX]}
}
function: N:n buckles K:n index INDEX:n {
RESULT: {}
CURRENT: 0
LIMIT: 0
loop I over {1..K-1}{
TUPLE: [buckles inner CURRENT+1 N K-I LIMIT INDEX]
CURRENT: 1@TUPLE
LIMIT: 2@TUPLE
RESULT:{RESULT,CURRENT}
}
result: {RESULT,CURRENT+INDEX-LIMIT}
}
function: select SELECTOR:s from SEQUENCE:s {
RESULT: {}
loop I over SELECTOR { RESULT: {RESULT,I@SEQUENCE} }
result: RESULT
}
function: draw K:n items from SEQUENCE:s at lexicographical index INDEX:n {
result: [select [#SEQUENCE buckles K index INDEX] from SEQUENCE]
}
function: draw from SEQUENCE:s K:n times {
INDEX: d[#SEQUENCE choose K]
result: [draw K items from SEQUENCE at lexicographical index INDEX]
}
SEQUENCE: {1, 1, 1, 1, 1, 3, 3, 3, 5, 5, 7}
output [draw from SEQUENCE 4 times] named "mydie"
The factorial and choose functions are self explanatory. The first calculates 123....*n, and the second one gives the total number of different ways to get k item out of n, in my case 4 out of 11. buckles and buckles inner implement the Buckles algorithm itself. If we pass N=11, P=4 and X=1 to the buckles function we will receive {1,2,3,4}. If we pass N=11, P=4 and X=2, we will receive {1,2,3,5} and so on an so forth until we go all the way to N=11, P=4 and X=330 to get {8,9,10,11}. Note that 330 here is [11 choose 4].
select is a helper function, that takes a selector, a sequence and returns the resulting sequence. For example for selector {1,6,7,11} and sequence {1, 1, 1, 1, 1, 3, 3, 3, 5, 5, 7} the result will be {1,3,3,7}.
draw items function selects K items from the given sequence at a given lexicographical index. For example with K = 4 at index 1 and sequence {1, 3, 5 , 9, 11} the result will be {1, 3 , 5 , 9}.
The second draw function allow you specifying a sequence and the number of items to draw from it. That will be the die.
Solution 2
This one is completely inspired by sandwich's answer. You try it here. And here is the code:
function: remove N:n from SEQUENCE:s {
RESULT:{}
REMOVED: 0
loop C over SEQUENCE {
if (REMOVED = 0) & (C=N) { REMOVED: 1 } else { RESULT: {RESULT,C} }
}
result: RESULT
}
function: draw from SEQUENCE:s with X:n and RESULT:s C:n more times{
if C = 0 {
result: RESULT
} else {
SEQUENCE: [remove X from SEQUENCE]
result: [draw from SEQUENCE with dSEQUENCE and {RESULT,X} C-1 more times]
}
}
function: draw from SEQUENCE:s N:n times {
result: [draw from SEQUENCE with dSEQUENCE and {} N more times]
}
SEQUENCE:{1, 1, 1, 1, 1, 3, 3, 3, 5, 5, 7}
output [draw from SEQUENCE 4 times] named "mydie"
I was not sure how to remove a value from an existing sequence, so I had to write my own function for that. It's remove. The two draw functions implement sandwich's idea of rolling a die and then removing the result from the die sequence. There are two of them, because one of them is recursive and the other bootstraps the first one.
I can tell you that the first solution took me about 3 hours, and the second one only a few minutes. Well done sandwich!