Check if root of cubic equation is complex or not? - c#

I use this Cubic root implementation.
I have equation #1:
x³ -2 x² -5 x + 6 = 0
It gives me 3 complex roots ({real, imaginary}):
{-2, 7.4014868308343765E-17}
{1 , -2.9605947323337506E-16}
{3 , 2.9605947323337506E-16}
But in fact, the right result should be 3 non-complex roots: -2, 1, 3.
With this case, I can test by: apply 3 complex roots to the equation, it returns non-zero result (failed); apply 3 non-complex roots to the equation, it returns zero result (passed).
But there is the case where I apply both 3-complex roots and 3-non-complex roots to the equation (e.g. 47 x³ +7 x² -52 x + 0 = 0), it return non-zero (failed).
I think what causes this issue is because of this code:
/// <summary>
/// Evaluate all cubic roots of this <c>Complex</c>.
/// </summary>
public static (Complex, Complex, Complex) CubicRoots(this Complex complex)
{
var r = Math.Pow(complex.Magnitude, 1d/3d);
var theta = complex.Phase/3;
const double shift = Constants.Pi2/3;
return (Complex.FromPolarCoordinates(r, theta),
Complex.FromPolarCoordinates(r, theta + shift),
Complex.FromPolarCoordinates(r, theta - shift));
}
I know that floating point value can lose precision when calculating (~1E-15), but the problem is the imaginary part needs to decide weather it's zero or non-zero to tell if it's complex number or not.
I can't tell the user of my app: "hey user, if you see the imaginary part is close enough to 0, you can decide for yourself that the root's not a complex number".
Currently, I use this method to check:
const int TOLERATE = 15;
bool isRemoveImaginary = System.Math.Round(root.Imaginary, TOLERATE) == 0; //Remove imaginary if it's too close to zero
But I don't know if this method is appropriate, what if the TOLERATE = 15 is not enough. Or is it the right method to solve this problem?
So I want to ask, is there any better way to tell the root is complex or not?

