2

Given an array A of N integers . I need to find X such that the difference between the following 2 values (A[1] * A[2] * ... * A[X]) and (A[X+1] * A[X+2] * ... * A[N]) is minimum possible i.e. I need to minimize | (A[1] * A[2] * ... * A[X]) - (A[X+1] * A[X+2] * ... * A[N]) | and if there are multiple such values of X, print the smallest one.

Constraints:-

  • 1 <= N <= 10^5

  • 1 <= A[i] <= 10^18.

I am not able to find the approach to solve this problem in efficient way. What should be the best approach to solve this problem. Is there any special algorithm for multiplying large quantity of numbers.

xrisk
  • 3,790
  • 22
  • 45
krpra
  • 466
  • 1
  • 4
  • 19
  • Conceptually, divide the array into three parts: `left`, `middle`, `right`. Initially, `middle` is the whole array, `left` and `right` are empty. You'll also need a floating point value which represents the `ratio` of the left product to the right product. Initially, `ratio` is 1. If `ratio >= 1` move the last element of `middle` to `right`, and divide `ratio` by that element. Otherwise move the first element of `middle` to `left`, and multiply `ratio` by that element. Continue until `middle` has only one element. The answer is to move that last element either to `left` or to `right`. – user3386109 May 06 '19 at 22:57
  • @krpra What solution did you reach for this ? – Firex Firexo Jun 27 '19 at 05:22

2 Answers2

0

The idea is to use a form of prefix and suffix products.

Let:

  1. pre[i] = A[1] * A[2] * ... A[i] and
  2. suf[i] = A[i] * A[i + 1] * ... A[N]

You can compute these arrays in O(n) time, as:

  • pre[i] = A[i] * pre[i - 1] with pre[1] = A[i] and

  • suf[i] = A[i] * suf[i + 1] with suf[N] = A[n]

Then, iterate from i = 1 to N and compute the maximum of:

abs(pre[i] - suf[i + 1])

Observe that pre[i] - suf[i + 1] is the same as:

(A[1] * A[2] * ... * A[i]) - (A[i + 1] * A[i + 2] ... * A[N])

which is exactly what you want to compute.

xrisk
  • 3,790
  • 22
  • 45
  • Thanks @Rishav for your Response. Yes this can be done in `O(n)` time, but how can you store the product of 100,000 array elements which are in range 10^17 and 10^18.Is it possible to get the product of all those array elements?? – krpra May 06 '19 at 07:28
  • Yeah I didn't consider that. Let me think about it. – xrisk May 06 '19 at 07:34
  • @krpra you can use logarithms as the other answer suggests. Multiplication becomes addition of logarithms and you need to check the difference of logarithms that's closest to 0. – xrisk May 06 '19 at 07:38
  • 1
    @Rishav Are you sure logarithm values are long enough? This smells like a contest-like problem. And if that's correct, the test data will be specifically crafted to force using a full precision, for example at some point the difference will appear in a few units at range 10^17. Taking logarithms decreases _absolute values_ which one must handle, but does **not** decrease the _precision_ necessary. And OP did not specify the programming environment and available numerical (floating-point) types. – CiaPan May 06 '19 at 07:50
  • @CiaPan Yes this is a contest-like problem. How can I solve this problem? Is there any other algorithm without considering logarithms? – krpra May 06 '19 at 08:37
  • @krpra How about implementing multiplication and subtraction routines for your own integer type I proposed in [this comment](https://stackoverflow.com/questions/55999814/partition-the-array-with-minimal-difference/56000174?noredirect=1#comment98647918_56000174) to the answer by Nyavro? – CiaPan May 06 '19 at 10:42
  • As a way to avoid storing 100000 of these large products, you could instead store 100000 smaller "partial products", and recompute each product using about log2(100000) multiplications of these. You only need to do this for one "direction"; let's do it for the prefixes (i.e., we will just keep a single leftwards-moving "running product" for the suffixes). The idea is to form the product of the first n/2 elements and store that, then split the array in half, and recurse on each half. You will still wind up with n products, but most will be small. – j_random_hacker May 07 '19 at 09:57
0

You can do it in O(n): first go - get the product of all elements of array (P) and the second go - assuming at start the left part is one and the second is P, on each step i multiply left on X[i] and divide right on X[i]. Continue the process until left is less than right.

Since you have large array of numbers, you need some big-number multiplication. So, maybe you better move to array of logarithms of A[i], LA[i] and move to new criteria.

Edit:

As mentioned by @CiaPan, the precision of standard 64-bit decimal is not enough for making log operation here (since values may be up to 10^18).

So to solve this problem you should first split values of the source array to pairs such that:

s[2*i]   = a[i].toDouble / (10.0^9)
s[2*i+1] = a[i]/s[2*i]  

Array s is twice longer than source array a, but its values do not exceed 10^9, so it is safe to apply log operation, then find desired sX for array s and divide it to 2 to get X for array a.

Extra-precision logarithm logic is not required.

Nyavro
  • 8,806
  • 2
  • 26
  • 33
  • Can you elaborate your approach more clearly. If I am not not wrong, product of whole array is not possible. Suppose if there are 10^5 elements in array and all array elements are in range 10^17 to 10^18, them how can we get the the product of all array elements. – krpra May 06 '19 at 07:22
  • @krpra By using some `BigInteger` library, maybe...? – CiaPan May 06 '19 at 07:33
  • @krpra You can also implement your own arithmetics for integers bigger than standard integral types, but not arbitrarily big - for example, on max. 14 bytes. The main algorithm is straightforward then, and all the difficulty (and efficiency issues) gets shifted to multiplication and division routines. – CiaPan May 06 '19 at 07:40
  • 1
    You don't have to hold such a large numbers, you can move to logarithms as I mentioned. So, you first create array la[i] = log(a[i]). The criteria moves to |sum[0-k] (la[i]) - sum[(k+1)-n] (la[i])| -> min (k). – Nyavro May 06 '19 at 08:10
  • 2
    @Nyavro Are you sure it will help? How many bits of integer representation are needed for distinguishing 10^(30) from 10^(30)+1? And how many bits of floating-point representation are needed for distinguishing log(10^(30)) from log(10^(30)+1)? How many bits are actually available in both cases? (See also [this comment](https://stackoverflow.com/questions/55999814/partition-the-array-with-minimal-difference/56000119?noredirect=1#comment98648181_56000119).) – CiaPan May 06 '19 at 09:42
  • @CiaPan, you're right, the array's values boundaries are too wide for standard precision log. Here some custom log operation should be implemented such that it can provide more precision. Anyway, for this problem the only option is to work with logarithms. My opinion. – Nyavro May 06 '19 at 10:21