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);}}
(スクロールバーを削除するために、バイトカウントにカウントされないいくつかの改行が上に追加されました。はい、最後の改行がカウントされます。)
コマンドラインで引数を期待し、標準出力がASCIIを受け入れると想定します。ランタイムはO(出力バイト数)= O(n * n)です。
いいえ、使用できませんprintf
。これには時間がかかりすぎて、プログラムがデスクトップの1分制限を超えてしまいます。現状では、一部のテストケースには約30秒かかります。
アルゴリズムは、出力がすぐに膨大になり、出力に強いパターンがあるため、出力を数値ではなく文字列として扱います。
やや自由:
#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;
}
}
}
}
証明
プログラムが問題を解決すること:
(証明では、すべての演算子と関数を、それらを近似するコンピューター操作ではなく、実際の数学関数と ^
みなします。ビットごとのxorではなく、べき乗を示します。)
わかりやすくするために、ToDec
数値を10進数のシーケンスとして記述する通常のプロセスを説明する関数を使用します。その範囲は、上の順序付けられたタプルのセットです{0...9}
。例えば、
ToDec(2014) = (2, 0, 1, 4).
正の整数の場合n
、;のL(n)
10進表現の桁数になるように定義しますn
。または、
L(n) = 1+floor(log10(n)).
正の整数をk
非負の整数n
とL(n)<k
定義Rep_k(n)
の桁の前にゼロを付加した実数であることをn
取得するために必要な場合、k
数字は合計し、次に無限それら繰り返すk
小数点後桁。例えば
Rep_4(2014) = .201420142014...
Rep_5(2014) = .020140201402...
乗算Rep_k(n) * 10^k
により、n
小数点の前の数字と、小数点のn
後に無限に繰り返される(ゼロが埋め込まれた)数字が得られます。そう
Rep_k(n) * 10^k = n + Rep_k(n)
Rep_k(n) = n / (10^k - 1)
正の整数が与えられ、問題の解r
であると仮定しx
、
ToDec(x) = ( x_1, x_2, ..., x_k )
どこx_1 != 0
とk = L(x)
。
解となるにx
は、はの倍数でr
あり、
ToDec(x/r) : ( x_2, x_3, ..., x_k, x_1 ).
Rep_k
関数を適用すると、素晴らしい方程式が得られます。
10*Rep_k(x) = x_1 + Rep_k(x/r)
上から閉じたフォームを使用して、
10x / (10^k - 1) = x_1 + x / r / (10^k - 1)
x = x_1 * r * (10^k-1) / (10r - 1)
x_1
セットに含まれている必要があります{1 ... 9}
。r
セットにあるように指定されました{2 ... 9}
。さて、唯一の問題は、k
上記の式のどの値がx
正の整数を与えるかということです。それぞれの可能な値をr
個別に検討します。
場合r
= 2、3、6、8、または9、10r-1
それぞれ、19、29、59、79、または89です。すべての場合において、分母p = 10r-1
は素数です。分子中に、唯一10^k-1
の倍数であることができるp
起こります、
10^k = 1 (mod p)
負の数にならない加算および減算では、ソリューションのセットは閉じられます。したがって、セットは、いくつかの共通因子のすべての倍数で構成されます。これは、の最小の正の解でもありk
ます。
いつr = 4
および10r-1 = 39
; またはときr = 7
と10r-1 = 69
、分母は3倍異なる素数ですp=(10r-1)/3
。 10^k-1
常に3の倍数であり、分子内の他の因子がの倍数になることはないp
ため、問題は次のように減少します。
10^k = 1 (mod p)
また、解はすべてに対する最小の正の解の倍数ですk
。
[終了していません...]
gprof
、私のプログラムの1つの入力ケースはコードで0.5秒未満しか費やしませんが、合計で約80秒かかります。