This problem of a label – or more correctly, problem of disposing with a
label -– we'll look at in solving an ancient English puzzle about fishermen and
fish:
"Three fishermen
lay down to sleep, not having counted or divided their catch. In the night, one
of them woke up, and (not quite trusting the others) decided to divide the pile
of fish and take his share. But the number of fish wasn't visible by three.
However, he found he could throw one fish away, then take exactly a third. This
he did (noting, in the true spirit of fairness, that an even number of fish was
left for his comrades) and went back to sleep. Later, the second and third
fishermen woke in turn, and each went through the same process. The question
is, what is the minimum number of fish in the catch that fulfils these
conditions?"
The reader might like first to try to solve
this task by hand; but we'll involve the computer in this for two reasons.
Firstly, it lets us consider some ways to structuralize programs in the Mathcad
environment: methods of clearing programs of labels, and 'squeezing' them into
the rigorous framework of structural design. Secondly, we'll show that the
Fishermen's Problem has, until now, been solved incorrectly...
This problem can be solved in Mathcad without
resorting to programming. Fig. 6.22 shows how this
might be done how by successive manual approximations, with Mathcad just
automating the division procedure. We set a first guess at 50 fish and see if
the division works; if not, we try 49; and so on, decrementing the guess by one
if the division produces a non-integer at any stage. Eventually, we guess at 25
fish, and this proves to be a solution. The first fisherman throws away a fish,
and takes eight, leaving 16 (eight each for the others, he thinks). The second
(not knowing what the first has done) leaves ten; and the third, six.
This solves the task – but by applying 'manual'
work in setting the value of the variable Catch and
decrementing the value of the variable Answer.
(Note that duplicating the block of operators fixing the actions of the three
fishermen, though done for clarity in Fig 6.22, isn't necessary.
It's sufficient to decrement Answer
and keep track of Catch).
Let's try to automate the search for a solution
to this task.
'1 Initial unstructured BASIC program
Input "Guess";
Answer
label:
Catch = Answer
For Fisherman = 1 To 3
Catch = Catch – 1
Catch = Catch - Catch /
3
If Catch > Int(Catch)
Then Answer = Answer - 1: Goto label
Next
Print
"Answer "; Answer; “fish”
'2. First stage of structuralization: a start
Input
"Guess"; Answer
Answer =
Answer + 1 ‘ step backward
label: Answer
= Answer - 1 ‘ step forward
Catch = Answer
For
Fisherman = 1 To 3
Catch = Catch – 1
Catch = Catch - Catch /
3
If Catch > Int(Catch)
Goto label
Next
Print
"Answer "; Answer; “fish”
'3. Second stage of structuralization: addition of an
attribute
Input
"Guess"; Answer
Answer =
Answer + 1
label:
Answer = Answer – 1
Catch =
Answer
Divided = "yes" ’
indicates catch divided
For
Fisherman = 1 To 3
Catch = Catch – 1
Catch = Catch - Catch /
3
If Catch > Int(Catch)
Then Divided = “no”
Next
If Divided =
“no" Goto label
Print
"Answer "; Answer; “fish”
4. Third step of structuralization: disposal of the
label
Input
"Guess"; Answer
Answer =
Answer + 1
Do ’ start loop
with checking on exit
Answer = Answer – 1
Catch = Answer
Divided = "yes"
For
Fisherman = 1 To 3
Catch = Catch – 1
Catch = Catch - Catch /
3
If Catch > Int(Catch)
Then Divided = “no”
Next
Loop Until Divided = "yes" ’ end loop
Print
"Answer "; Answer; “fish”
Fig. 6.23 shows four
BASIC programs to automate the solution of the Fishermen's Problem. In all
four, the operator Input requests the first guess (50 fish, for example) and the operator Print gives out the answer: 25 fish.
The first program follows exactly our 'manual'
algorithm. Inside the loop for the three fishermen's successive leavings, we
watch until we find fractional 'tails': that is, the parameter Catch is not an integer (the built-in
BASIC function Int chops off these
tails; the Mathcad analogue is the floor
function). Then the guess decrements by one (Answer = Answer - 1) and the program control passes to the labelled
statement (Goto label).
Before this program can be translated to
Mathcad's programming language, we need to get rid of the label. And not only
because in Mathcad's programming arsenal there is no facility to jump to a
label, either conditionally or unconditionally, but for other reasons
unconnected with Mathcad. In our 'toy' program (item 1 in a Fig.
6.23) the label is quite pertinent and natural, but if a program with
labels expands, it becomes difficult to understand and practically impossible
to debug and develop. The result, as structural programming wizards rightly
emphasise, becomes like spaghetti: try pulling out a block of statements for
separate debugging or compilation, and you find adhering strings of Goto links. Besides, such program can't
be created by a group of developers ('bottom-up' technology). The first implementations
of the Pascal language had no labels at all, as Pascal was developed by Niklaus
Wirth for training students in structural programming. The label has appeared
only in the late versions. So I think our view on learner programmers and the
label must be, "Hide matches from children"!
The first step in structuralizing the BASIC
program (see item 2 in Fig. 6.23) is moving the
decrement code from the conditional statement to the label. That is:
If Catch > Int (Catch) Then Answer = Answer - 1: Goto
label
becomes
label:
Answer = Answer - 1
...
If Catch > Int(Catch) Goto label
There is another necessary change. Rather like
an athlete taking a step back before a jump, we must insert Answer = Answer
+ 1, because when
the program is run, label begins by decrementing Answer. Structuralizing generally
complicates the algorithm a little: remember that "There's no such thing
as a free lunch", "Beauty is only achieved by suffering", and so
on.
The second step in structuralizing (see item 3)
is extracting the conditional jump from the body of the For loop. For this purpose, we
introduce an auxiliary binary variable, Divided, indicating successful or unsuccessful division. Inside the loop, the
conditional jump to label is replaced by an "alternative with one shoulder" (that is, Divided stores the result of the binary
choice, carrying it to a single exit from the loop – one of the basic
structural managing designs). The conditional jump 'slips' down and outside the
loop.
After this (see item 4) the program can be
modified with one more basic of structural design: a loop with checking on
exit, which performs the same function as label and If ... Goto label in item 3. This finally enables the program to completely dispense with
labels: the outer loop with exit checking, containing an inner For loop with its 'alternative with one
shoulder'.
After all these manipulations, the fourth
variant of the BASIC program is the one possible to copy to Mathcad (see Fig. 6.24). It's necessary to define it as a user
function, as there are no Input and Print operators in the Mathcad language. Their analogues (the operators [ ]:= [ ] and [ ]=) work, alas, only outside of
programs.
In Fig. 6.24 the result
of calling the function Answer are shown for various input values: 50, 24, -3 and even -30 fish. The
English physicist and early quantum theorist Paul Dirac postulated not only
antiparticles but "antifish". In one version of a well-known story[1],
he is remembered as saying that 25 fish was an incorrect solution to this
problem, and that the correct answer is -2 fish (or two antifish). If we start
with two antifish, throwing away one away gives three antifish; we take our
share of one antifish, leaving two antifish .. and so on indefinitely. Our computer
solution of the problem shows that even Dirac was mistaken: "Paul, you are
not right!" The conditions of the problem are fulfilled by an infinity of
numbers – we'll call it a Dirac of fish – with a step of 27 (i.e. -2, 25, 52,
etc).
To avoid being an ultra-pedant[2],
it's possible to insert at the end of the for loop in Fig. 6.24 a statement break if
Divided = "no" to interrupt the loop's operation even before
all the fishermen have taken their share. If there aren't three fishermen but
more (for example, thirty-three – a task for the reader), this device
essentially speeds up the program's operation (see the heading
"Optimization of Mathcad programs"). The task can be complicated even
more, generalizing to any number of fisherman sharing, or discarding, any
number of fish.
The Fishermen's Problem can be solved in
another way, by reversing it. Rather than making an initial guess for the
catch, we can assume that the last fisherman leaves two fish (it can't be
fewer) and increment that number by one until the conditions of the task can be
fulfilled. This will solve the problem faster, and we won't ever get solutions
of -2 or -29 fish (the paradigm "Keep it
simple, stupid!" applies here). It's psychologically difficult for humans
to appreciate negative solutions, but the machine does it quietly, without any
prejudices. A more thought-out analytical solution of this task – the search
for integer roots of one equation with two unknowns – doesn't give the negative
answer.
[1]In another version, the story involves shipwrecked sailors dividing a
heap of coconuts, the spare coconuts being thrown to a monkey. As the 'Monkey
and Coconut Problem', this has been analysed in detail using the number theory
of Diophantine equations. For five sailors, the minimum positive number of
coconuts is 15621 (Martin Gardner comments that with a Dirac-type solution of
four anticoconuts, the monkey benefits most from the process, as it receives a
positive coconut at each stage of the division).
[2]There's a Russian word «çàíóäà», meaning "boringly pedantic to the point
of irrationality".