New blog post: Puzzled Programmers
This commit is contained in:
parent
9f7637d1d0
commit
3f5cc2a20c
1 changed files with 332 additions and 0 deletions
332
_posts/2017-09-21-puzzled-programmers.md
Normal file
332
_posts/2017-09-21-puzzled-programmers.md
Normal file
|
|
@ -0,0 +1,332 @@
|
|||
---
|
||||
layout: post
|
||||
title: Puzzled Programmers
|
||||
date: 2017-09-21 10:00:00
|
||||
permalink: /blog/2017/09/21/
|
||||
machines:
|
||||
- id: ibm5160-msdos320
|
||||
type: pcx86
|
||||
resume: 1
|
||||
config: /devices/pcx86/machine/5160/ega/640kb/machine.xml
|
||||
drives: '[{name:"10Mb Hard Disk",type:3,path:"/disks/pcx86/fixed/10mb/MSDOS320-C400.json"}]'
|
||||
autoMount:
|
||||
A:
|
||||
name: None
|
||||
B:
|
||||
name: None
|
||||
---
|
||||
|
||||
One of books I used to have a copy of (and perhaps still do, in the bowels of my storage unit) was "Puzzled Programmers"
|
||||
by Michael Wiesenberg. It was published by Microsoft Press in 1987, and I recently rediscovered an online copy in the
|
||||
[Internet Archive's](https://archive.org) [Open Library](https://openlibrary.org):
|
||||
|
||||
> [Puzzled Programmers: 15 mind-boggling story puzzles to test your programming prowess, solutions in BASIC, Pascal, and C](https://openlibrary.org/books/OL2379315M/Puzzled_programmers)
|
||||
|
||||
Someone else was currently "borrowing" it, so I added myself to the wait-list, and a few days ago, the Internet Archive
|
||||
notified me that it was available for reading. I started skimming it, and noticed that for each of the puzzles, it
|
||||
included complete solutions in all the aforementioned languages: BASIC, Pascal, and C. I also noticed this portion of
|
||||
the Introduction:
|
||||
|
||||
> **Timing the Programs on Your Computer**
|
||||
|
||||
> Run times are specified for each puzzle, but you should look at these times only as guidelines or, in the vernacular,
|
||||
as ballpark figures. The figures given are for a Hewlett-Packard Vectra (IBM PC AT compatible) running at a clock speed
|
||||
of 8 MHz. Obviously, the times will differ with other interpreters and on other computers. The following table gives you
|
||||
some idea of how long a program ought to take on your particular system. This table shows timings on various systems
|
||||
of the programs presented in the solution to Puzzle 5.
|
||||
|
||||
Computer BASIC Turbo Pascal Microsoft C
|
||||
-------------------------------------------------------------------------------
|
||||
Tandy 1000 (4.77 MHz) 2 minutes 11 seconds 1.3 seconds 2.4 seconds
|
||||
IBM PC XT clone (4.77 MHz) 1 minute 58.5 seconds 1.3 seconds 2.3 seconds
|
||||
IBM PC A T (6 MHz) 50 seconds 0.5 seconds 0.9 seconds
|
||||
HP Vectra (8 MHz) 35 seconds 0.5 seconds 0.4 seconds
|
||||
Macintosh Plus 8 seconds < 1 second < 1 second
|
||||
Apple II C 2 minutes 20 seconds 23.6 seconds* n/a
|
||||
-------------------------------------------------------------------------------
|
||||
*Apple Pascal
|
||||
|
||||
This almost seemed like an invitation to try my hand at Puzzle #5, and to see how a PCjs "machine" fared.
|
||||
|
||||
### A Pleasant Fourthsum
|
||||
|
||||
I skipped ahead to page 27, "Puzzle 5: A Pleasant Fourthsum", and started reading. I have to admit that the story
|
||||
completely failed to interest me. There were lots of irrelevant details about a delicious breakfast that this imaginary
|
||||
bunch of successful twenty-somethings were enjoying, when suddenly one of them says:
|
||||
|
||||
"Before we get into the future of languages at I-Q, I've got a great computer puzzle for everyone."
|
||||
|
||||
which he then states in one sentence:
|
||||
|
||||
"Find a four-digit number that is the sum of the fourth powers of its digits."
|
||||
|
||||
and then the conversation returns to other random topics, like whether anyone should be worried that FORTRAN might
|
||||
become a weapons guidance language, and whether they need to make lunch reservations -- when they've barely finished
|
||||
breakfast!
|
||||
|
||||
Skipping ahead to the "Solutions" section, it turned out that the challenge was to actually find *all* four-digit
|
||||
numbers, not just *a* number. Also, only numbers 1000 through 9999 are considered (no numbers with leading zeros).
|
||||
|
||||
Just for fun, I created [my own solution](/tests/node/puzzled/puzzle5.js) in JavaScript first:
|
||||
|
||||
```javascript
|
||||
let p = new Array(10);
|
||||
|
||||
/**
|
||||
* test(n)
|
||||
*
|
||||
* Returns true IFF the number matches the criteria.
|
||||
*
|
||||
* @param {number} n
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function test(n) {
|
||||
let total = 0;
|
||||
let value = n;
|
||||
while (n) {
|
||||
total += p[n % 10];
|
||||
n = (n / 10)|0;
|
||||
}
|
||||
return total == value;
|
||||
}
|
||||
|
||||
function run() {
|
||||
let n = 1000;
|
||||
for (let d = 0; d < 10; d++) {
|
||||
p[d] = Math.pow(d, 4);
|
||||
}
|
||||
while (true) {
|
||||
if (test(n)) console.log(n);
|
||||
if (++n > 9999) break;
|
||||
}
|
||||
}
|
||||
|
||||
run();
|
||||
```
|
||||
|
||||
then I looked at the solutions in "Puzzled Programmers". Here's the BASIC solution:
|
||||
|
||||
```basic
|
||||
5 REM BASIC Solution to Puzzle #5 in "Puzzled Programmers" (c) 1987
|
||||
10 DIM PWR(9)
|
||||
20 FOR I = 0 TO 9
|
||||
30 PWR(I) = I^4
|
||||
40 NEXT I
|
||||
50 H000 = 1000
|
||||
60 FOR H = 1 TO 9
|
||||
70 I00 = 0
|
||||
80 FOR I = 0 TO 9
|
||||
90 J0 = 0
|
||||
100 FOR J = 0 TO 9
|
||||
110 PARTSUM = H000 + I00 + J0
|
||||
120 PART4THS = PWR(H) + PWR(I) + PWR(J)
|
||||
130 FOR K = 0 TO 9
|
||||
140 SUM = PARTSUM + K
|
||||
150 IF PART4THS + PWR(K) <> SUM THEN 190
|
||||
160 PRINT SUM; "= ";
|
||||
170 PRINT USING "#^4 + "; H, I, J;
|
||||
180 PRINT USING "#^4"; K
|
||||
190 NEXT K
|
||||
200 J0 = J0 + 10
|
||||
210 NEXT J
|
||||
220 I00 = I00 + 100
|
||||
230 NEXT I
|
||||
240 H000 = H000 + 1000
|
||||
250 NEXT H
|
||||
```
|
||||
|
||||
and the C solution (which I modified to loop 100 times, to make timing with a stopwatch easier):
|
||||
|
||||
```c
|
||||
/* C solution to puzzle #5 in "Puzzled Programmers" (c) 1987 */
|
||||
main()
|
||||
{
|
||||
int pwr[10], /* the fourth power of each digit */
|
||||
h, /* the digit in the thousands position */
|
||||
h000, /* h * 1000, that is, the value of the number
|
||||
in the thousands position */
|
||||
i, /* the digit in the hundreds position */
|
||||
i00, /* i * 100, that is, the value of the number
|
||||
in the hundreds position */
|
||||
j, /* the digit in the tens position */
|
||||
j0, /* i * 10, that is, the value of the number
|
||||
in the tens position */
|
||||
part4ths, /* h^4 + i^4 + j^4 */
|
||||
partsum, /* h000 + i00 + j0 */
|
||||
k, /* the digit in the ones position */
|
||||
sum, l;
|
||||
|
||||
for (i = 0; i < 10; i++) pwr[i] = i * i * i * i;
|
||||
for (l = 100; l-- > 0;) {
|
||||
for (h000 = 1000, h = 1; h <= 9; h++, h000 += 1000) {
|
||||
/* thousands digit */
|
||||
for (i00 = i = 0; i <= 9; i++, i00 += 100) {
|
||||
/* hundreds digit */
|
||||
for ( j0 = j = 0; j <= 9; j++, j0 += 10) {
|
||||
/* tens digit */
|
||||
partsum = h000 + i00 + j0;
|
||||
part4ths = pwr[h] + pwr[i] + pwr[j];
|
||||
for ( k = 0; k <= 9; k++) { /* ones digit */
|
||||
sum = partsum + k; /* this produces the four-digit
|
||||
number */
|
||||
if (part4ths + pwr[k] == sum) {
|
||||
if (!l) printf("%d = %d^4 + %d^4 + %d^4 + %d^4\n",
|
||||
sum, h, i, j, k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here's my own C solution, which is slightly smaller and faster than the book's solution:
|
||||
|
||||
```c
|
||||
/* @jeffpar's optimized C solution to puzzle #5 */
|
||||
main()
|
||||
{
|
||||
int p[10];
|
||||
int d, l, n, s;
|
||||
for (d = 0; d < 10; d++) p[d] = d * d * d * d;
|
||||
for (l = 100; l-- > 0;) { /* loop 100 times for timing purposes */
|
||||
for (n = 100; n < 1000; n++) {
|
||||
/* I originally used a "do...while (t /= 10)" loop here
|
||||
to create the power summation (where t was a copy of n),
|
||||
but since we know n is a 3-digit number, we can inline
|
||||
all three power references and minimize the number of
|
||||
divisions; we also bias the sum by -(n*10) so that we
|
||||
don't have to add (n*10) to d for the final comparison. */
|
||||
s = p[n/100] + p[(n/10)%10] + p[n%10] - (n*10);
|
||||
for (d = 0; d < 10; d++) {
|
||||
if (s + p[d] == d) {
|
||||
if (!l) printf("%d = %d^4 + %d^4 + %d^4 + %d^4\n",
|
||||
n * 10 + d, n/100, (n/10)%10, n%10, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And finally, here's the book's Pascal solution:
|
||||
|
||||
```pascal
|
||||
program sum4ths(input, output);
|
||||
var
|
||||
pwr: { the fourth power of each number, 0 to 9 }
|
||||
array[0..9] of integer;
|
||||
h, { the digit in the thousands position }
|
||||
h000, { h * 1000, that is, the value of the number
|
||||
in the thousands position }
|
||||
i, { the digit in the hundreds position }
|
||||
i00, { i * 100, that is, the value of the number in
|
||||
the hundreds position }
|
||||
j, { the digits in the tens position }
|
||||
j0, { j * 10, that is, the value of the number in
|
||||
the tens position }
|
||||
part4ths, { h^4 + i^4 + j^4 }
|
||||
partsum, { h000 + i00 + j0 }
|
||||
k, { the digit in the ones position }
|
||||
sum: integer;
|
||||
|
||||
begin
|
||||
for i := 0 to 9 do pwr[i] := i * i * i * i;
|
||||
h000 := 1000;
|
||||
for h := 1 to 9 do begin { thousands digit }
|
||||
i00 := 0;
|
||||
for i := 0 to 9 do begin { hundreds digit }
|
||||
j0 := 0;
|
||||
for j := 0 to 9 do begin { tens digits }
|
||||
partsum := h000 + i00 + j0;
|
||||
part4ths := pwr[h] + pwr[i] + pwr[j];
|
||||
for k := 0 to 9 do begin { ones digit }
|
||||
sum := partsum + k; { this produces the four-digit number }
|
||||
if part4ths + pwr[k] = sum then
|
||||
writeln(sum, ' = ', h, '^4 + ', i, '^4 + ', j, '^4 + ', k, '^4')
|
||||
end;
|
||||
j0 := j0 + 10;
|
||||
end;
|
||||
i00 := i00 + 100;
|
||||
end;
|
||||
h000 := h000 + 1000;
|
||||
end
|
||||
end.
|
||||
```
|
||||
|
||||
You can play with all these solutions in the 4.77Mhz IBM PC XT machine below. Use `CD \PUZZLED\PUZZLE5` to switch
|
||||
to the directory for Puzzle #5.
|
||||
|
||||
To run the BASIC version, use `GWBASIC`, since the machine boots MS-DOS 3.20:
|
||||
|
||||
C:\PUZZLED\PUZZLE5>GWBASIC PUZZLE5.BAS
|
||||
|
||||
To compile and run the C version, you can use the pre-installed copy of Microsoft C 4.00:
|
||||
|
||||
C:\PUZZLED\PUZZLE5>cl puzzle5.c
|
||||
Microsoft (R) C Compiler Version 4.00
|
||||
Copyright (C) Microsoft Corp 1984, 1985, 1986. All rights reserved.
|
||||
|
||||
puzzle5.c
|
||||
Microsoft (R) Overlay Linker Version 3.51
|
||||
Copyright (C) Microsoft Corp 1983, 1984, 1985, 1986. All rights reserved.
|
||||
|
||||
Object Modules [.OBJ]: PUZZLE5.OBJ
|
||||
Run File [PUZZLE5.EXE]: PUZZLE5.EXE/NOI
|
||||
List File [NUL.MAP]: NUL
|
||||
Libraries [.LIB]: ;
|
||||
|
||||
C:\PUZZLED\PUZZLE5>puzzle5
|
||||
1634 = 1^4 + 6^4 + 3^4 + 4^4
|
||||
8208 = 8^4 + 2^4 + 0^4 + 8^4
|
||||
9474 = 9^4 + 4^4 + 7^4 + 4^4
|
||||
|
||||
To compile and run the Pascal version, load diskette "IBM Pascal 1.00 (Combined)" in drive A, and then type the
|
||||
commands shown below:
|
||||
|
||||
C:\PUZZLED\PUZZLE5>a:pas1
|
||||
IBM Personal Computer Pascal Compiler
|
||||
Version 1.00 (C)Copyright IBM Corp 1981
|
||||
Source filename [.PAS]: puzzle5p
|
||||
Object filename [PUZZLE5P.OBJ]:
|
||||
Source listing [NUL.LST]:
|
||||
Object listing [NUL.COD]: puzzle5p
|
||||
Pass One No Errors Detected.
|
||||
|
||||
C:\PUZZLED\PUZZLE5>a:pas2
|
||||
|
||||
Code Area Size = #0296 (662)
|
||||
Cons Area Size = #004C (76)
|
||||
Data Area Size = #002A (42)
|
||||
|
||||
Pass Two No Errors Detected.
|
||||
|
||||
C:\PUZZLED\PUZZLE5>a:link
|
||||
IBM Personal Computer Linker
|
||||
Version 1.00 (C) Copyright IBM Corp 1981
|
||||
Object Modules: puzzle5p
|
||||
Run File: puzzle5p
|
||||
List File [PUZZLE5P.MAP] :
|
||||
Libraries [ ] :
|
||||
Publics [No]:
|
||||
Line Numbers [No]:
|
||||
Stack size [Object file stack]:
|
||||
Load Low [Yes]:
|
||||
DSAllocation [No]:
|
||||
|
||||
C:\PUZZLED\PUZZLE5>puzzle5p
|
||||
1634 = 1^4 + 6^4 + 3^4 + 4^4
|
||||
8208 = 8^4 + 2^4 + 0^4 + 8^4
|
||||
9474 = 9^4 + 4^4 + 7^4 + 4^4
|
||||
|
||||
It appears that PCjs is somewhat faster than a real machine. For example, the author's BASIC solution ran
|
||||
for almost two minutes on a PC XT clone, but it finishes in about one and a half minutes on the 4.77Mhz PC XT
|
||||
configuration below.
|
||||
|
||||
Clearly, I still have some work to do if I want PCjs to faithfully simulate how *slow* these systems used to be.
|
||||
|
||||
{% include machine.html id="ibm5160-msdos320" %}
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*Sep 21, 2017*
|
||||
Loading…
Reference in a new issue