C: sum of inverse numbers

So, I want to solve an exercise in C or SML, but I just can't come up with an algorithm that does this. First, I will write an exercise, and then the problems that I have encountered so that you can help me a little.

THE EXERCISE

We define the inverse number of a natural number N as the natural number Nr, which is obtained by reading N from right to left, starting with the first non-zero digit. For example, if N = 4236, then Nr = 6324, and if N = 5400, then Nr = 45.

So, for any natural number G (1≤G≤10 ^ 100000) write a program in C that checks whether G can occur by the sum of the natural number N and its inverse Nr. If there is such a number, then the program should return it N. If not, then the program should return 0. The input number G will be transmitted through a txt file consisting of only 1 line.

For example, using C, if number1.txt contains number 33, then the program with the instruction:

> ./sum_of_reverse number1.txt 

may return, for example, 12, because 12 + 21 = 33 or 30, because 30 + 3 = 33. If number1.txt contains the number 42, then the program will return 0.

Now in ML, if number1.txt contains number 33, then the program with the instruction:

 sum_of_reverse "number1.txt"; 

he will return:

 val it = "12" : string 

The program should work after about 10 seconds with a space limit: 256 MB

The problems i am facing

  • At first I tried to find patterns that contain numbers with this property. I learned that numbers like 11,22,33,44,888, or numbers like 1001, 40004, 330033, can easily be written as the sum of the inverse numbers. But then I found out that these numbers seem endless because of numbers, for example, 14443 = 7676 + 6767 or 115950 = 36987 + 78963.

  • Even if I try to include all of the above patterns in my algorithm, my program will not work after 10 seconds for very large numbers, because I will need to find the length of the specified number, which takes a lot of time.

  • Since the number will be transmitted via txt, in the case of a number with 999999 digits, I assume that I just can not pass the value of this integer to a variable. Same thing with the result. I assume that you are going to save it to txt first and then print it?

Therefore, I assume that I should find an algorithm that takes a group of digits from txt, checks them for something, and then moves on to the next group of numbers ...?

+5
source share
6 answers

Let the number of digits at the input be N (after missing any leading zeros). Then - if my analysis below is correct - the algorithm only requires & approx; N bytes of space and one loop that works & approx; N / 2 times. No special "large numbers" or recursive functions are required.

Observations

The larger of the 2 numbers that make up this number should either:
(a) have N digits, OR
(b) have N-1 digits (in this case, the first digit should be 1 in total)

There is probably a way to deal with these two scenarios as one, but I did not think about it. In the worst case, you need to execute the algorithm below twice for numbers starting with 1.

In addition, when adding numbers:

  • the maximum amount of 2 digits is 18, which means the maximum outgoing carry 1
  • even with incoming transfer 1, the maximum amount is 19, so anyway, the maximum transfer 1
  • the outgoing transfer does not depend on the incoming transfer, unless the sum of two digits is exactly 9

Adding them

In the text below, all variables are a single digit, and the adjacency of variables simply means adjacent digits (not multiplication). The operator denotes a sum modulo 10. I use the notation xc XS to denote the carry digits (0-1) and the sum (0-9) obtained from adding two digits.

Take an example of 5 digits, which is sufficient to study logic, which can then be generalized to any number of digits.

  ABCDE + EDCBA 

Let A + E = xc XS , B + D = yc YS and C + C = 2 * C = zc ZS

In the simple case, when all transfers are equal to zero, the result is a palindrome:

XS YS ZS YS XS

But due to hyphenation, it is more like:

xc XS⊕yc YS⊕zc ZS⊕yc YS⊕xc XS

I say “how” because of the case mentioned above, where the sum of 2 digits is 9. In this case, the amount itself is not transferred, but the previous transfer can be spread through it. So, we will be more generalized and write:

c5 XS⊕c4 YS⊕c3 ZS⊕c2 YS⊕c1 XS

This is what should match the input number - if there is a solution. If not, we will find something that does not match and will not work.

(Informal logic for) Algorithm

