I have a few friends in the engineering program at Purdue. They have to take a class called CS159, which formally teaches them the programming language C, presumably with its applications to engineering. I don’t really know the details, as I neither am in their program nor the class.
They had an assignment that revolves around a user selecting between three given formulas, inputting an argument and receiving the computed value. The catch? If-statements are entirely illegal. The majority of C is similarly off limits.
The approach they are told to use is to generate some expression in C that based on two values, an integer that stores the choice between equations 1, 2 and 3 and the actual argument, that would both select the desired formula and evaluate the argument in the formula. I don’t remember the exact term, and Google isn’t helping with my searches, but I believe it to be something along the lines of “selection by computing.” [EDIT:] The official term is selection by calculation. Anyways, the solution the TAs and students came up with is the following, where is a variable that stores the choice of formula and
is the argument. Here, I will use
to designate the nth formula.
The idea is that if , only
should be evaluated and not the rest. How is this done? We wish for
to, well, exist. and not the other two functions. The easiest way is to have some function in terms of
that is 1 when
is 1, but 0 for all other values. We also need a function that is 0 when
is not 2 but 1 if it is, and likewise for
is 3. Their method takes advantage of how rounding in C works.
is 1 when
is 1, but a number less than 1 for all other relevant values
. By casting it to an integer, it is always rounded down to the greatest integer less than it. That is, 1 will stay as 1, but 0.5 would become 0.
- If
,
, which is rounded down to 1. This means if the user selects formula 1, formula 1 will be run.
- If
,
which is rounded down to 0. This means if the user selects formula 2, formula 1 will not be run.
- The same logic for
applies here.
The second term takes advantage of modular arithmetic. It says to increment the choice variable by 1, divide by two then take the remainder.
- If
,
, which has remainder 0 modulo 2. This means if the user selects formula 1, formula 2 will not be run.
- If
,
which has remainder 0 modulo 2. This means if the user selects formula 2, formula 2 will be run.
- The same logic for
applies here.
The third term again takes advantage of how rounding works.
- If
,
, which is rounded down to 0. This means if the user selects formula 1, formula 3 will not be run.
- The same logic for
applies here.
- If
,
, which is rounded down to 1. This means if the user selects formula 3, formula 3 will be run.
This is incredibly ad-hoc and not systematic whatsoever. Worse still, the methods here will not work for more choices. What if we had four choices? Only the very first term will work as expected. What if we had exactly 73 choices? What should the 63rd term be?
I had my own expression before I saw the one above. It’s very computationally expensive, with so it’s more of a novelty than anything, but it generalizes nicely to certain
. I jokingly called it the Patel formula after one of my friends, and then later called it the Herr-Patel formula or HPF.
Again, I think they should just be allowed to use if-statements, but I digress.
The idea is for the terms, if our choices are 1-3, and we only want, say, to give a value but the rest to be 0, we can have
as our term. This is trivially 0 for
but not when
. We can then change the factors of this polynomial to filter out different values of
and append those to the specified functions.
The terms are then evaluated modulo 3. This is done for two reasons. (In the original formulation, the factors were actually just
. I made the change to make the formula more readable; note that the two factors function identically modulo 3) Note that if one takes an integer modulo
, the set of inputs ranges from 0 to
. I could have written the expression to be mapped from 0 to 3, but this would require a total of 4 terms. Instead, I “remapped” the
term to the
term by taking the expression modulo 3. Doing so means our inputs will now be from 0 to 2 instead of 0 to 3.
The second reason, which actually is not satisfied, is to hold magnitudes constant at 1. So instead of working with or something, it would just be
. This actually didn’t work out; they all turned out to be 2. At the time, this was good enough. Simply divide by two at the very end, and no harm, no foul.
I actually came up with the equation generalizing from a much simpler case for when ranged from 1 to 2. A subconscious design goal was to have an expression that generalized easily to however many
was needed.
In the formula presented above, it works for the case where . To generalize the formula for a greater number of choices, one needs to add a new term to allow another choice to be made and then give each term a new factor. So for the
case, for instance,
There’s a problem, however. The generation of these expressions require that given a certain choice , it should output a single function, and have the rest zero out, by creating a polynomial that filters out all other choices. This does not work in general. This formula does not, and can not exist in general for more than five choices. Further, in the
case, the entire expression is divided by two. Without blindly testing, how do we know what to divide by?
This is actually a problem in number theory.
The explanation and the mechanics behind it makes use of the fact that integers modulo prime numbers are a field, but integers modulo composite numbers form a ring. These are the same ideas that prove Wilson’s theorem, with the help of Lagrange’s theorem, but this explanation is quite technical and I can’t wrap my head around it myself. Instead, I will give a non-rigorous, incomplete proof, that explains the main ideas specific to this problem.
Consider what happens when we take the first term of the supposed case,
and evaluate it at
. This should give us zero, and it does so trivially. However, what happens if we have
? Writing this term out explicitly gives
, which partially simplifies to
, which is equivalent to
. And therein lies the problem. In that expression, we evaluate
, which equals six, which becomes zero modulo six. Instantly, the entire expression goes to zero. Boom.
But wait, that’s a problem! It’s not supposed to go to 0. This term is supposed to be 0 when , but be something when
does equal 1. This problem happens for all of the terms; no matter what the choice is for
, the term is zero. The entire expression as a whole, will evaluate to zero, becomes no matter what, a
will slip in there somehow, making the entire thing zero.
And like that, the formula breaks. However, this problem can be avoided. In the 6 choices case, we had a problem where no matter what, a slips in and instantly breaks the problem. There were two numbers that, no matter what, multiplied to be the modulus. So, we need to avoid instances where two numbers can possibly multiply to our modulus.
Those numbers are the prime numbers, as they are only divisible by themselves, 1 and no other numbers. So for 2, 3, 5, 7 and so on choices, the formula looks good. I actually don’t know why 4 works.
The last step is to find out what to divide all the numbers by. This is just turns out to be , or one less than the number of choices. Why? Consider the second term for 3 choices,
. If
, then the term becomes
, which simplifies to become
, equivalent to
, or
or
. In fact, since each term is just the previous term but just shifted to filter out different values, if a term doesn’t go to zero, it is equivalent to
. By Wilson’s theorem, this is
.
And like that, we are done. For prime cases, we have an expression that, when given a variable
that stores a choice of term and a bunch of functions
to be evaluated at
, outputs the specific function evaluated at
. In summation form, it is given by
.