Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
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;
}
(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
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);
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);
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
//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);
Trial
Trial
Trial
Trial
Trial
Trial
Trial
Trial
Trial
Trial
Trial
Trial
Trial
Trial
Trial
Trial
Trial
Trial
Trial
Trial
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.
file:///C:/Users/Joe/Desktop/post.html
3/4
11/9/2014
post.html
file:///C:/Users/Joe/Desktop/post.html
4/4