We do not need to store the number in a numeric variable, just use a character array / string. All math happens on separate digits (just use int digit = c[i] - '0' , you don't need atoi and co.)

We already know the value of c5 depending on whether we are in case (a) or (b) described above.

Now we start a cycle that takes pairs of numbers from two ends and makes its way to the center. Let me name two digits that are compared in the current iteration of H and L. Thus, the loop will compare:

  • XS⊕c4 and XS
  • YS⊕c3 and YS⊕c1
  • and etc.

If the number of digits is odd (as in this example), after the loop there will be one last piece of logic for the central digit.

As we will see, at each step we will already consider the cout porting that should have come out of H and porting the cin that comes in L. (If you are going to write your C ++ code, don't really use cout and cin as names variables!)

Initially, we know that cout = c5 and cin = 0 and quite clearly XS = L (we usually use L⊖cin ).

Now we have to confirm that H, which is XS⊕c4 , is the same digit as XS or XS⊕1 . If not, there is no solution - exit.

But if it is so good, and we can calculate c4 = H⊖L Now there are 2 cases: -

  • XS is <= 8 and therefore xc = cout
  • XS is 9, and in this case xc = 0 (since 2 digits cannot contain up to 19), and c5 should be equal to c4 (if not, exit)

Now we know both xc and XS. For the next step, cout = c4 and cin = xc (in general, you also need to consider the previous cin value). Now, comparing YS⊕c3 and YS⊕c1 , we already know c1 = cin and can calculate YS = L&ominus;c1 . The rest of the logic follows as before.

For the center digit, check that ZS is a multiple of 2 times outside the loop.

If we pass all these tests, then one or more solutions exist, and we find independent sums A + E, B + D, C + C. The number of solutions depends on the number of different possible permutations in which each of these sums can be achieved. If you want only one solution, just take sum/2 and sum-(sum/2) for each individual sum (where / denotes an integer division).

I hope this works, although I won’t be surprised if it turns out to be a simpler and more elegant solution.

Adding

This problem teaches you that programming is not only about knowing how to rotate the loop, you also need to figure out how efficient and effective the loop is after a detailed logical analysis. The huge upper limit of the input number will probably make you think about it and not get away easily with brute force. This is an important skill for developing critical parts of a scalable program.

+2
source

I think you should deal with your numbers as C strings. This is probably the easiest way to quickly find the return number (read the number in C buffer back). Then, the fun part is writing the Big Number math routines to add. This is not as difficult as you think, since the addition is processed only one digit at a time with a potential carry value to the next digit.

Then, for the first pass, start from scratch and see if G is its inverse. Then 0 + 1 and G-1, then ... continue the cycle to G / 2 and G / 2. This may take more than 10 seconds for a large quantity, but it is a good place to start. (note that with numbers like this, this will not be enough, but it will become the basis for future work.)

After that, I know that there are several mathematical reductions that could be made to speed them up (numbers of different lengths cannot be inverse to each other - keep trailing zeros, start in the middle (G / 2) and count outwards, so that the lengths are the same, but the match is captured faster, etc.)

+3
source

Based on the length of the input, there are no more than two possibilities for the length of the response. Try them apart. For example, suppose the answer is 8 digits, ABCDEFGH. Then the amount can be represented as:

  ABCDEFGH
 + HGFEDCBA

In particular, look at the sums at the extreme points: the last sum (H + A) is equal to the first sum (A + H). You can also see the following two sums: G + B equals B + G. This suggests that we should try to build our number from both extremes and go to the middle.

Let extremes be chosen at the same time. For each possibility of the pair (A, H), having looked at whether A + H coincides with the first digit of the sum, we know whether the next sum (B + G) has a carry or not. And if A + H has hyphenation, then this will affect the result of B + G, so we must also save this information. Summing up the relevant information, we can write a recursive function with the following arguments:

  • how many numbers have we filled
  • did the last amount carry forward?
  • if the current amount has carry over?

This recursion has exponential complexity, but we can note that there are no more than 50,000 * 2 * 2 = 200,000 possible arguments with which it can be called. Therefore, remembering the values ​​of this recursive function should give us an answer in less than 10 seconds.

Example:

Entering 11781, suppose the answer is 4 digits.

  Abcd
 + DCBA

Since our numbers have 4 digits and the answer has 5, A + D has a carry. Therefore, we call rec (0, 0, 1), given that so far we have chosen 0 numbers, the current amount has carry over, and the previous amount was not.

Now try all the possibilities for (A, D). Suppose we choose (A, D) = (9.2). 9 + 2 matches the first and final 1 in the answer, so that's good. We now note that B + C cannot have a carry, otherwise the first A + D will exit as 12, not 11. Thus, we call rec (2, 1, 0).

Now try all the possibilities for (B, C). Suppose we choose (B, C) = (3.3). This is not good, because it does not correspond to the values ​​that the sum B + C should get. Suppose we choose (B, C) = (4.3). 4 + 3 matches of 7 and 8 at the input (remembering that we got a carry from A + D), so this is a good answer. Return "9432" as our answer.

+2
source

I do not think that you will have many successful support numbers up to 10 ^ 100000; quick search on wikipedia. I just showed that even 80-bit floats only go up to 10 ^ 4932.

But assuming you're going to limit yourself to numbers that C can handle, one method would be something like this (this is pseudocode):

 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 } 

This code will need to be slightly modified according to C (this pseudo code is based on what I wrote in JavaScript), but there is a basic logic.

If you need NUMBERS that are larger than C, you can find them in large libraries or just create your own addition / subtraction methods for arbitrarily large numbers (maybe store them in strings / char arrays?).

+1
source

A way to make the program faster will be ... You may notice that your input number should be a linear combination of numbers, such as:

100 ... 001, 010 ... 010, ..., and the last will be 0 ... 0110 ... 0 if #digits is even or 0 ... 020 ... 0 if #digits is odd.

Example: G = 11781

G = 11x1001 + 7x0110

Then each number abcd such that a + d = 11 and b + c = 7 will be a solution.

A way to develop is to start subtracting these numbers until you can no longer. If you find zero at the end, then there is an answer that you can build from the coefficients, otherwise not.

+1
source

I did this and it seems to work:

 #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; } 
0
source

Source: https://habr.com/ru/post/1247668/


All Articles