Thank you Mark Dickinson.
So according to Wikipedia:
delta > 0: the cubic has three distinct real roots
delta < 0: the cubic has one real root and two non-real complex
conjugate roots.
The delta D = (B*B - 4*A*A*A)/(-27*a*a)
My ideal is:
delta > 0: remove all imaginary numbers of 3 roots.
delta < 0: find the real root then remove its imaginary part if any
(to make sure it's real). Leave the other 2 roots untouched. Now I
have 2 ideas to find the real root:
Ideal #1
In theory, the real root should have imaginary = 0, but due to floating point precision, imaginary can deviate from 0 a little (e.g. imaginary = 1E-15 instead of 0). So the idea is: the 1 real root among 3 roots should have the imaginary whose value is closest to 0.
Code:
NumComplex[] arrRoot = { x1, x2, x3 };
if (delta > 0)
{
for (var idxRoot = 0; idxRoot < arrRoot.Length; ++idxRoot)
arrRoot[idxRoot] = arrRoot[idxRoot].RemoveImaginary();
}
else
{
//The root with imaginary closest to 0 should be the real root,
//the other two should be non-real.
var realRootIdx = 0;
var absClosest = double.MaxValue;
double abs;
for (var idxRoot = 0; idxRoot < arrRoot.Length; ++idxRoot)
{
abs = System.Math.Abs(arrRoot[idxRoot].GetImaginary());
if (abs < absClosest)
{
absClosest = abs;
realRootIdx = idxRoot;
}
}
arrRoot[realRootIdx] = arrRoot[realRootIdx].RemoveImaginary();
}
The code above can be wrong if there are 3 roots ({real, imaginary}) like this:
{7, -1E-99}
{3, 1E-15}//1E-15 caused by floating point precision, 1E-15 should be 0
{7, 1E-99}//My code will mistake this because this is closer to 0 than 1E-15.
Maybe if that case does happen in real life, I will come up with a better way to pick the real root.
Idea #2
Take a look at how the 3 roots calculated:
x1 = FromPolarCoordinates(r, theta);
x2 = FromPolarCoordinates(r, theta + shift);
x3 = FromPolarCoordinates(r, theta - shift);
3 roots have the form (know this by tests, not proven by math):
x1 = { A }
x2 = { B, C }
x3 = { B, -C }
Use math knowledge to prove which one among the 3 roots is the real one.
Trial #1: Maybe the root x1 = FromPolarCoordinates(r, theta) is always real? (failed) untrue because the following case proved that guess is wrong: -53 x³ + 6 x² + 14 x - 54 = 0 (Thank Mark Dickinson again)
I don't know if math can prove something like: while delta < 0: if B < 0 then x3 is real, else x1 is real?
So until I get better idea, I'll just use idea #1.

Related

Linear interpolation between two numbers with steps

I've a little trouble finding out how to linearly interpolate between two numbers with a defined number of intermediate steps.
Let's say I want to interpolate between 4 and 22 with 8 intermediate steps like so : Example
It's easy to figure out that it's x+2 here. But what if the starting value was 5 and the final value 532 with 12 intermediate steps? (In my special case I would need starting and ending value with 16 steps in between)
If you have two fence posts and you put k fence posts between them, you create k + 1 spaces. For instance:
| |
post1 post2
adding one posts creates two spaces
| | |
post1 post2
If you want those k + 1 spaces to be equal you can divide the total distance by k + 1 to get the distance between adjacent posts.
d = 22 - 4 = 18
k = 8
e = d / (k + 1) = 18 / 9 = 2
In your other case example, the answer is
d = 532 - 5 = 527
k = 12
e = d / (k + 1) = 527 / 13 ~ 40.5
I hesitate to produce two separate answers, but I feel this methodology is sufficiently unique from the other one. There's a useful function which may be exactly what you need which is appropriately called Mathf.Lerp().
var start = 5;
var end = 532;
var steps = 13;
for (int i = 0; i <= steps; i++) {
// The type conversion is necessary because both i and steps are integers
var value = Mathf.Lerp(start, end, i / (float)steps);
Debug.Log(value);
}
For actually doing the linear interpolation, use Mathf.MoveTowards().
For figuring out your maximum delta (i.e. the amount you want it to move each step), take the difference, and then divide it by the number of desired steps.
var start = 4;
var end = 22;
var distance = end - start;
var steps = 9; // Your example technically has 9 steps, not 8
var delta = distance / steps;
Note that this conveniently assumes your distance is a clean multiple of steps. If you don't know this is the case and it's important that you never exceed that number of steps, you may want to explicitly check for it. Here's a crude example for an integer. Floating point methods may be more complicated:
if (distance % delta > 0) { delta += 1; }

How to interpolate through 3 points/numbers with a defined number of samples? (in c#)

So for example we have 1, 5, and 10 and we want to interpolate between these with 12 points, we should get:
1.0000
1.7273
2.4545
3.1818
3.9091
4.6364
5.4545
6.3636
7.2727
8.1818
9.0909
10.0000
say we have 5, 10, and 4 and again 12 points, we should get:
5.0000
5.9091
6.8182
7.7273
8.6364
9.5455
9.4545
8.3636
7.2727
6.1818
5.0909
4.0000
This is a generalized solution that works by these principles:
Performs linear interpolation
It calculates a "floating point index" into the input array
This index is used to select 1 (if the fractional parts is very close to 0) or 2 numbers from the input array
The integer part of this index is the base input array index
The fractional part says how far towards the next array element we should move
This should work with whatever size input arrays and output collections you would need.
public IEnumerable<double> Interpolate(double[] inputs, int count)
{
double maxCountForIndexCalculation = count - 1;
for (int index = 0; index < count; index++)
{
double floatingIndex = (index / maxCountForIndexCalculation) * (inputs.Length - 1);
int baseIndex = (int)floatingIndex;
double fraction = floatingIndex - baseIndex;
if (Math.Abs(fraction) < 1e-5)
yield return inputs[baseIndex];
else
{
double delta = inputs[baseIndex + 1] - inputs[baseIndex];
yield return inputs[baseIndex] + fraction * delta;
}
}
}
It produces the two collections of outputs you showed in your question but beyond that, I have not tested it. Little error checking is performed so you should add the necessary bits.
The problem is an interpolation of two straight lines with different slopes given the end points and the intersection.
Interpolation is defined as following : In the mathematical field of numerical analysis, interpolation is a method of constructing new data points within the range of a discrete set of known data points.
I'm tired of people giving negative points for solutions to hard problems. This is not a simply problem, but a problem that require "thinking out of the box". lets looks at the solution for following input : 1 12 34
I picked these numbers because the results are all integers
The step size L (Lower) = distance of elements from 1 to 12 = 2
The step size H (Higher) = distance of elements from 12 to 34 = 4
So the answer is : 1 3 5 7 9 11 [12] 14 18 22 26 30 34
Notice the distance between the 6th point 11 and center is 1 (half of L)
Notice the distance between the center point 12 and the 7th point is 2 (half of H)
Finally notice the distance between the 6th and 7th points is 3.
My results are scaled exactly the same as the OPs first example.
It is hard to see the sequence with the fractional inputs the OP posted. If you look at the OP first example and calculate the step distance of the first 6 points you get 0.72. The last 6 points the distance is 0.91. Then calculate the distance from the 6th point to the center is .36 (half 0.72). Then center to 7th point 0.45 (half 0.91). Excuse me for rounding the numbers a little bit.
It is a sequence problem just like the in junior high school where you learned arithmetic and geometric sequences. Then as a bonus question you got the sequence 23, 28, 33, 42,51,59,68,77,86 which turns out to be the train stations on the NYC 3rd Ave subway system. Solving problems like this you need to think "Outside the Box" which comes from the tests IBM gives to Job Applicants. These are the people who can solve the Nine Point Problem : http://www.brainstorming.co.uk/puzzles/ninedotsnj.html
I did the results when the number of points is EVEN which in you case is 12. You will need to complete the code if the number of points is ODD.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
const int NUMBER_POINTS = 12;
static void Main(string[] args)
{
List<List<float>> tests = new List<List<float>>() {
new List<float>() { 1,5, 10},
new List<float>() { 5,10, 4}
};
foreach (List<float> test in tests)
{
List<float> output = new List<float>();
float midPoint = test[1];
if(NUMBER_POINTS % 2 == 0)
{
//even number of points
//add lower numbers
float lowerDelta = (test[1] - test[0])/((NUMBER_POINTS / 2) - .5F);
for (int i = 0; i < NUMBER_POINTS / 2; i++)
{
output.Add(test[0] + (i * lowerDelta));
}
float upperDelta = (test[2] - test[1]) / ((NUMBER_POINTS / 2) - .5F); ;
for (int i = 0; i < NUMBER_POINTS / 2; i++)
{
output.Add(test[1] + (i * upperDelta) + (upperDelta / 2F));
}
}
else
{
}
Console.WriteLine("Numbers = {0}", string.Join(" ", output.Select(x => x.ToString())));
}
Console.ReadLine();
}
}
}

