7 Indicator Methods
Sometimes the value needed to perform a calculation is known, and other times it too must be calculated. This is true of both “traditional” values and indicators (of the kind discussed in Chapter 6). This chapter considers problems in which an indicator’s value must be calculated before it can be used in another expression.
Motivation
There’s a famous saying among performers, “the show must go on”, which means that there must be a show whenever there’s an audience. In the context of Chapter 6 on indicators, this means that the number of seats sold for a particular showtime must be used to calculate an indicator, setting it to 0
when no tickets have been sold and setting it to 1
otherwise. This is an example of a threshold indicator in which the threshold is 1.
So, given that some number of tickets has been sold for a particular show time, you must determine the number of shows that must be offered at that time (i.e., either 0
or 1
). Letting shows
denote the number of shows and sold
denote the number of tickets sold, you want to calculate shows
from sold
in such a way that it takes on the value 0
when sold
is 0 and it takes on the value 1
for all positive values of sold
.
Review
You could, of course, calculate the value of the variable shows
as follows:
if (sold >= 1) { shows = 1; } else { shows = 0; }
However, you should also know that this is a bad practice, since it is likely that you will need to use this simple algorithm in more than one place. Hence, to avoid code duplication, you should instead write a method like the following:
public static int shows(int sold) { if (sold >= 1) { return 1; } else { return 0; } }
and use it as needed.
The Pattern
The entertainment example is, obviously, a very particular problem. However, it can easily be generalized to uncover a pattern. In particular, the entertainment problem is a particular example of a general problem in which you need to determine whether a particular value exceeds a particular threshold. Further, there is an even more general problem in which you need a method that returns a value of 0
or 1
based on the value of the parameters it is passed.
For threshold indicators, the solution to the problem is the following method:
public static int indicator(int value, int threshold) { if (value >= threshold) { return 1; } else { return 0; } }
For other indicators, the solution is a method with parameters that vary with the specifics of the problem.
Examples
It’s useful to consider examples of both threshold indicators and other indicators.
Threshold Indicators
Continuing with the entertainment example, the threshold is 1
(i.e., if the size of the audience is 1 or more then there must be a show), so given the size of the audience (represented by the variable size
), the number of shows is given by the following:
shows = indicator(sold, 1);
Returning to the parking ticket example used in Chapter 6, letting the number of prior tickets be given by priors
, the fine can be calculated as follows:
baseFine = 10.00; repeatOffenderPenalty = 35.00; totalFine = baseFine + (indicator(priors, 1) * repeatOffenderPenalty);
It is also instructive to return to the car rental example from Chapter 6. For this example, you can initialize the necessary variables as follows:
baseRate = 19.95; ageSurcharge = 10.00; ageThreshold = 25; multiSurcharge = 5.00; multiThreshold = 1;
Then, you can calculate the daily rental rate as follows:
rate = baseRate + (1 - indicator(minimumAge, ageThreshold)) * ageSurcharge + indicator(extraDrivers, multiThreshold) * multiSurcharge;
Notice that, the age surcharge uses a converse threshold indicator while the multi-driver surcharge uses an ordinary threshold indicator.
Other Indicators
As an example of a more general indicator, consider the following method for calculating a person’s basic metabolic rate (BMR):
[latex]b = 5.00 + 10.00 m + 6.25 h - 5.00 a - 161.00 \delta_f[/latex]
where [latex]b[/latex] denotes the BMR (in kilocalories per day), [latex]m[/latex] denotes the person’s mass (in kilograms), [latex]h[/latex] denotes the person’s height (in centimeters), [latex]a[/latex] denotes the person’s age (in years), and [latex]\delta_f[/latex] is [latex]1[/latex] if the person is female and [latex]0[/latex] otherwise.
Now, suppose the double
variables m
, h
, and a
contain the values of the person’s mass, height, and age (respectively), and the String
variable s
contains the person’s sex. Then, you would create the following indicator method:
public static int indicator(char sex) { if ((sex == 'F') || (sex == 'f')) { return 1; } else { return 0; } }
and use it as follows:
b = 5.00 + 10.00 * m + 6.35 * h - 5.00 * a - 161.00 * indicator(s);
Some Warnings
As in the discussion of indicator variables in Chapter 6, you might think that you should use boolean
variables, if
statements, and the updating pattern from Chapter 1 instead of indicator methods. The trade-offs here are the same as the trade-offs there.
It is also important to realize that you shouldn’t over-generalize the code that is used in this pattern. Suppose, for example, you needed to assign the value true
to the boolean
variable isLegal
if and only if the value in the variable age
is greater than or equal to the value in the “constant” DRINKING_AGE
. Given the code used to implement the pattern, you might be tempted to do something like the following.
if (age >= DRINKING_AGE) { isLegal = true; } else { isLegal = false; }
However, most people think this is completely inappropriate (and demonstrates a lack of understanding of relational operators and boolean
variables). Instead, it should be implemented as follows:
isLegal = (age >= DRINKING_AGE);
That is, the expression (age >= DRINKING_AGE)
evaluates to a boolean
that should be directly assigned to the variable isLegal
— the if
statement is superfluous. In other words, a pattern that is appropriate for assigning values to numeric variables or returning numeric values (like those in the previous section) may not be appropriate for boolean
variables.
Looking Ahead
On some hardware architectures, if
statements (including the boolean
expressions that must be evaluated) take more CPU time to execute than arithmetic operations. Hence, on such architectures it is important to replace if
statements with arithmetic expressions. Threshold indicators are an example of this that might arise in a course on computer architecture.
If you’ve read Chapter 4 on arithmetic on the circle, you’re may be thinking about how you can use the integer division operator and/or the remainder operator to eliminate the if
statements. While it is, indeed, possible to do so, the solution is not obvious. Consider the following candidates:
(value / (max + 1))
is0
whenvalue
is0
, it is also0
for every other possibleint
.(value / max)
is slightly better since it is0
whenvalue
is0
, and1
whenvalue
ismax
, but it is still0
for every other possibleint
.value % (max + 1)
is0
whenvalue
is0
, and is1
whenvalue
is1
, it isvalue
(not1
) otherwise. (Usingmax
rather than(max + 1)
in the denominator does not improve things.)
The easiest way to think about a solution that does work is to consider the problem in three stages. First, consider the case when the threshold is less than twice the value. Then, consider the special case of a non-zero threshold indicator (i.e., when the threshold is 1
). Finally, consider the general case (i.e., the threshold can be anything).
A Threshold Less Than Twice the Value
When value
is strictly less than (2 * threshold)
, it follows that (value / threshold)
is going to be either 0
or 1
(because, using real division, the result will always be less than 2
). Hence, in this case, you can find the indicator
as follows:
indicator = (value / threshold);
Unfortunately, this won’t work in general because we don’t always know value
.
A Threshold of 1
One way to get to the correct solution to the special case when the threshold is 1
is to find max
consecutive integers that have the property that each divided by some value is 1
.
To move in the right direction, observe that for value
in the set [latex]\{\texttt{1}, \texttt{2}, \ldots, \texttt{max}\}[/latex] it follows that (value + max)
is an element of the set [latex]\{\texttt{(1+max)}, \texttt{(2+max)}, \ldots, \texttt{(value+max)}\}[/latex]. Since value
is less than or equal to max
, it also follows that each element of this set is less than or equal to (2*max)
. Hence, the result of dividing each element by (max+1)
(using integer division) is 1
(because using real division the result would be strictly between 1
and 2
).
To finalize the pattern when the threshold is 1
, observe that (0 + max) / (max + 1)
is 0
(using integer division), since the numerator is strictly less than the denominator. What this means is that the pattern when the threshold is 1
can be expressed as the following non-zero indicator:
indicatorNZ = (value + max) / (max + 1);
The General Case
In the general case, you need to create an indicator that is 0
when a non-negative value
is less some threshold
and 1
otherwise. The easiest way to create such an indicator is to first calculate an intermediate value that is 0
when the value
is less than the threshold
and positive otherwise, and then use a non-zero indicator.
To do so, observe that, since both value
and threshold
are in the interval [latex][\texttt{0}, \texttt{max}][/latex], it follows that, as required, (value / threshold)
is 0
when value
is less than threshold
and that it is in [latex][\texttt{1}, \texttt{max}][/latex] otherwise. Hence, you can calculate intermediate
as follows:
intermediate = value / threshold;
Then, you can use intermediate
and the expression for a non-zero indicator to calculate a threshold indicator as follows:
indicatorT = (intermediate + max) / (max + 1);
Putting it all together yields the following general expression for a threshold indicator:
indicatorT = ((value / threshold) + max) / (max + 1);
To see that this is, indeed, a generalization of the special case, you need only substitute 1
for threshold
in this expression.
This leads to a general-purpose method like the following for calculating the threshold indicator without an if
statement:
public static int aindicator(int value, int threshold, int max) { return ((value / threshold) + max) / (max + 1); }