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
。这会花费太多时间,并使程序超出桌面上的分钟限制。实际上,某些测试用例大约需要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;
}
}
}
}
证明
该程序解决了问题:
(在证明中,将所有运算符和函数视为真实的数学函数,而不是近似于它们的计算机运算。 ^
表示幂运算,而不是按位异或。)
为了清楚起见,我将使用一个函数ToDec
来描述将数字写为十进制数字序列的一般过程。其范围是上有序元组的集合{0...9}
。例如,
ToDec(2014) = (2, 0, 1, 4).
对于正整数n
,定义L(n)
为十进制表示中的数字位数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
被指定为set中的{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
,我的程序的一种输入情况在我的代码中花费的时间不到半秒,但是总共花费了大约80秒,我认为这在输出中主要是阻塞的。