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);}}
(Certains sauts de ligne non comptés dans le nombre d'octets ont été ajoutés ci-dessus pour éliminer les barres de défilement. Oui, le saut de ligne final est compté.)
Attend des arguments sur la ligne de commande et suppose que la sortie standard accepte ASCII. Le temps d'exécution est O (nombre d'octets en sortie) = O (n * n).
Non, je ne peux pas utiliser printf
. Cela prend trop de temps et pousse le programme au-delà de la limite des minutes sur mon bureau. En l'état, certains cas de test prennent environ 30 secondes.
L'algorithme traite la sortie comme des chaînes, et non comme des nombres, car elles deviennent rapidement énormes et il existe de forts schémas dans la sortie.
Assez peu 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;
}
}
}
}
Preuve
que le programme résout le problème:
(Dans la preuve, prenez tous les opérateurs et fonctions pour être les vraies fonctions mathématiques, pas les opérations informatiques qui les rapprochent. ^
Dénote l'exponentiation, pas le xor au niveau du bit.)
Pour plus de clarté, je vais utiliser une fonction ToDec
pour décrire le processus ordinaire d'écriture d'un nombre sous la forme d'une séquence de chiffres décimaux. Sa gamme est l'ensemble de tuples ordonnés sur {0...9}
. Par exemple,
ToDec(2014) = (2, 0, 1, 4).
Pour un entier positif n
, définissez L(n)
comme étant le nombre de chiffres dans la représentation décimale de n
; ou,
L(n) = 1+floor(log10(n)).
Pour un entier positif k
et un entier non négatif n
avec L(n)<k
, définissez Rep_k(n)
comme étant le nombre réel obtenu en ajoutant des zéros devant les chiffres décimaux de n
, si nécessaire pour obtenir le k
total des chiffres, puis en répétant à l'infini ces k
chiffres après le point décimal. Par exemple
Rep_4(2014) = .201420142014...
Rep_5(2014) = .020140201402...
La multiplication Rep_k(n) * 10^k
donne les chiffres d' n
avant la virgule décimale et les chiffres (remplis de zéro) n
infiniment répétés après la virgule décimale. Donc
Rep_k(n) * 10^k = n + Rep_k(n)
Rep_k(n) = n / (10^k - 1)
Étant donné un entier positif r
, supposons x
une solution au problème, et
ToDec(x) = ( x_1, x_2, ..., x_k )
où x_1 != 0
et k = L(x)
.
Pour être une solution, x
est un multiple de r
, et
ToDec(x/r) : ( x_2, x_3, ..., x_k, x_1 ).
L'application de la Rep_k
fonction donne une belle équation:
10*Rep_k(x) = x_1 + Rep_k(x/r)
En utilisant sa forme fermée d'en haut,
10x / (10^k - 1) = x_1 + x / r / (10^k - 1)
x = x_1 * r * (10^k-1) / (10r - 1)
x_1
doit être dans l'ensemble {1 ... 9}
. r
a été spécifié pour être dans l'ensemble {2 ... 9}
. Maintenant, la seule question est: pour quelles valeurs k
la formule ci-dessus x
donne-t-elle un entier positif? Nous considérerons chaque valeur possible r
individuellement.
Quand r
= 2, 3, 6, 8 ou 9, 10r-1
est respectivement 19, 29, 59, 79 ou 89. Dans tous les cas, le dénominateur p = 10r-1
est premier. Dans le numérateur, seul 10^k-1
peut être un multiple de p
, ce qui se produit lorsque
10^k = 1 (mod p)
L'ensemble des solutions est fermé sous addition et sous soustraction qui n'aboutit pas à un nombre négatif. Ainsi, l'ensemble comprend tous les multiples d'un facteur commun, qui est également la solution la moins positive pour k
.
Quand r = 4
et 10r-1 = 39
; ou quand r = 7
et 10r-1 = 69
, le dénominateur est 3 fois un nombre premier différent p=(10r-1)/3
. 10^k-1
est toujours un multiple de 3, et là encore aucun autre facteur du numérateur ne peut être un multiple de p
, donc encore une fois le problème se réduit à
10^k = 1 (mod p)
et encore une fois les solutions sont tous les multiples de la solution la moins positive pour k
.
[Pas terminé...]
gprof
, un cas d'entrée pour mon programme passe moins d'une demi-seconde dans mon code, mais prend environ 80 secondes au total, ce qui, je suppose, doit principalement bloquer la sortie.