Sei sulla pagina 1di 4

11/9/2014

post.html

Recently I stumbled back upon Rune-Server while studying for the ACM ICPC Regionals. I figured that I would see if there were any challenges available.
I found the "small challenge" post (located at http://www.rune-server.org/programming/application-development/399239-small-challenge.html)
Here is my solution:
I was able to recall a problem from a competition where you didn't in fact need the whole BigInteger when doing calculations so I tried this:
int f1 = 1, f2 = 1;
for (int n = 3; ; ++n) {
int f = (f1 + f2) % 1000000000;
f2 = f1;
f1 = f;
}

Where f is F( n ), f1 is F(n-1), and f2 is F(n-2).


This appears to compute the last 9 digits of F( n ) correctly.
Now for the beginning digits. At first I attempted the same solution, just for the upper digits. I didn't have any luck.
I remembered there exists a closed-form of the Nth Fibonacci:

(More can be read about Binet's formula here: Jacques Philippe Marie Binet - Wikipedia, the free encyclopedia)
However, since we cannot deal with 1+sqrt(5) (the golden ratio) correctly in Java, we are in trouble. However, this shouldn't be a problem since we just need a fast
approximation with only 9 digits.
I attempted implementing this with no luck, the powers were simply to large for Java to handle. However, when I faced this problem in the past I used a trick with
logarithms to solve large powers.
Log's have a nice property that given (x^y)/z, can be calculated like this: exp(ln(x)*y-z). At a first glance, this doesn't solve our original problem at all because we still
have to raise e to a very large power.
There is a trick. If you use base 10 logarithms, you can easily pick and choose where you want that decimal placed just by subtracting or adding to your exponent.
For example, say you want to first 5 digits of (123^456)/789 (good luck).
First we need to compute our exponent, x.
x = log(123)*456789-log(789) = 1956.28341785 (approx.)
Then we need to move the decimal point and compute our final calculation:
floor(10^(x - floor(x) + 5 - 1)) = 19205 (approx)
Boom.
Proof:
floor(10^(x - floor(x) + 5 - 1)) where x = 654*log10(987)-log10(100) - Wolfram|Alpha
(987^654)/100 - Wolfram|Alpha
static int firstDigits(double base, int exp, double denom, int digits) {
double x = exp * Math.log10(base) - Math.log10(denom);
return (int) Math.pow(10, x - Math.floor(x) + digits - 1);
}

This doesn't help us completely in implementing Binet's formula. We still have that nasty reciprocal of the golden ratio. We could try introducing negative power, but that
wouldn't work well for our logarithm technique.
There is however a similar formula that is an approximation, described here: Fibonacci number - Wikipedia, the free encyclopedia

Perfect! Now we just plug our values in:


first 9 digits of F( n ) =
firstDigits((1 + Math.sqrt(5)) / 2, n, Math.sqrt(5), 9)

Optimized:
static
static
static
static

final
final
final
final

double
double
double
double

root5 =
phi = (1
logphi =
logroot5

Math.sqrt(5);
+ root5) / 2;
Math.log10(phi);
= Math.log10(root5);

static int fibEnd(int n) {


double x = logphi * n - logroot5;
return (int) Math.pow(10, x - Math.floor(x) + 9 - 1);
}

Awesome. Now we just put them together and we get this:


file:///C:/Users/Joe/Desktop/post.html

1/4

11/9/2014

post.html

/* author killer_99 */
public class pandigital {
public static void main(String[] args) throws Throwable {
long elapse = 0;
int result = -1;
for (int i = 0; i < 1; ++i) {
result = -1;
long start = System.nanoTime();
int f1 = 1, f2 = 1;
for (int n = 3; ; ++n) {
int f = (f1 + f2) % 1000000000;
if (isPandigital(f) && isPandigital(fibEnd(n))) {
result = n;
break;
}
f2 = f1;
f1 = f;
}
long end = System.nanoTime();
elapse = end - start;
}
System.out.println(result);
System.out.println(elapse + "ns (" + (elapse / 1000000D) + "ms)");
}
static
static
static
static

final
final
final
final

double
double
double
double

root5 =
phi = (1
logphi =
logroot5

Math.sqrt(5);
+ root5) / 2;
Math.log10(phi);
= Math.log10(root5);

static int fibEnd(int n) {


double x = logphi * n - logroot5;
return (int) Math.pow(10, x - Math.floor(x) + 9 - 1);
}

static boolean isPandigital(long n) {


if (n < 100000000)
return false;
int set = 0;
for (int i = 0; i < 9; ++i) {
int v = (int) (n % 10);
if (v == 0)
return false;
if ((set & (1 << v)) != 0)
return false;
set |= 1 << v;
n /= 10;
}
return true;
}

I forgot to mention the implementation of isPandigital, but it is trivial and probably could be implemented in several different ways. I used an optimized bit set with
remainders of 10.
Hope this helps!
I decided to write a highly optimized version of this, just to see how fast I could make it.
/* author killer_99 */
public class pandigital {
public static void main(String[] args) throws Throwable {
int i = 1; //default
int trials = 1000;
int expected = 329468; //taken from the original thread ;)
int subTrials = 20;
for (int j = 1; j <= subTrials; ++j) {
double result = avgBench(i, trials, expected);
System.out.println("Trial " + j + ": " + result + " ns (" + (result / 1000000D) + " ms)");
}
}
static double avgBench(int i, int trials, int expected) {
return bench(i, trials, expected) / (double) trials;
}
static long bench(int i, int trials, int expected) {
long start = System.nanoTime();
for (int j = 1; j <= trials; ++j) {
int result = search(i);
if (result != expected)
throw new RuntimeException(result + " != " + expected);
}
long end = System.nanoTime();
return end - start;
}
//Change n to type long for larger values
static int search(int i) {
//We can use int's here because we only need the first 9 digits and 1 decimal for a carry (10 total digits)
//Which int is sufficient for.
//We can start at F(38) and F(39) because F(40) is the first value with enough digits.
//Dynamic programming approach.
int f2 = 39088169, f1 = 63245986;

file:///C:/Users/Joe/Desktop/post.html

2/4

11/9/2014

post.html

for (int n = 40; ; ++n) {


int f = (f1 + f2);// % 1000000000;
if (f >= 1000000000) f -= 1000000000; //optimization of f mod 1000000000
if (isPandigital(f) && isPandigital(fibEnd(n)) && --i <= 0)
return n;
f2 = f1;
f1 = f;
}

//Constants
static final
static final
static final
static final

double
double
double
double

root5 =
phi = (1
logphi =
logroot5

Math.sqrt(5);
+ root5) / 2;
Math.log10(phi);
= Math.log10(root5);

//Change n to type long for larger values


//Optimized version of our firstDigits method
//Math.floor(x) was replaced with (long) x
static int fibEnd(int n) {
double x = logphi * n - logroot5; //our exponent
return (int) Math.pow(10, x - (long) x + 9 - 1); //move to the last 9 digits and calculate.
}
//Division trick.
static final long inverse9 = ((1L<<32)+9-1) / 9;
static final long inverse10 = ((1L<<32)+10-1) / 10;
static boolean isPandigital(int n) {
////////123456789
if (n < 100000000) //not large enough. a result of leading zeros.
return false;
//all pandigitals have the property of being divisible of 9
if (n != 9 * (int) ((n * inverse9) >> 32)) //check if n is divisible by 9
return false;
int set = 0, i = 0;
while (true) {
int r = (int) ((n * inverse10) >> 32);//n / 10;
int v = n - r * 10;
if (v == 0)
return false; //zero is not aloud!
if ((set & (1<

Results on my PC (i5-2500k @ 4.6 ghz with 16gb of RAM)

Trial
Trial
Trial
Trial
Trial
Trial
Trial
Trial
Trial
Trial
Trial
Trial
Trial
Trial
Trial
Trial
Trial
Trial
Trial
Trial

1: 1680504.045 ns (1.680504045 ms)


2: 1669781.725 ns (1.669781725 ms)
3: 1653643.198 ns (1.6536431980000001 ms)
4: 1677129.606 ns (1.6771296059999998 ms)
5: 1686573.059 ns (1.6865730589999999 ms)
6: 1667213.73 ns (1.66721373 ms)
7: 1651068.982 ns (1.6510689820000002 ms)
8: 1643373.394 ns (1.6433733940000002 ms)
9: 1641080.33 ns (1.64108033 ms)
10: 1647728.132 ns (1.647728132 ms)
11: 1634336.428 ns (1.6343364280000001 ms)
12: 1652975.152 ns (1.652975152 ms)
13: 1652460.122 ns (1.652460122 ms)
14: 1649676.909 ns (1.649676909 ms)
15: 1656628.565 ns (1.6566285649999999 ms)
16: 1655356.852 ns (1.655356852 ms)
17: 1657532.355 ns (1.657532355 ms)
18: 1654802.325 ns (1.654802325 ms)
19: 1654091.36 ns (1.65409136 ms)
20: 1660662.653 ns (1.660662653 ms)

I would be interested in seeing a multi-threaded version of this, but that would be trivial and maybe worse for small values and only useful for calculating say..

For fun, here are the first 100 outputs. This didn't take very long at all. I changed the parameters to use longs to ensure accuracy and allow for larger outputs.

329468 @ 1699038 ns (1.699038 ms)


6496145 @ 33187688 ns (33.187688 ms)
6677673 @ 34100497 ns (34.100497 ms)
21552527 @ 112036050 ns (112.03605 ms)
25305770 @ 131114849 ns (131.114849 ms)
25587471 @ 132770656 ns (132.770656 ms)
44330360 @ 229360793 ns (229.360793 ms)
51855023 @ 267815405 ns (267.815405 ms)
55789533 @ 288193908 ns (288.193908 ms)
60022132 @ 309854387 ns (309.854387 ms)
71582467 @ 369452894 ns (369.452894 ms)
71850664 @ 370852431 ns (370.852431 ms)
74651596 @ 385088833 ns (385.088833 ms)
76145155 @ 392620830 ns (392.62083 ms)

file:///C:/Users/Joe/Desktop/post.html

3/4

11/9/2014

post.html

78236482 @ 403364920 ns (403.36492 ms)


78313196 @ 403779183 ns (403.779183 ms)
79854278 @ 412195687 ns (412.195687 ms)
97767194 @ 508385868 ns (508.385868 ms)
99141649 @ 515519463 ns (515.519463 ms)
110332299 @ 573158618 ns (573.158618 ms)
125012963 @ 650749573 ns (650.749573 ms)
128791121 @ 671007094 ns (671.007094 ms)
142201377 @ 741361510 ns (741.36151 ms)
151237298 @ 787577022 ns (787.577022 ms)
161082694 @ 840028870 ns (840.02887 ms)
166798039 @ 871145554 ns (871.145554 ms)
186817307 @ 975944753 ns (975.944753 ms)
190905194 @ 997673965 ns (997.673965 ms)
206626179 @ 1079654801 ns (1079.654801 ms)
208337872 @ 1088263509 ns (1088.263509 ms)
211159994 @ 1102756492 ns (1102.756492 ms)
215573758 @ 1125096525 ns (1125.096525 ms)
218947570 @ 1142396358 ns (1142.396358 ms)
224179646 @ 1170551000 ns (1170.551 ms)
231507145 @ 1208778577 ns (1208.778577 ms)
231884457 @ 1210859533 ns (1210.859533 ms)
251032855 @ 1309400935 ns (1309.400935 ms)
253495283 @ 1321897557 ns (1321.897557 ms)
254334371 @ 1326118560 ns (1326.11856 ms)
256667607 @ 1338186923 ns (1338.186923 ms)
259042382 @ 1350544835 ns (1350.544835 ms)
267626691 @ 1394803484 ns (1394.803484 ms)
283112441 @ 1476193715 ns (1476.193715 ms)
286840127 @ 1495161796 ns (1495.161796 ms)
298747069 @ 1556315655 ns (1556.315655 ms)
302037683 @ 1573622641 ns (1573.622641 ms)
304186216 @ 1585444686 ns (1585.444686 ms)
305570751 @ 1592531009 ns (1592.531009 ms)
317812488 @ 1656557651 ns (1656.557651 ms)
318339170 @ 1659814218 ns (1659.814218 ms)
319945154 @ 1669797894 ns (1669.797894 ms)
321597362 @ 1679826355 ns (1679.826355 ms)
332796942 @ 1737623813 ns (1737.623813 ms)
333944433 @ 1743743522 ns (1743.743522 ms)
338077689 @ 1764890837 ns (1764.890837 ms)
343814750 @ 1795136387 ns (1795.136387 ms)
351870349 @ 1836744623 ns (1836.744623 ms)
353463338 @ 1845579123 ns (1845.579123 ms)
355483753 @ 1856206896 ns (1856.206896 ms)
368345572 @ 1923157327 ns (1923.157327 ms)
381491353 @ 1991022744 ns (1991.022744 ms)
381776334 @ 1992487903 ns (1992.487903 ms)
384681669 @ 2008295829 ns (2008.295829 ms)
401102390 @ 2093113060 ns (2093.11306 ms)
406247068 @ 2120185704 ns (2120.185704 ms)
452223361 @ 2359890632 ns (2359.890632 ms)
465276504 @ 2428727639 ns (2428.727639 ms)
489590248 @ 2556397139 ns (2556.397139 ms)
492621123 @ 2572039609 ns (2572.039609 ms)
498514961 @ 2601811493 ns (2601.811493 ms)
510214928 @ 2664282161 ns (2664.282161 ms)
527796908 @ 2760359135 ns (2760.359135 ms)
528277311 @ 2762794018 ns (2762.794018 ms)
536969786 @ 2807436139 ns (2807.436139 ms)
545743218 @ 2853754906 ns (2853.754906 ms)
549712722 @ 2874301353 ns (2874.301353 ms)
555641893 @ 2904528553 ns (2904.528553 ms)
581952380 @ 3041724236 ns (3041.724236 ms)
586921894 @ 3068327258 ns (3068.327258 ms)
589140104 @ 3079607527 ns (3079.607527 ms)
592609004 @ 3098281705 ns (3098.281705 ms)
604524121 @ 3160593137 ns (3160.593137 ms)
614016664 @ 3210580557 ns (3210.580557 ms)
614844293 @ 3214788187 ns (3214.788187 ms)
616463425 @ 3222969570 ns (3222.96957 ms)
619346653 @ 3237697987 ns (3237.697987 ms)
619980232 @ 3240889553 ns (3240.889553 ms)
640050428 @ 3343681505 ns (3343.681505 ms)
646159639 @ 3374796012 ns (3374.796012 ms)
646394093 @ 3376036313 ns (3376.036313 ms)
646866698 @ 3378435741 ns (3378.435741 ms)
649357364 @ 3392040796 ns (3392.040796 ms)
667293089 @ 3485712432 ns (3485.712432 ms)
682167770 @ 3563207285 ns (3563.207285 ms)
682526826 @ 3565143934 ns (3565.143934 ms)
690226007 @ 3604377622 ns (3604.377622 ms)
699708733 @ 3654513393 ns (3654.513393 ms)
702022089 @ 3667448847 ns (3667.448847 ms)
708983533 @ 3705744845 ns (3705.744845 ms)
709821957 @ 3710832318 ns (3710.832318 ms)

file:///C:/Users/Joe/Desktop/post.html

4/4

Potrebbero piacerti anche