Inconsummate numbers in base 10: Difference between revisions

Added PL/M
(→‎{{header|ALGOL 68}}: Simplify - no need for the divisor sum table, find the consummate numbers while finding the sums, handle the stretch goal)
(Added PL/M)
Line 282:
 
Real time: 3.342 s CPU share: 99.16 %
</pre>
 
=={{header|PL/M}}==
Based on the Algol 68 sample but only finds the first 50 Inconsummate numbers as it would require more than 16 bit arithmetic t0 go much further.<br>
Calculating the digit sums as here appears to be faster than using MOD and division.
{{works with|8080 PL/M Compiler}} ... under CP/M (or an emulator)
<syntaxhighlight lang="plm">
100H: /* FIND SOME INCOMSUMMATE NUMBERS: INTEGERS THAT CANNOT BE EXPRESSED */
/* AS AN INTEGER DIVIDED BY THE SUM OF ITS DIGITS */
 
DECLARE FALSE LITERALLY '0', TRUE LITERALLY '0FFH';
 
/* CP/M SYSTEM CALL AND I/O ROUTINES */
BDOS: PROCEDURE( FN, ARG ); DECLARE FN BYTE, ARG ADDRESS; GOTO 5; END;
PR$CHAR: PROCEDURE( C ); DECLARE C BYTE; CALL BDOS( 2, C ); END;
PR$STRING: PROCEDURE( S ); DECLARE S ADDRESS; CALL BDOS( 9, S ); END;
PR$NL: PROCEDURE; CALL PR$CHAR( 0DH ); CALL PR$CHAR( 0AH ); END;
PR$NUMBER: PROCEDURE( N ); /* PRINTS A NUMBER IN THE MINIMUN FIELD WIDTH */
DECLARE N ADDRESS;
DECLARE V ADDRESS, N$STR ( 6 )BYTE, W BYTE;
V = N;
W = LAST( N$STR );
N$STR( W ) = '$';
N$STR( W := W - 1 ) = '0' + ( V MOD 10 );
DO WHILE( ( V := V / 10 ) > 0 );
N$STR( W := W - 1 ) = '0' + ( V MOD 10 );
END;
CALL PR$STRING( .N$STR( W ) );
END PR$NUMBER;
/* END SYSTEM CALL AND I/O ROUTINES */
 
DECLARE MAX$C LITERALLY '999', /* MAXIMUM NUMBER WE WILL TEST */
MAX$C$PLUS$1 LITERALLY '1000'; /* MAX$C + 1 FOR ARRAY BOUNDS */
 
DECLARE ( I, J, MAX$SUM, V, MAX$NUMBER, COUNT ) ADDRESS;
 
/* TABLE OF NUMBERS THAT CAN BE FORMED BY N / DIGIT SUM N */
DECLARE CONSUMMATE ( MAX$C$PLUS$1 ) ADDRESS;
DO I = 0 TO LAST( CONSUMMATE ); CONSUMMATE( I ) = FALSE; END;
 
/* CALCULATE THE MAXIMUM NUMBER WE MUST CONSIDER TO FIND CONSUMMATE */
/* NUMBERS UP TO LAST(CONSUMMATE)- WHICH IS 9 * THE NUMBER OF DIGITS IN */
/* LAST(CONSUMMATE) */
MAX$SUM = 9;
V = LAST( CONSUMMATE );
DO WHILE ( V := V / 10 ) > 0; MAX$SUM = MAX$SUM + 9; END;
MAX$NUMBER = LAST( CONSUMMATE ) * MAX$SUM;
 
/* CONSTRUCT THE DIGIT SUMS OF THE NUMBERS UP TO MAX$NUMBER */
/* AND FIND THE CONSUMATE NUMBERS, WE START THE LOOP FROM 10 TO AVOID */
/* HAVING TO DEAL WITH 0-9 */
DO;
DECLARE ( D, N, TN, HN, TH, TT, SUMD ) ADDRESS;
CONSUMMATE( 1 ) = TRUE;
TT, TH, HN = 0; TN = 1;
DO N = 10 TO MAX$NUMBER BY 10;
SUMD = TT + TH + HN + TN;
DO D = N TO N + 9;
IF D MOD SUMD = 0 THEN DO;
/* D IS COMSUMMATE */
DECLARE D$RATIO ADDRESS;
IF ( D$RATIO := D / SUMD ) <= LAST( CONSUMMATE ) THEN DO;
CONSUMMATE( D$RATIO ) = TRUE;
END;
END;
SUMD = SUMD + 1;
END;
IF ( TN := TN + 1 ) > 9 THEN DO;
TN = 0;
IF ( HN := HN + 1 ) > 9 THEN DO;
HN = 0;
IF ( TH := TH + 1 ) > 9 THEN DO;
TH = 0;
TT = TT + 1;
END;
END;
END;
END;
END;
 
COUNT = 0;
CALL PR$STRING( .'THE FIRST 50 INCONSUMMATE NUMBERS:$' );CALL PR$NL;
I = 0;
DO WHILE ( I := I + 1 ) <= LAST( CONSUMMATE ) AND COUNT < 50;
IF NOT CONSUMMATE( I ) THEN DO;
IF I < 10 THEN CALL PR$CHAR( ' ' );
IF I < 100 THEN CALL PR$CHAR( ' ' );
IF I < 1000 THEN CALL PR$CHAR( ' ' );
CALL PR$CHAR( ' ' );
CALL PR$NUMBER( I );
IF ( COUNT := COUNT + 1 ) MOD 10 = 0 THEN CALL PR$NL;
END;
END;
 
EOF
</syntaxhighlight>
{{out}}
<pre>
THE FIRST 50 INCONSUMMATE NUMBERS:
62 63 65 75 84 95 161 173 195 216
261 266 272 276 326 371 372 377 381 383
386 387 395 411 416 422 426 431 432 438
441 443 461 466 471 476 482 483 486 488
491 492 493 494 497 498 516 521 522 527
</pre>
 
3,026

edits