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)) is 0 when value is 0, it is also 0 for every other possible int.
  • (value / max) is slightly better since it is 0 when value is 0, and 1 when value is max, but it is still 0 for every other possible int.
  • value % (max + 1) is 0 when value is 0, and is 1 when value is 1, it is value (not 1) otherwise. (Using max 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);
    }

License

Icon for the Creative Commons Attribution 4.0 International License

Patterns for Beginning Programmers Copyright © 2022 by David Bernstein is licensed under a Creative Commons Attribution 4.0 International License, except where otherwise noted.

Share This Book