# Include <stdlib. h>
Long A = 10000, B, c = 2800, d, e, f [2801], G;
Main ()
{
For (; B-c;) f [B ++] = A/5;
For (; D = 0, G = C * 2; C-= 14, printf ("%. 4D", e + D/A), E = D %)
For (B = C; D + = f [B] * A, F [B] = D % -- g, D/= g --, -- B; D * = B );
System ("pause ");
}
Cracking the weird PI Program
Cong Wang
25th November, 2005
Institute of Post and Telecommunication, si'an, PRC China
Network Engineering DEP.
Introduction
There is a weird PI program circulating on the Internet. Although there are only three rows, the PI value can be obtained to reach a total of 800 digits before the decimal point. This program is as follows:
[Copy to clipboard]
Code:
/* A year of obfuscated C Contest :*/
# Include <stdio. h>
Long A = 10000, B, c = 2800, d, e, f [2801], G;
Main (){
For (; B-c;) f [B ++] = A/5;
For (; D = 0, G = C * 2; C-= 14, printf ("%. 4D", e + D/A), E = D %)
For (B = C; D + = f [B] * A, F [B] = D % -- g, D/= g --, -- B; D * = B );
}
/* (This program can calculate the PI value before the decimal point, a total of 800 digits)
(This program is recorded in sci. Math FAQ. The original author is unknown )*/
At first glance, this program is quite scary. Don't be flustered. Here we will show you how it works and some tips for writing weird C Programs. Pai_^
Expand and simplify
We know that in C, the for loop and the while loop can replace each other.
[Copy to clipboard]
Code:
For (statement1; statement2; statement3 ){
Statements;
}
The preceding for statement can be replaced by the following while statement:
[Copy to clipboard]
Code:
Statement1;
While (statement2 ){
Statements;
Statement3;
}
In addition, to write weird C Programs, the comma operator is undoubtedly a good assistant. Its role is:
Calculate the values of each expression from left to right, and return the value of the rightmost expression.
Embedding it in a for loop is one of the common tips for writing weird code. Therefore, the above program can be expanded:
[Copy to clipboard]
Code:
# Include <stdio. h>/* 1 */
/* 2 */
Long A = 10000, B, c = 2800, d, e, f [2801], G;/* 3 */
Main () {/* 4 */
While (B-c! = 0) {/* 5 */
F [B] = A/5;/* 6 */
B ++;/* 7 */
}/* 8 */
D = 0;/* 9 */
G = C * 2;/* 10 */
While (G! = 0) {/* 11 */
B = C;/* 12 */
D + = f [B] * A;/* 13 */
F [B] = D % -- g;/* 14 */
D = D/g --;/* 15 */
-- B;/* 16 */
While (B! = 0) {/* 17 */
D = D * B + F [B] * A;/* 18 */
F [B] = D % -- g;/* 19 */
D = D/g --;/* 20 */
-- B;/* 21 */
}/* 22 */
C-= 14;/* 23 */
Printf ("%. 4D", e + D/A);/* 24 */
E = D % A;/* 25 */
D = 0;/* 26 */
G = C * 2;/* 27 */
}/* 28 */
}/* 29 */
Is it nice now?
Further simplification
You should be aware that the value of a is always 10000, so we can replace a with 10000. Then, observe g carefully. In the outer loop, when it is used for division or remainder, it is always equal to 2 * C-1, and B is always initialized to C. In an inner layer loop, B decreases by 1 each time, and g decreases by 2 each time. You will think of it now? Replace g with 2 * B-1! Try it on your behalf. That's right! In addition, we can also make a little simplification. The D = 0 of the 26th rows is redundant. We will merge it into the 13th rows, and the 13th rows can be rewritten: D = f [B] * ;. So the program can be changed:
[Copy to clipboard]
Code:
# Include <stdio. h>
Long B, c = 2800, d, e, f [2801];
Main (){
While (B-c! = 0 ){
F [B] = 2000;
B ++;
}
While (C! = 0 ){
B = C;
D = f [B] x 10000;
F [B] = D % (B * 2-1 );
D = D/(B * 2-1 );
-- B;
While (B! = 0 ){
D = D * B + F [B] * 10000;
F [B] = D % (B * 2-1 );
D = D/(B * 2-1 );
-- B;
}
C-= 14;
Printf ("%. 4D", e + D/10000 );
E = D %10000;
}
}
Two variables are missing!
In-depth analysis
Now, we will go to the substantive analysis. Certain mathematical knowledge is very necessary. First, you must know that the following formula can be used to calculate pi:
PI/2 = 1 + 1! /3 !! + 2! /5 !! + 3! /7 !! +... + K! /(2 * k + 1 )!! +...
Pi has enough precision as long as there are enough items. As for why, we leave it to mathematicians.
Anyone who has written a high-precision division program knows how to use an integer array for division, instead of decimal places. In fact, it is very easy to think about how you perform division by hand. Divide the number by the divisor and place the obtained number into the result. Multiply the remainder by 10 and proceed to the next division until the remainder is zero or the required number of digits has been reached.
The original program uses so much mathematical knowledge that it is complicated and difficult to understand because it cleverly places algorithms in a loop. Let's start to analyze the program. First, let's start with the mathematical formula. We seek Pi, and the formula gives PI/2. Therefore, we multiply both sides of the formula by 2 at the same time:
[Copy to clipboard]
Code:
Pi = 2*1 + 2*1! /3 !! + 2*2! /5 !! + 2*3! /7 !! +... + 2 * k! /(2 * k + 1 )!! +...
Next, we rewrite it into another form and expand it:
[Copy to clipboard]
Code:
Pi = 2*1 + 2*1! /3 !! + 2*2! /5 !! + 2*3! /7 !! +... + 2 * n! /(2 * n + 1 )!!
= 2*(n-1)/(2 * N-1) * (n-2)/(2 * n-3) * (n-3)/(2 * N-5 )*... * 3/7x2/5*1/3
+ 2*(n-2)/(2 * n-3) * (n-3)/(2 * N-5) *... * 3/7*2/5*1/3
+ 2*(n-3)/(2 * N-5) *... * 3/7*2/5*1/3
+ 2*3/7*2/5*1/3
+ 2*2/5*1/3
+ 2*1/3
+ 2*1
Look at the formula, we can see that B Corresponds to the formula of N, 2 * B-1 corresponds to 2 * n-1. B starts from 2800, that is, n = 2800. (As to why n = 2800, the first 800 digits of Pi are not covered here .) Check the corresponding part of the program:
[Copy to clipboard]
Code:
D = D * B + F [B] * 10000;
F [B] = D % (B * 2-1 );
D = D/(B * 2-1 );
D is used to store the integer part of the division result. It is accumulated, so the last d is the integer part we want. F [B] is used to store the remainder of the computation to B.
You may not understand it yet. First, why is the array containing 2801 elements? This is simple, because the author wants to use F [1] ~ F [2800], and the array subscript of C language starts from 0, and f [0] cannot be used. Second, why should we Initialize all array elements except f [2800] to 2000? What is the role of 10000? This is interesting. Because from printf ("%. 4D ", e + D/10000); we can see that D/10000 is the number before the first digit of D, and E = D % 4th ;, E is the last four digits of D. Furthermore, E and D are in a different loop. So the printed result is exactly the corresponding 4 bits of the PI we want! At the beginning, the reason why we initialize the array element to 2000 is that we can enlarge the PI by 1000 times to ensure that the integer part has four digits, and that 2 is the 2 multiplied by both sides of our formula! So it's 2000! Note that the remainder must be multiplied by 10000 instead of 10! F [2800] is 0 because the first multiplication is n-1, that is, 2799, rather than 2800! Every 4 bits are calculated, C minus 14, which ensures that a total of 4*2800/14 = 800 bits can be printed. However, this will not affect the accuracy of the results! I am not very familiar with mathematics and cannot give a definite answer. (If anyone knows, please email me to the xiyou.wangcong@gmail.com to tell me .)
By mistake, I saw an "accurate" adapted based on the above program on the Internet (this accurate refers to the absence of any element in the f [] array .) Print the 2800-bit program as follows:
[Copy to clipboard]
Code:
Long B, c = 2800, d, e, f [2801], G;
Int main (INT argc, char * argv [])
{
For (B = 0; B f [B] = 2;
E = 0;
While (C> 0)
{
D = 0;
For (B = C; B> 0; B --)
{
D * = B;
D + = f [B] * 10;
F [B] = D % (B * 2-1 );
D/= (B * 2-1 );
}
C-= 1;
Printf ("% d", (e + D/10) % 10 );
E = D % 10;
}
Return 0;
}
Try compressing the above program into three lines.
Conclusion
End the full text with one sentence in knuth Turing's speech:
[Copy to clipboard]
Code:
We have seen that computer programming is an art, because it applies accumulated knowledge to the world, because it requires skill and ingenuity, and especially because it produces objects of beauty. A programmer who subconsciously views himself as an artist will enjoy what he does and will do it better.
I would like to share with you! Pai_^
(EOF)