C 468
#include <stdlib.h>
#include <stdio.h>
#define a(s)fputs(s,stdout);
#define b(c)putchar(c);
int r;int z(char*s,int m){for(int i=0;i++<=m;)a(s)b(47)b(r+48)b(61)char*t=s;
while(*++t==48);a(t)while(m--)a(s)b(*s)b(10)}char x[10][60];
int main(int c,char**v){r=atoi(v[1]);int n=atoi(v[2]),q=10*r-1,d=0,p;
while(d++<9){p=r*d;char*y=x[d];do{p*=10;*y++=p/q+48;p%=q;}while(p!=r*d);}
d=1;p=q=0;while(n--){r==5&p<6?z(x[7],7*q+p++):(z(x[d],(r==5&d==7)?7*q+6:q),
++d>9?q+=d=1,p=0:0);}}
(Alcune nuove righe non conteggiate nel conteggio byte sono state aggiunte sopra per eliminare le barre di scorrimento. Sì, viene conteggiata la nuova riga finale.)
Si aspetta argomenti dalla riga di comando e presuppone che l'output standard accetti ASCII. Il tempo di esecuzione è O (numero di byte in uscita) = O (n * n).
No, non posso usare printf
. Ciò richiede troppo tempo e spinge il programma oltre il limite dei minuti sul mio desktop. Allo stato attuale, alcuni casi di test impiegano circa 30 secondi.
L'algoritmo tratta l'output come stringhe, non numeri, poiché diventano rapidamente enormi e ci sono modelli forti nell'output.
Abbastanza ungolfed:
#include <stdlib.h>
#include <stdio.h>
/* r is as in the problem description */
int r;
void show_line(const char* num, int repeats) {
for (int i=0; i <= repeats; ++i)
fputs(num, stdout);
printf("/%c=", '0'+r);
/* Assume the repeated num is a solution. Just move the first
digit and skip any resulting leading zeros. */
const char* num_tail = num;
++num_tail;
while (*num_tail=='0')
++num_tail;
fputs(num_tail, stdout);
while (repeats--)
fputs(num, stdout);
printf("%c\n", *num);
}
/* sol[0] is unused. Otherwise, sol[d] is the repeating digits in the
decimal representation of (r*d)/(10*r-1). */
char sol[10][60];
int main(int argc, char** argv) {
r = atoi(argv[1]);
int n = atoi(argv[2]);
int q = 10*r-1;
int d = 0;
/* Populate the strings in sol[]. */
while (d++<9) {
int p = r*d;
char* sol_str = sol[d];
/* Do the long division p/q in decimal, stopping when the remainder
is the original dividend. The integer part is always zero. */
do {
p *= 10;
*sol_str++ = p/q + '0';
p %= q;
} while (p != r*d);
}
/* Output the answers. */
d = 1;
int repeats = 0;
int r5x7_repeats = 0;
while (n--) {
if (r==5 && r5x7_repeats<6) {
show_line(x[7], 7*repeats + r5x7_repeats);
} else {
if (r==5 && d==7)
show_line(x[d], 7*repeats + 6);
else
show_line(x[d], repeats);
if (++d > 9) {
d = 1;
++repeats;
r5x7_repeats = 0;
}
}
}
}
Prova
che il programma risolva il problema:
(Nella dimostrazione, considera tutti gli operatori e le funzioni come le vere funzioni matematiche, non le operazioni del computer che le avvicinano. ^
Denota esponenziazione, non xor bit a bit.)
Per chiarezza, userò una funzione ToDec
per descrivere il normale processo di scrittura di un numero come una sequenza di cifre decimali. La sua gamma è l'insieme delle tuple ordinate {0...9}
. Per esempio,
ToDec(2014) = (2, 0, 1, 4).
Per un numero intero positivo n
, definire L(n)
il numero di cifre nella rappresentazione decimale di n
; o,
L(n) = 1+floor(log10(n)).
Per un numero intero positivo k
e un numero intero non negativo n
con L(n)<k
, definire Rep_k(n)
come il numero reale ottenuto aggiungendo zeri prima delle cifre decimali di n
, se necessario per ottenere il k
totale delle cifre, quindi ripetere ripetutamente quelle k
cifre dopo il punto decimale. Per esempio
Rep_4(2014) = .201420142014...
Rep_5(2014) = .020140201402...
La moltiplicazione Rep_k(n) * 10^k
indica le cifre n
prima del punto decimale e le cifre (con zero) di n
infinito ripetute dopo il punto decimale. Così
Rep_k(n) * 10^k = n + Rep_k(n)
Rep_k(n) = n / (10^k - 1)
Dato un numero intero positivo r
, supponiamo che x
sia una soluzione al problema e
ToDec(x) = ( x_1, x_2, ..., x_k )
dove x_1 != 0
e k = L(x)
.
Per essere una soluzione, x
è un multiplo di r
e
ToDec(x/r) : ( x_2, x_3, ..., x_k, x_1 ).
L'applicazione della Rep_k
funzione fornisce una bella equazione:
10*Rep_k(x) = x_1 + Rep_k(x/r)
Usando la sua forma chiusa dall'alto,
10x / (10^k - 1) = x_1 + x / r / (10^k - 1)
x = x_1 * r * (10^k-1) / (10r - 1)
x_1
deve essere nel set {1 ... 9}
. r
è stato specificato per essere nel set {2 ... 9}
. Ora l'unica domanda è: per quali valori della k
formula precedente si ottiene x
un numero intero positivo? Prenderemo in considerazione ogni possibile valore di r
individualmente.
Quando r
= 2, 3, 6, 8 o 9, 10r-1
è rispettivamente 19, 29, 59, 79 o 89. In tutti i casi, il denominatore p = 10r-1
è primo. Nel numeratore, solo 10^k-1
può essere un multiplo di p
, che succede quando
10^k = 1 (mod p)
L'insieme di soluzioni viene chiuso in aggiunta e in sottrazione che non comporta un numero negativo. Quindi l'insieme comprende tutti i multipli di qualche fattore comune, che è anche la soluzione meno positiva per k
.
Quando r = 4
e 10r-1 = 39
; o quando r = 7
e 10r-1 = 69
, il denominatore è 3 volte un numero primo diverso p=(10r-1)/3
. 10^k-1
è sempre un multiplo di 3, e di nuovo nessun altro fattore nel numeratore può essere un multiplo di p
, quindi il problema si riduce a
10^k = 1 (mod p)
e ancora una volta le soluzioni sono tutti i multipli della soluzione meno positiva per k
.
[Non finito...]
gprof
, un caso di input per il mio programma spende meno di mezzo secondo nel mio codice, ma impiega circa 80 secondi in totale, che presumo debba essere principalmente bloccato sull'output.