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);}}
(Algunas líneas nuevas no contadas en el recuento de bytes se han agregado anteriormente para eliminar las barras de desplazamiento. Sí, se cuenta la línea nueva final).
Espera argumentos en la línea de comando y supone que la salida estándar acepta ASCII. El tiempo de ejecución es O (número de bytes de salida) = O (n * n).
No, no puedo usar printf
. Eso lleva demasiado tiempo y empuja el programa por encima del límite de minutos en mi escritorio. Tal como están las cosas, algunos casos de prueba tardan unos 30 segundos.
El algoritmo trata la salida como cadenas, no números, ya que rápidamente se vuelven enormes, y hay patrones fuertes en la salida.
Algo sin golf:
#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;
}
}
}
}
Prueba
que el programa resuelve el problema:
(En la prueba, considere que todos los operadores y funciones son las funciones matemáticas reales, no las operaciones de la computadora que las aproximan. ^
Denota exponenciación, no bit a xor).
Para mayor claridad, usaré una función ToDec
para describir el proceso ordinario de escribir un número como una secuencia de dígitos decimales. Su rango es el conjunto de tuplas ordenadas {0...9}
. Por ejemplo,
ToDec(2014) = (2, 0, 1, 4).
Para un entero positivo n
, defínalo L(n)
como el número de dígitos en la representación decimal de n
; o,
L(n) = 1+floor(log10(n)).
Para un entero positivo k
y un entero no negativo n
con L(n)<k
, defina Rep_k(n)
como el número real obtenido agregando ceros delante de los dígitos decimales de n
, si es necesario para obtener k
dígitos totales, y luego repitiendo infinitamente esos k
dígitos después del punto decimal. P.ej
Rep_4(2014) = .201420142014...
Rep_5(2014) = .020140201402...
Multiplicar Rep_k(n) * 10^k
da los dígitos n
anteriores al punto decimal, y los dígitos (rellenos con cero) de n
infinitamente repetidos después del punto decimal. Entonces
Rep_k(n) * 10^k = n + Rep_k(n)
Rep_k(n) = n / (10^k - 1)
Dado un número entero positivo r
, supongamos que x
es una solución al problema, y
ToDec(x) = ( x_1, x_2, ..., x_k )
donde x_1 != 0
y k = L(x)
.
Para ser una solución, x
es un múltiplo de r
y
ToDec(x/r) : ( x_2, x_3, ..., x_k, x_1 ).
Aplicar la Rep_k
función da una buena ecuación:
10*Rep_k(x) = x_1 + Rep_k(x/r)
Usando su forma cerrada desde arriba,
10x / (10^k - 1) = x_1 + x / r / (10^k - 1)
x = x_1 * r * (10^k-1) / (10r - 1)
x_1
debe estar en el set {1 ... 9}
. r
se especificó para estar en el conjunto {2 ... 9}
. Ahora la única pregunta es, ¿para qué valores de k
la fórmula anterior x
da un número entero positivo? Consideraremos cada valor posible de forma r
individual.
Cuando r
= 2, 3, 6, 8 o 9, 10r-1
es 19, 29, 59, 79 u 89, respectivamente. En todos los casos, el denominador p = 10r-1
es primo. En el numerador, solo 10^k-1
puede ser un múltiplo de p
, lo que sucede cuando
10^k = 1 (mod p)
El conjunto de soluciones se cierra con sumas y restas que no resultan en un número negativo. Entonces, el conjunto comprende todos los múltiplos de algún factor común, que también es la solución menos positiva para k
.
Cuando r = 4
y 10r-1 = 39
; o cuando r = 7
y 10r-1 = 69
, el denominador es 3 veces un primo diferente p=(10r-1)/3
. 10^k-1
siempre es un múltiplo de 3, y nuevamente ningún otro factor en el numerador puede ser múltiplo de p
, por lo que nuevamente el problema se reduce a
10^k = 1 (mod p)
y nuevamente las soluciones son todos los múltiplos de la solución menos positiva para k
.
[Sin terminar...]
gprof
, un caso de entrada para mi programa pasa menos de medio segundo en mi código, pero toma alrededor de 80 segundos en total, lo que supongo que debe estar bloqueando la salida.