Thank your for the explanation! The modulo was a big piece that I was missing.
I still need to spend some time understanding how the math guarantees unique combinations of values, but I did figure out how to generate the series now (in Ruby).
(0..4).map do |b|
(0..4).map do |a|
(0..4).map do |x|
(a * x + b) % 5
end.join(" ")
end.join("\n")
end.join("\n\n").then { puts _1 }
EDIT: To make this line up with the document, I actually had to offset a by the value of b. I'm still not totally sure why this is necessary.
(0..4).map do |b|
(0..4).map do |a|
(0..4).map do |x|
((a - b) * x + b) % 5
end.join(" ")
end.join("\n")
end.join("\n\n").then { puts _1 }
Both versions of your code generate the same polynomials (i.e., rows of the table), but in a different order.
The order doesn't affect psst. This is why each page of the worksheet shows the polynomials in the order that's most convenient for the situation at hand.
The only place where the order matters is in choosing which dice throw is assigned to which polynomial. In psst, the dice throws correspond to share #1.
I still need to spend some time understanding how the math guarantees unique combinations of values, but I did figure out how to generate the series now (in Ruby).
EDIT: To make this line up with the document, I actually had to offset a by the value of b. I'm still not totally sure why this is necessary.