'Grokkable' algorithm to understand exponentiation where the exponent is floating point

To clarify first:
2^3 = 8. That's equivalent to 2*2*2. Easy.
2^4 = 16. That's equivalent to 2*2*2*2. Also easy.
2^3.5 = 11.313708... Er, that's not so easy to grok.
Want I want is a simple algorithm which most clearly shows how 2^3.5 = 11.313708. It should preferably not use any functions apart from the basic addition, subtract, multiply, or divide operators.
The code certainly doesn't have to be fast, nor does it necessarily need to be short (though that would help). Don't worry, it can be approximate to a given user-specified accuracy (which should also be part of the algorithm). I'm hoping there will be a binary chop/search type thing going on, as that's pretty simple to grok.
So far I've found this, but the top answer is far from simple to understand on a conceptual level.
The more answers the merrier, so I can try to understand different ways of attacking the problem.
My language preference for the answer would be C#/C/C++/Java, or pseudocode for all I care.
Ok, let's implement pow(x, y) using only binary searches, addition and multiplication.
Driving y below 1
First, take this out of the way:
pow(x, y) == pow(x*x, y/2)
pow(x, y) == 1/pow(x, -y)
This is important to handle negative exponents and drive y below 1, where things start getting interesting. This reduces the problem to finding pow(x, y) where 0<y<1.
Implementing sqrt
In this answer I assume you know how to perform sqrt. I know sqrt(x) = x^(1/2), but it is easy to implement it just using a binary search to find y = sqrt(x) using y*y=x search function, e.g.:
#define EPS 1e-8
double sqrt2(double x) {
double a = 0, b = x>1 ? x : 1;
while(abs(a-b) > EPS) {
double y = (a+b)/2;
if (y*y > x) b = y; else a = y;
}
return a;
}
Finding the answer
The rationale is that every number below 1 can be approximated as a sum of fractions 1/2^x:
0.875 = 1/2 + 1/4 + 1/8
0.333333... = 1/4 + 1/16 + 1/64 + 1/256 + ...
If you find those fractions, you actually find that:
x^0.875 = x^(1/2+1/4+1/8) = x^(1/2) * x^(1/4) * x^(1/8)
That ultimately leads to
sqrt(x) * sqrt(sqrt(x)) * sqrt(sqrt(sqrt(x)))
So, implementation (in C++)
#define EPS 1e-8
double pow2(double x, double y){
if (x < 0 and abs(round(y)-y) < EPS) {
return pow2(-x, y) * ((int)round(y)%2==1 ? -1 : 1);
} else if (y < 0) {
return 1/pow2(x, -y);
} else if(y > 1) {
return pow2(x * x, y / 2);
} else {
double fraction = 1;
double result = 1;
while(y > EPS) {
if (y >= fraction) {
y -= fraction;
result *= x;
}
fraction /= 2;
x = sqrt2(x);
}
return result;
}
}
Deriving ideas from the other excellent posts, I came up with my own implementation. The answer is based on the idea that base^(exponent*accuracy) = answer^accuracy. Given that we know the base, exponent and accuracy variables beforehand, we can perform a search (binary chop or whatever) so that the equation can be balanced by finding answer. We want the exponent in both sides of the equation to be an integer (otherwise we're back to square one), so we can make accuracy any size we like, and then round it to the nearest integer afterwards.
I've given two ways of doing it. The first is very slow, and will often produce extremely high numbers which won't work with most languages. On the other hand, it doesn't use log, and is simpler conceptually.
public double powSimple(double a, double b)
{
int accuracy = 10;
bool negExponent = b < 0;
b = Math.Abs(b);
bool ansMoreThanA = (a>1 && b>1) || (a<1 && b<1); // Example 0.5^2=0.25 so answer is lower than A.
double accuracy2 = 1.0 + 1.0 / accuracy;
double total = a;
for (int i = 1; i < accuracy* b; i++) total = total*a;
double t = a;
while (true) {
double t2 = t;
for(int i = 1; i < accuracy; i++) t2 = t2 * t; // Not even a binary search. We just hunt forwards by a certain increment
if((ansMoreThanA && t2 > total) || (!ansMoreThanA && t2 < total)) break;
if (ansMoreThanA) t *= accuracy2; else t /= accuracy2;
}
if (negExponent) t = 1 / t;
return t;
}
This one below is a little more involved as it uses log(). But it is much quicker and doesn't suffer from the super-high number problems as above.
public double powSimple2(double a, double b)
{
int accuracy = 1000000;
bool negExponent= b<0;
b = Math.Abs(b);
double accuracy2 = 1.0 + 1.0 / accuracy;
bool ansMoreThanA = (a>1 && b>1) || (a<1 && b<1); // Example 0.5^2=0.25 so answer is lower than A.
double total = Math.Log(a) * accuracy * b;
double t = a;
while (true) {
double t2 = Math.Log(t) * accuracy;
if ((ansMoreThanA && t2 > total) || (!ansMoreThanA && t2 < total)) break;
if (ansMoreThanA) t *= accuracy2; else t /= accuracy2;
}
if (negExponent) t = 1 / t;
return t;
}
You can verify that 2^3.5 = 11.313708 very easily: check that 11.313708^2 = (2^3.5)^2 = 2^7 = 128
I think the easiest way to understand the computation you would actually do for this would be to refresh your understanding of logarithms - one starting point would be http://en.wikipedia.org/wiki/Logarithm#Exponentiation.
If you really want to compute non-integer powers with minimal technology one way to do that would be to express them as fractions with denominator a power of two and then take lots of square roots. E.g. x^3.75 = x^3 * x^(1/2) * x^(1/4) then x^(1/2) = sqrt(x), x^(1/4) = sqrt(sqrt(x)) and so on.
Here is another approach, based on the idea of verifying a guess. Given y, you want to find x such that x^(a/b) = y, where a and b are integers. This equation implies that x^a = y^b. You can calculate y^b, since you know both numbers. You know a, so you can - as you originally suspected - use binary chop or perhaps some numerically more efficient algorithm to solve x^a = y^b for x by simply guessing x, computing x^a for this guess, comparing it with y^b, and then iteratively improving the guess.
Example: suppose we wish to find 2^0.878 by this method. Then set a = 439, b = 500, so we wish to find 2^(439/500). If we set x=2^(439/500) we have x^500 = 2^439, so compute 2^439 and (by binary chop or otherwise) find x such that x^500 = 2^439.
Most of it comes down to being able to invert the power operation.
In other words, the basic idea is that (for example) N2 should be basically the "opposite" of N1/2 so that if you do something like:
M = N2
L = M1/2
Then the result you get in L should be the same as the original value in N (ignoring any rounding and such).
Mathematically, that means that N1/2 is the same as sqrt(N), N1/3 is the cube root of N, and so on.
The next step after that would be something like N3/2. This is pretty much the same idea: the denominator is a root, and the numerator is a power, so N3/2 is the square root of the cube of N (or the cube of the square root of N--works out the same).
With decimals, we're just expressing a fraction in a slightly different form, so something like N3.14 can be viewed as N314/100--the hundredth root of N raised to the power 314.
As far as how you compute these: there are quite a few different ways, depending heavily on the compromise you prefer between complexity (chip area, if you're implementing it in hardware) and speed. The obvious way is to use a logarithm: AB = Log-1(Log(A)*B).
For a more restricted set of inputs, such as just finding the square root of N, you can often do better than that extremely general method though. For example, the binary reducing method is quite fast--implemented in software, it's still about the same speed as Intel's FSQRT instruction.
As stated in the comments, its not clear if you want a mathematical description of how fractional powers work, or an algorithm to calculate fractional powers.
I will assume the latter.
For almost all functions (like y = 2^x) there is a means of approximating the function using a thing called the Taylor Series http://en.wikipedia.org/wiki/Taylor_series. This approximates any reasonably behaved function as a polynomial, and polynomials can be calculated using only multiplication, division, addition and subtraction (all of which the CPU can do directly). If you calculate the Taylor series for y = 2^x and plug in x = 3.5 you will get 11.313...
This almost certainly not how exponentiation is actually done on your computer. There are many algorithms which run faster for different inputs. For example, if you calculate 2^3.5 using the Taylor series, then you would have to look at many terms to calculate it with any accuracy. However, the Taylor series will converge much faster for x = 0.5 than for x = 3.5. So one obvious improvement is to calculate 2^3.5 as 2^3 * 2^0.5, as 2^3 is easy to calculate directly. Modern exponentiation algorithms will use many, many tricks to speed up processing - but the principle is still much the same, approximate the exponentiation function as some infinite sum, and calculate as many terms as you need to get the accuracy that is required.

Why does Math.Pow(-78.0921, -64.6294) return NaN?

I have two values: X = -78.0921 and Y = -64.6294. Now, when I want to compute Math.Pow(X, Y) it returns NaN. What should I do? How can I solve this problem?
How should I calculate this power? Is there any other function that can calculate this?...or maybe it is not defined mathematically ?
You've tried to compute a number that is not real.
By not real I mean, if we tried every single number between the largest number and the smallest number you can think of, none of those numbers is the solution to -78.0921 to the power of -64.6294.
In fact, no real number is the solution to -1 to the power of 0.5, or the square root of -1, and in general for a^b if a is negative and b is non-integer, the result is not real.
The inability to express such a useful result in real numbers lead to the invention of complex numbers. We say sqrt(-1) = i, the imaginary unit, in the complex number system - all complex numbers have a real component and an imaginary component, expressed as a + b*i.
In general, no negative number to a fractional power produces a real result, as it will have some component of i in it - the closer to a .5 the power is, the more i, the closer to a .0, the more real, and the path follows a circle between real and imaginary, e.g.
-1^x = cos(pi*x)+i*sin(pi*x)
Read more about complex numbers: http://en.wikipedia.org/wiki/Complex_number
If you wish to work with complex numbers in C#, try http://msdn.microsoft.com/en-us/library/system.numerics.complex.aspx
However, unless complex numbers have some meaning in your problem domain (they are meaningful in many electrical engineering, physics and signal analysis problems, for example) it's possible that your data is wrong or your logic is wrong to be attempting to do such a thing in the first place.
The documentation states that the returned value for those inputs is NaN.
x < 0 but not NegativeInfinity; y is not an integer, NegativeInfinity, or PositiveInfinity: returns NaN
The reason that NaN is returned is that the function is not well-defined for your input values. The Wikipedia article on Exponentiation covers this topic.
I think you mean that it returns NaN because your input matches the following:
x < 0 but not NegativeInfinity; y is not an integer, NegativeInfinity, or PositiveInfinity
Which is correct, as per the documentation.
Here is what I used for (native C# library System.Numerics):
Complex.Pow(x, y).Real;
The result is the same as:
double checkSquareRoot(double x, double y)
{
var result = Math.Pow(x, y);
if (x > 0)
{
return result;
}
else
{
return -1 * Math.Pow(-x, y);
}
}
Hope it helps!
I had a similar issue and handled as shown below, you have to adjust the min and max values as needed, in my case they are 0 and 10.
double alpha = FastMath.pow(weight, parameters.getAlpha());
if(alpha == Double.NEGATIVE_INFINITY) {
alpha = 0d;
}
if(alpha == Double.POSITIVE_INFINITY) {
alpha = 10d;
}
double beta = FastMath.pow(1d / distanceMatrix[row][column],
parameters.getBeta());
if(beta == Double.NEGATIVE_INFINITY) {
beta = 0d;
}
if(beta == Double.POSITIVE_INFINITY) {
beta = 10d;
}
It is another weak point in C#. We know that cubic root of -125 is equal to -5, but the result of Console.Write(Math.Pow(-125,1.0/3)); is NaN.
Perhaps you should try this:
if (x>0) {
Console.Write(Math.Pow(x,y));
}
else if (x<0) {
double x = Abs(x);
double z = Math.Pow(x,y);
if (y%2==0)
Console.Write(z);
else
Console.Write(-z);
}

Google Code Jam 2013 R1B - Falling Diamonds

Yesterdays Code Jam had a question titled Falling Diamonds. The full text can be found here, but in summary:
Diamonds fall down the Y axis.
If a diamond hits point to point with another diamond, there is a 50/50 chance it will slide to the right or left, provided it is not blocked from doing so.
If a diamond is blocked from sliding one direction, it will always slide the other way.
If a diamond is blocked in both directions, it will stop and rest on the blocking diamonds.
If a diamond hits the ground, it will bury itself half way, then stop.
The orientation of the diamond never changes, i.e. it will slide or sink, but not tumble.
The objective is to find the probability that a diamond will rest at a given coordinate, assuming N diamonds fall.
The above requirements basically boil down to the diamonds building successively larger pyramids, one layer at a time.
Suffice to say, I have not been able to solve this problem to google’s satisfaction. I get the sample from the problem description correct, but fail on the actual input files. Ideally I would like to see a matched input and correct output file that I can play with to try and find my error. Barring that, I would also welcome comments on my code.
In general, my approach is to find how many layers are needed to have one which contains the coordinate. Once I know which layer I am looking at, I can determine a number of values relevant to the layer and point we are trying to reach. Such as how many diamonds are in the pyramid when this layer is empty, how many diamonds can stack up on a side before the rest are forced the other way, how many have to slide in the same direction to reach the desired point, etc.
I then check to see if the number of diamonds dropping either makes it impossible to reach the point (probability 0), or guarantees we will cover the point (probability 1). The challenge is in the middle ground where it is possible but not guaranteed.
For the middle ground, I first check to see if we are dropping enough to potentially fill a side and force remaining drops to slide in the opposite direction. Reason being that in this condition we can guarantee that a certain number of diamonds will slide to each side, which reduces the number of drops we have to worry about, and resolves the problem of the probability changing when a side gets full. Example: if 12 diamonds drop it is guaranteed that each side of the outer layer will have 2 or more diamonds in it, whether a given side has 2, 3, or 4 depends on the outcome of just 2 drops, not of all 6 that fall in this layer.
Once I know how many drops are relevant to success, and the number that have to break the same way in order to cover the point, I sum the probabilities that the requisite number, or more, will go the same way.
As I said, I can solve the sample in the problem description, but I am not getting the correct output for the input files. Unfortunately I have not been able to find anything telling me what the correct output is so that I can compare it to what I am getting. Here is my code (I have spent a fair amount of time since the contest ended trying to tune this for success and adding comments to keep from getting myself lost):
protected string Solve(string Line)
{
string[] Inputs = Line.Split();
int N = int.Parse(Inputs[0]);
int X = int.Parse(Inputs[1]);
int Y = int.Parse(Inputs[2]);
int AbsX = X >= 0 ? X : -X;
int SlideCount = AbsX + Y; //number that have to stack up on one side of desired layer in order to force the remaining drops to slide the other way.
int LayerCount = (SlideCount << 1) | 1; //Layer is full when both sides have reached slidecount, and one more drops
int Layer = SlideCount >> 1; //Zero based Index of the layer is 1/2 the slide count
int TotalLayerEmpty = ((Layer * Layer) << 1) - Layer; //Total number of drops required to fill the layer below the desired layer
int LayerDrops = N - TotalLayerEmpty; //how many will drop in this layer
int MinForTarget; //Min number that have to be in the layer to hit the target location, i.e. all fall to correct side
int TargetCovered; //Min number that have to be in the layer to guarantee the target is covered
if (AbsX == 0)
{//if target X is 0 we need the layer to be full for coverage (top one would slide off until both sides were full)
MinForTarget = TargetCovered = LayerCount;
}
else
{
MinForTarget = Y + 1; //Need Y + 1 to hit an altitude of Y
TargetCovered = MinForTarget + SlideCount; //Min number that have to be in the layer to guarantee the target is covered
}
if (LayerDrops >= TargetCovered)
{//if we have enough dropping to guarantee the target is covered, probability is 1
return "1.0";
}
else if (LayerDrops < MinForTarget)
{//if we do not have enough dropping to reach the target under any scenario, probability is 0
return "0.0";
}
else
{//We have enough dropping that reaching the target is possible, but not guaranteed
int BalancedDrops = LayerDrops > SlideCount ? LayerDrops - SlideCount : 0; //guaranteed to have this many on each side
int CriticalDrops = LayerDrops - (BalancedDrops << 1);//the number of drops relevant to the probablity of success
int NumToSucceed = MinForTarget - BalancedDrops;//How many must break our way for success
double SuccessProb = 0;//Probability that the number of diamonds sliding the same way is between NumToSucceed and CriticalDrops
double ProbI;
for (int I = NumToSucceed; I <= CriticalDrops; I++)
{
ProbI = Math.Pow(0.5, I); //Probability that I diamonds will slide the same way
SuccessProb += ProbI;
}
return SuccessProb.ToString();
}
}
Your general approach seems to fit the problem, though the calculation of the last probability is not completely correct.
Let me describe how I solved this. We are looking at pyramids. These pyramids can be assigned a layer, based on how many diamonds the pyramid has. A pyramid of layer 1 has only 1 diamond. A pyramid of layer 2 has 1 + 2 + 3 diamonds. A pyramid of layer 3 has 1 + 2 + 3 + 4 + 5 diamonds. A pyramid of layer n has 1 + 2 + 3 + ... + 2*n-1 diamonds, which equals (2 * n - 1) * n.
Given this, we can calculate the layer of the biggest pyramid we are able to build with a given number of diamonds:
layer = floor( ( sqrt( 1 + 8 * diamonds ) + 1 ) / 4 )
and the number of diamonds which are not needed in order to build this pyramid. These diamonds will start to fill the next bigger pyramid:
overflow = diamonds - layer * ( 2 * layer - 1 )
We can now see the following things:
If the point is within the layer layer, it will be covered, so p = 1.0.
If the point is not within the layer layer + 1 (i.e. the next bigger pyramid), it will not be covered, so p = 0.0.
If the point is within the the layer layer + 1, is might be covered, so 0 <= p <= 1.
Since we only need to solve the last problem, we can simplify the problem statement a little bit: Given are the two sides of the triangle, r and l. Each side has a fixed capacity, the maximum number of diamonds it can take. What is the probability for one configuration (nr, nl), where nr denotes the diamonds on the right side, nl denotes the diamonds on the left side and nr + nl = overflow.
This probability can be calculated using Bernoulli's Trails:
P( nr ) = binomial_coefficient( overflow, k ) * pow( 0.5, overflow )
However, this will fail in one case: If one side is completely filled with diamonds, the probabilities change. The probability, that the diamond falls on the completely filled side is now 0, while the probability for the other side is 1.
Assume the following case: Each side can take up to 4 diamonds, while 6 diamonds are still left. The interesting case is now P( 2 ), because in this case, the left side will take 4 diamonds.
Some examples how the 6 diamonds could fall down. r stands for the decision go right, while l stands for go left:
l r l r l l => For every diamond, the probability for each side was 0.5. This case doesn't differ from the previous case. The probability for exactly this case is pow( 0.5, 6 ). There are 4 different cases like this (rllllr, lrlllr, llrllr, lllrlr). There are 10 different cases like this. The number of cases is the number of ways one element can be chosen from 5: binomial_coefficient( 5, 2 ) = 10
l r l l l r => The last diamond was going to fall on the right side, because the left side was full. The last probability was 1 for the right side and 0 for the left side. The probability for exactly this case is pow( 0.5, 5 ). There are 4 different cases like this: binomial_coefficient( 4, 1 ) = 4
l l l l r r => The last two diamonds were going to fall on the right side, because the left side was full. The last two probabilities were 1 for the right side and 0 for the left side. The probability for exactly this case is pow( 0.5, 4 ). There is exactly one case like this, because binomial_coefficient( 3, 0 ) = 1.
The general algorithm is to assume, that the last 0, 1, 2, 3, ..., nr elements will go to the right side inevitably, then to calculate the probability for each of these cases (the last 0, 1, 2, 3, ..., nr probabilites will be 1) and multiply each probability with the number of different cases where the last 0, 1, 2, 3, ..., nr probabilities are 1.
See the following code. p will be the probability for the case that nr diamonds will go on the right side and the left side is full:
p = 0.0
for i in range( nr + 1 ):
p += pow( 0.5, overflow - i ) * binomial_coefficient( overflow - i - 1, nr - i )
Now that we can calculate the probabilities for each individual combinations (nr, nl), one can simply add all cases where nr > k, with k being the minimal number of diamonds for one side for which the required point is still covered.
See the complete python code I used for this problem: https://github.com/frececroka/codejam-2013-falling-diamonds/blob/master/app.py
Your assumption are over simplicistic. You can download the correct answers of the large dataset caluclated with my solution:
http://pastebin.com/b6xVhp9U
You have to calc all the possible combinations of diamonds that will occupy your point of interests. To do that I have used this formula:
https://math.stackexchange.com/a/382123/32707
You basically have to:
Calc the height of the pyramid (ie calc the FIXED diamonds)
Calc the number of the diamonds that can freely move on the left or on the right
Calc the probability (with sums of binomial coeff)
With the latter and the Point Y you can apply that formula to calc the probability.
Also don't worry if you are not able solve this problem because it was pretty tough. If you want my solution in PHP here it is:
Note that you have to calc if the point is inside the fixed pyramid of is outside the fixed pyramid, also you have to do other minor checks.
<?php
set_time_limit(0);
$data = file('2bl.in',FILE_IGNORE_NEW_LINES);
$number = array_shift($data);
for( $i=0;$i<$number;$i++ ) {
$firstLine = array_shift($data);
$firstLine = explode(' ',$firstLine);
$s = $firstLine[0];
$x = $firstLine[1];
$y = $firstLine[2];
$s = calcCase( $s,$x,$y );
appendResult($i+1,$s);
}
function calcCase($s,$x,$y) {
echo "S: [$s] P($x,$y)\n<br>";
$realH = round(calcH($s),1);
echo "RealHeight [$realH] ";
$h = floor($realH);
if (isEven($h))
$h--;
$exactDiamonds = progression($h);
movableDiamonds($s,$h,$exactDiamonds,$movableDiamonds,$unfullyLevel);
$widthLevelPoint = $h-$y;
$spacesX = abs($x) - $widthLevelPoint;
$isFull = (int)isFull($s,$exactDiamonds);
echo "Diamonds: [$s], isFull [$isFull], Height: [$h], exactDiamonds [$exactDiamonds], movableDiamonds [$movableDiamonds], unfullyLevel [$unfullyLevel] <br>
widthlevel [$widthLevelPoint],
distance from pyramid (horizontal) [$spacesX]<br> ";
if ($spacesX>1)
return '0.0';
$pointHeight = $y+1;
if ($x==0 && $pointHeight > $h) {
return '0.0';
}
if ($movableDiamonds==0) {
echo 'Fixed pyramid';
if ( $y<=$h && abs($x) <= $widthLevelPoint )
return '1.0';
else
return '0.0';
}
if ( !$isFull ) {
echo "Pyramid Not Full ";
if ($spacesX>0)
return '0.0';
if ($unfullyLevel == $widthLevelPoint)
return '0.5';
else if ($unfullyLevel > $widthLevelPoint)
return '0.0';
else
return '1.0';
}
echo "Pyramid full";
if ($spacesX<=0)
return '1.0';
if ($movableDiamonds==0)
return '0.0';
if ( $movableDiamonds > ($h+1) ) {
$otherDiamonds = $movableDiamonds - ($h+1);
if ( $otherDiamonds - $pointHeight >= 0 ) {
return '1.0';
}
}
$totalWays = totalWays($movableDiamonds);
$goodWays = goodWays($pointHeight,$movableDiamonds,$totalWays);
echo "<br>GoodWays: [$goodWays], totalWays: [$totalWays]<br>";
return sprintf("%1.7f",$goodWays / $totalWays);
}
function goodWays($pointHeight,$movableDiamonds,$totalWays) {
echo "<br>Altezza punto [$pointHeight] ";
if ($pointHeight>$movableDiamonds)
return 0;
if ( $pointHeight == $movableDiamonds )
return 1;
$good = sumsOfBinomial( $movableDiamonds, $pointHeight );
return $good;
}
function totalWays($diamonds) {
return pow(2,$diamonds);
}
function sumsOfBinomial( $n, $k ) {
$sum = 1; //> Last element (n;n)
for($i=$k;$i<($n);$i++) {
$bc = binomial_coeff($n,$i);
//echo "<br>Binomial Coeff ($n;$i): [$bc] ";
$sum += $bc;
}
return $sum;
}
// calculate binomial coefficient
function binomial_coeff($n, $k) {
$j = $res = 1;
if($k < 0 || $k > $n)
return 0;
if(($n - $k) < $k)
$k = $n - $k;
while($j <= $k) {
$res = bcmul($res, $n--);
$res = bcdiv($res, $j++);
}
return $res;
}
function isEven($n) {
return !($n&1);
}
function isFull($s,$exact) {
return ($exact <= $s);
}
function movableDiamonds($s,$h,$exact,&$movableDiamonds,&$level) {
$baseWidth = $h;
$level=$baseWidth;
//> Full pyramid
if ( isFull($s,$exact) ) {
$movableDiamonds = ( $s-$exact );
return;
}
$movableDiamonds = $s;
while( $level ) {
//echo "<br> movable [$movableDiamonds] removing [$level] <br>" ;
if ($level > $movableDiamonds)
break;
$movableDiamonds = $movableDiamonds-$level;
$level--;
if ($movableDiamonds<=0)
break;
}
return $movableDiamonds;
}
function progression($n) {
return (1/2 * $n *(1+$n) );
}
function calcH($s) {
if ($s<=3)
return 1;
$sqrt = sqrt(1+(4*2*$s));
//echo "Sqrt: [$sqrt] ";
return ( $sqrt-1 ) / 2;
}
function appendResult($caseNumber,$string) {
static $first = true;
//> Cleaning file
if ($first) {
file_put_contents('result.out','');
$first=false;
}
$to = "Case #{$caseNumber}: {$string}";
file_put_contents( 'result.out' ,$to."\n",FILE_APPEND);
echo $to.'<br>';
}

Categories

Resources