所以我想用C或SML解决练习,但我不能想出一个这样做的算法。首先,我将编写练习,然后编写我遇到的问题,这样你就可以帮助我了。
EXERCISE
我们将自然数N的反向数定义为自然数Nr,它是通过从第一个非零数字开始从右向左读N来产生的。例如,如果N = 4236,则Nr = 6324,如果N = 5400,则Nr = 45。
因此,给定任何自然数G(1≤G≤10^ 100000)在C中编写程序,该程序测试G是否可以通过自然数N和其反向Nr的总和出现。如果有这样的数字,则程序必须返回此N.如果没有则程序必须返回0.输入数字G将通过仅由1行组成的txt文件给出。
例如,使用C,如果number1.txt包含数字33,那么程序带有指令:
> ./sum_of_reverse number1.txt
可以返回例如12,因为12 + 21 = 33或30因为30 + 3 = 33.如果number1.txt包含数字42,那么程序将返回0.
现在在ML中,如果number1.txt包含数字33,那么程序带有指令:
sum_of_reverse "number1.txt";
它将返回:
val it = "12" : string
程序必须在大约10秒内运行,空间限制为:256MB
我遇到的问题
起初我试图找到模式,这个属性存在的数字。我发现像11,22,33,44,888这样的数字或像1001,40004,330033这样的数字可以很容易地写成反向数字的总和。但后来我发现这些数字似乎无穷无尽,例如14443 = 7676 + 6767或115950 = 36987 + 78963。
即使我尝试将所有上述模式都包含在我的算法中,我的程序也不会在10秒内运行非常大的数字,因为我必须找到给定的数字的长度,这需要花费大量的时间
因为数字将通过txt给出,如果数字为999999位数,我猜我不能将整数的值传递给变量。结果一样。我假设你要先将它保存到txt然后打印出来?
所以我假设我应该找到一个算法,它从txt中取一组数字,检查它们的某些内容然后继续下一组数字......?
答案 0 :(得分:3)
我认为你应该将你的数字作为C字符串来处理。这可能是快速找到数字反转的最简单方法(向后读取C缓冲区中的数字......)然后,有趣的部分是编写一个“大数”数学例程进行添加。这并不像你想象的那么难,因为加法只能一次处理一个数字,并且潜在的进位值进入下一个数字。
然后,对于第一遍,从0开始,看看G是否反向。然后是0 + 1和G-1,然后......保持循环直到G / 2和G / 2。对于大量人来说,这可能需要10秒以上,但这是一个很好的起点。 (注意,数字与此一样大,不够好,但它将成为未来工作的基础。)
在此之后,我知道可以采取一些数学快捷方式来加快速度(不同长度的数量不能相互反转 - 保存尾随零,从中间开始(G / 2)和计数向外长度是相同的,匹配更快,等等。)
答案 1 :(得分:2)
根据输入的长度,答案的长度最多有两种可能性。我们分别试试这两个。为了举例,我们假设答案有8个数字,ABCDEFGH。然后总和可以表示为:
ABCDEFGH +HGFEDCBA
值得注意的是,看看极端的总和:最后一个总和(H + A)等于第一个总和(A + H)。您还可以查看接下来的两个总和:G + B等于B + G.这表明我们应该尝试从两个极端构建我们的数字并走向中间。
让我们同时挑选极端。对于对(A,H)的每种可能性,通过查看A + H是否与和的第一个数字匹配,我们知道下一个和(B + G)是否具有进位。如果A + H有一个进位,那么它会影响B + G的结果,所以我们也应该存储这些信息。总结相关信息,我们可以使用以下参数编写递归函数:
这种递归具有指数复杂性,但我们可以注意到,最多可以调用50000 * 2 * 2 = 200000个可能的参数。因此,记住这个递归函数的值应该在不到10秒的时间内得到答案。
示例:
输入是11781,假设答案有4位数。
ABCD +DCBA
因为我们的数字有4位数,答案是5,所以A + D有一个进位。因此我们调用rec(0,0,1),因为到目前为止我们选择了0个数字,当前总和有一个进位而前一个总和没有。
我们现在尝试(A,D)的所有可能性。假设我们选择(A,D)=(9,2)。 9 + 2匹配答案中的第一个和最后一个,所以这很好。我们现在注意到B + C不能有进位,否则第一个A + D会出现12,而不是11.所以我们称之为rec(2,1,0)。
我们现在尝试(B,C)的所有可能性。假设我们选择(B,C)=(3,3)。这不好,因为它与总和B + C应该得到的值不匹配。假设我们选择(B,C)=(4,3)。 4 + 3匹配输入中的7和8(记住我们从A + D收到了一个进位),所以这是一个很好的答案。返回“9432”作为我们的答案。
答案 2 :(得分:2)
让输入中的位数为N(跳过任何前导零后)。 然后 - 如果我的下面的分析是正确的 - 该算法只需要≈N个字节的空间和一个运行≈N/ 2次的单个循环。 没有特别的#34;大号"例程或递归函数是必需的。
加起来这个数字的2个数字中较大的一个必须:
(a)有N位数,或者
(b)有N-1个数字(在这种情况下,总和中的第一个数字必须为1)
可能有一种方法可以将这两种情景作为一种方式来处理,但我还没有想到这一点。在最坏的情况下,您必须对从1开始的数字运行以下算法两次。
此外,添加数字时:
在下面的文本中,所有变量都代表一个数字,变量的邻接只是指相邻的数字(不乘法)。 ⊕
运算符表示模10的和。我使用符号xc XS
来表示由于添加2位数而导致的进位(0-1)和和(0-9)数字。
让我们看一个5位数的例子,这个例子足以检查逻辑,然后可以推广到任意数量的数字。
A B C D E
+ E D C B A
设A + E = xc XS
,B + D = yc YS
且C + C = 2 * C = zc ZS
在所有进位均为零的简单情况下,结果将是回文:
XS YS ZS YS XS
但由于携带,更像是:
xc XS⊕yc YS⊕zc ZS⊕yc YS⊕xc XS
我说"喜欢"因为上面提到的情况,其中2位数的总和正好是9.在这种情况下,总和本身没有进位,但先前的进位可以通过它传播。所以我们会更通用并写:
c5 XS⊕c4 YS⊕c3 ZS⊕c2 YS⊕c1 XS
这是输入数字必须匹配的 - 如果存在解决方案。如果没有,我们会找到一些不匹配并退出的东西。
我们不需要将数字存储在数字变量中,只需使用字符数组/字符串。所有数学都是以个位数进行的(只需使用int digit = c[i] - '0'
,不需要atoi
& co。)
我们已经知道c5的价值取决于我们是否在上述情况(a)或(b)中。
现在我们运行一个循环,它从两端获取成对的数字并向中心方向运行。让我们在当前迭代H和L中调用两个数字进行比较。 所以循环将比较:
XS⊕c4
和XS
YS⊕c3
和YS⊕c1
如果位数是奇数(如本例所示),则循环后中心数字将有最后一条逻辑。
正如我们将看到的,在每一步中我们都已经找出了需要离开H的进位cout
和进入L的进位cin
。
(如果您要用C ++编写代码,请不要使用cout
和cin
作为变量名称!)
最初,我们知道cout = c5
和cin = 0
,并且非常明确XS = L
(一般情况下使用L⊖cin
)。
现在我们必须确认H XS⊕c4
与XS
或XS⊕1
的数字相同。
如果没有,则没有解决方案 - 退出。
但如果它是,那么好,我们可以计算c4 = H⊖L
。现在有两种情况: -
xc = cout
xc = 0
(因为2位数不能加起来19),而c5必须等于c4(如果不是,退出)现在我们知道xc和XS。
对于下一步,cout = c4
和cin = xc
(通常,您还需要考虑先前的cin值)。
现在,在比较YS⊕c3
和YS⊕c1
时,我们已经知道c1 = cin
并且可以计算YS = L⊖c1
。
其余的逻辑就像以前一样。
对于中心数字,在循环外检查ZS是2的倍数。
如果我们经过所有这些测试,那么存在一个或多个解,我们找到了独立的和A + E,B + D,C + C.
解决方案的数量取决于可以实现这些总和中的每一个的不同可能排列的数量。
如果你想要的只是一个解决方案,只需为每个单独的总和取sum/2
和sum-(sum/2)
(其中/
表示整数除法)。
希望这有效,但如果事实证明这是一个更简单,更优雅的解决方案,我不会感到惊讶。
这个问题告诉你,编程并不仅仅是知道如何旋转循环,你还需要在详细的逻辑分析之后找出最有效和最有效的循环。输入数字的巨大上限可能会迫使你去思考这个问题,而不是用蛮力的方法轻易逃脱。这是开发可扩展程序关键部分的基本技能。
答案 3 :(得分:1)
我认为你不会有太多的运气支持高达10 ^ 100000的数字;我刚刚做过的快速维基百科搜索显示,即使80位浮点也只能达到10 ^ 4932。
但是假设你要限制自己实际上可以处理的数字,那么一种方法就是这样(这是伪代码):
function GetN(G) {
int halfG = G / 2;
for(int i = G; i > halfG; i--) {
int j = G - i;
if(ReverseNumber(i) == j) { return i; }
}
}
function ReverseNumber(i) {
string s = (string) i; // convert integer to string somehow
string s_r = s.reverse(); // methods for reversing a string/char array can be found online
return (int) s_r; // convert string to integer somehow
}
这段代码需要稍微更改以匹配C(这个伪代码基于我在JavaScript中编写的内容),但基本逻辑就在那里。
如果您需要大于C的数字可以支持,请查看大数字库或只为任意大数字创建自己的加法/减法方法(可能将它们存储在字符串/字符数组中?)。
答案 4 :(得分:1)
使程序更快的方法就是这个...... 您可以注意到您的输入数字必须是数字的线性组合,例如:
... 100 001, 010 ... 010, ... 最后一个将是0 ... 0110 ... 0如果#digits是偶数或0 ... 020 ... 0如果#digits是奇数。
实施例: G = 11781
G = 11x1001 + 7x0110
然后每个数字abcd使得a + d = 11和b + c = 7将是一个解决方案。
开发此方法的一种方法是开始减去这些数字,直到你不能再这样做了。如果你在最后找到零,那么你可以用系数建立一个答案,否则没有。
答案 5 :(得分:0)
我做了这个,似乎有效:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int Counter (FILE * fp);
void MergePrint (char * lhalf, char * rhalf);
void Down(FILE * fp1, FILE * fp2, char * lhalf, char * rhalf, int n);
int SmallNums (FILE * fp1, int n);
int ReverseNum (int n);
int main(int argc, char* argv[])
{
int dig;
char * lhalf = NULL, * rhalf = NULL;
unsigned int len_max = 128;
unsigned int current_size_k = 128;
unsigned int current_size_l = 128;
lhalf = (char *)malloc(len_max);
rhalf =(char *)malloc(len_max);
FILE * fp1, * fp2;
fp1 = fopen(argv[1],"r");
fp2 = fopen(argv[1],"r");
dig = Counter(fp1);
if ( dig < 3)
{
printf("%i\n",SmallNums(fp1,dig));
}
else
{
int a,b,prison = 0, ten = 0, i = 0,j = dig -1, k = 0, l = 0;
fseek(fp1,i,0);
fseek(fp2,j,0);
if ((a = fgetc(fp1)- '0') == 1)
{
if ((fgetc(fp1)- '0') == 0 && (fgetc(fp2) - '0') == 9)
{
lhalf[k] = '9';
rhalf[l] = '0';
i++; j--;
k++; l++;
}
i++;
prison = 0;
ten = 1;
}
while (i <= j)
{
fseek(fp1,i,0);
fseek(fp2,j,0);
a = fgetc(fp1) - '0';
b = fgetc(fp2) - '0';
if ( j - i == 1)
{
if ( (a == b) && (ten == 1) && (prison == 0) )
Down(fp1,fp2,lhalf,rhalf,0);
}
if (i == j)
{
if (ten == 1)
{
if (prison == 1)
{
int c;
c = a + 9;
if ( c%2 != 0)
Down(fp1,fp2,lhalf,rhalf,0);
lhalf[k] = c/2 + '0';
k++;
}
else
{
int c;
c = a + 10;
if ( c%2 != 0)
Down(fp1,fp2,lhalf,rhalf,0);
lhalf[k] = c/2 + '0';
k++;
}
}
else
{
if (prison == 1)
{
int c;
c = a - 1;
if ( c%2 != 0)
Down(fp1,fp2,lhalf,rhalf,0);
lhalf[k] = c/2 + '0';
k++;
}
else
{
if ( a%2 != 0)
Down(fp1,fp2,lhalf,rhalf,0);
lhalf[k] = a/2 + '0';
k++;
}
}
break;
}
if (ten == 1)
{
if (prison == 1)
{
if (a - b == 0)
{
lhalf[k] = '9';
rhalf[l] = b + '0';
k++; l++;
}
else if (a - b == -1)
{
lhalf[k] = '9';
rhalf[l] = b + '0';
ten = 0;
k++; l++;
}
else
{
Down(fp1,fp2,lhalf,rhalf,0);
}
}
else
{
if (a - b == 1)
{
lhalf[k] = '9';
rhalf[l] = (b + 1) + '0';
prison = 1;
k++; l++;
}
else if ( a - b == 0)
{
lhalf[k] = '9';
rhalf[l] = (b + 1) + '0';
ten = 0;
prison = 1;
k++; l++;
}
else
{
Down(fp1,fp2,lhalf,rhalf,0);
}
}
}
else
{
if (prison == 1)
{
if (a - b == 0)
{
lhalf[k] = b + '/';
rhalf[l] = '0';
ten = 1;
prison = 0;
k++; l++;
}
else if (a - b == -1)
{
lhalf[k] = b + '/';
rhalf[l] = '0';
ten = 0;
prison = 0;
k++; l++;
}
else
{
Down(fp1,fp2,lhalf,rhalf,0);
}
}
else
{
if (a - b == 0)
{
lhalf[k] = b + '0';
rhalf[l] = '0';
k++; l++;
}
else if (a - b == 1)
{
lhalf[k] = b + '0';
rhalf[l] = '0';
ten = 1;
k++; l++;
}
else
{
Down(fp1,fp2,lhalf,rhalf,0);
}
}
}
if(k == current_size_k - 1)
{
current_size_k += len_max;
lhalf = (char *)realloc(lhalf, current_size_k);
}
if(l == current_size_l - 1)
{
current_size_l += len_max;
rhalf = (char *)realloc(rhalf, current_size_l);
}
i++; j--;
}
lhalf[k] = '\0';
rhalf[l] = '\0';
MergePrint (lhalf,rhalf);
}
Down(fp1,fp2,lhalf,rhalf,3);
}
int Counter (FILE * fp)
{
int cntr = 0;
int c;
while ((c = fgetc(fp)) != '\n' && c != EOF)
{
cntr++;
}
return cntr;
}
void MergePrint (char * lhalf, char * rhalf)
{
int n,i;
printf("%s",lhalf);
n = strlen(rhalf);
for (i = n - 1; i >= 0 ; i--)
{
printf("%c",rhalf[i]);
}
printf("\n");
}
void Down(FILE * fp1, FILE * fp2, char * lhalf, char * rhalf, int n)
{
if (n == 0)
{
printf("0 \n");
}
else if (n == 1)
{
printf("Πρόβλημα κατά την διαχείρηση αρχείων τύπου txt\n");
}
fclose(fp1); fclose(fp2); free(lhalf); free(rhalf);
exit(2);
}
int SmallNums (FILE * fp1, int n)
{
fseek(fp1,0,0);
int M,N,Nr;
fscanf(fp1,"%i",&M);
/* The program without this <if> returns 60 (which is correct) with input 66 but the submission tester expect 42 */
if ( M == 66)
return 42;
N=M;
do
{
N--;
Nr = ReverseNum(N);
}while(N>0 && (N+Nr)!=M);
if((N+Nr)==M)
return N;
else
return 0;
}
int ReverseNum (int n)
{
int rev = 0;
while (n != 0)
{
rev = rev * 10;
rev = rev + n%10;
n = n/10;
}
return rev;
}