How to convert 2 restricted decimal variables to a

2019-09-21 14:34发布

问题:

I have 2 convertor methods as below:

private const decimal MaxValidValue = 99.99m;
public decimal ConvertABToC(decimal a, decimal b)
{
    return a * b;
}

public void ConvertCtoAB(decimal c, ref decimal a, ref decimal b)
{
    if (c > MaxValidValue*MaxValidValue)
    {
        throw new ApplicationException();
    }

    if (c <= MaxValidValue)
    {
        a = 1.00m;
        b = c;
    }
    else 
    {
        // need to introduce some logic or assumptions here
    }
}

There are 3 important things to know:

1) The a and b variables are in the range of 0.00 to 99.99 therefore c can't have a value greater than 99.99*99.99

2) the a, b and c must not have more than 2 decimal precession e.g. a = 99.123 would be invalid.

3) you can use rounding if you'd need to as long as decimal.Round(a * b, 2) == c.

4) combinations like (1, 3), (3, 1), (2, 2), (1, 4), (0.5, 8) or even (0.25, 16) are all valid; it doesn't matter as long as c would be the product of a and b.

How would you complete the implementation of ConvertCtoAB?

Many thanks,

回答1:

Multiply C by 10,000. Then factor this number into its prime factors. Then find a partition of the prime factors into two sets such that the product of the numbers in each set is less than 10,000. If such a partition can be found, then return these two products divided by 100 as A and B. Otherwise, add one to the number and try again.

For example, if C=100.07, then the factors are 2, 2, 5, 5, 10007. Because one of the products must include the factor 10007, which is a prime number, the first condition can never be satisfied. So we try again with 1000701 = 3*3*3*13*2851. This time, we can partition the number, and we have A=3.51 and B=28.51 as a possible solution.

You can do this at most 99 times. If you need 100 or more, than the input value cannot have been generated from ConvertABToC.

This only guarantees that the result of ConvertCtoAB, when fed back into ConvertABtoC will produce the same C, not the other way around. It appears to violate rule #3, but then elsewhere the question is about rounding.

If no rounding at all is allowed, then one should stop and report infeasibility after trying the original 10000*C.



回答2:

I've deleted my previous answer, as I don't believe it was helpful any more, as the question's changed so much over time.

Here's what I understand the question to be:

You are given an input (c) of type decimal such that:

  • 0 <= c <= 99.99m * 99.99m
  • c has at most two decimal places (i.e. c == decimal.Round(c, 2))

You are required to find to decimal values a and b such that:

  • Each of a and b are in the range [0, 99.99m]
  • Each of a and b have at most two decimal places
  • decimal.Round(a * b, 2) == c

My answer is that it's still not possible for all values of c. Counterexample: c = 9997.50

The highest possible values of a and b (99.99m each) gives decimal.Round(a * b, 2) == 9998.00, so that fails with a product which is too high.

Now if you keep a as high as it can be, and reduce b as little as possible, we get a=99.99m, b=99.98m - and now decimal.Round(a * b, 2) == 9997.00, so that fails with a product which is too low.

There is no way of getting any product between those two values - we've perturbed our first attempt by as small an amount as possible. Therefore there are no values for a and b satisfying the problem.

(I'm anticipating a new rule being introduced to cope with this, as that seems to be the way this question is going...)



回答3:

Skeet's idea to treat the interval as itself * 100 makes everything so much clearer...

The problem is indeed without a complete solution. It asks you to create a bijective function f : A x B -> C,
where A = B = {0 ... 9999} and C = {0 ... 9999*9999 }

9999*9999 = 9998001; plus the 0, that gives a cardinality of 99,980,002.

A X B has a cardinality of 100,000,000.

A bijective function over finite sets can't be defined when the domain and codomain have different cardinalities. There will always be at most 19,998 values of c whose (a, b) breakdown will have more than one solution.

Going back on the original interval definition: the closest you can get to a proper function is something like:

public decimal Ab2C(decimal a, decimal b)
{
    if(a != 99.99 and a != 99.98)
        return a*100 + b;
    return (100-a)*100 + b; // for instance;
}

In this case, values for a between 0.02 to 99.97 will give unique results; a = 0.00 or 99.99 will be identical, likewise for a = 0.01 or 99.98. there is NO possible discrimination between these two values.

public void C2AB(decimal c, out decimal a, out decimal b)
{
    // todo: sanity checks.
    if (c <= 99.99)  // either a = 0.00, or a = 99.99; and b = c.
    {
        b = c;
        a = 0.00;
        return;
    }

    if (c <= 2*99.99)
    {
        b = c - 99.99;
        a = 0.01; // or 9.98.
        return;
    }
    a = c / 100;
    b = c % 100;
}

}