views:

772

answers:

8

Hey guys,

Consider a list of numbers L=. We can only have the series between +=2^j, while 0<=j<=32.We have to implement an algorithm which implements the largest product.

+3  A: 

EDIT: I adjusted the algorithm outline to match the actual pseudo code and put the complexity analysis directly into the answer:

Outline of algorithm

Go seqentially over the sequence and store value and first/last index of the product (positive) since the last 0. Do the same for another product (negative) which only consists of the numbers since the first sign change of the sequence. If you hit a negative sequence element swap the two products (positive and negative) along with the associagted starting indices. Whenever the positive product hits a new maximum store it and the associated start and end indices. After going over the whole sequence the result is stored in the maximum variables.

To avoid overflow calculate in binary logarithms and an additional sign.

Pseudo code

maxProduct = 0
maxProductStartIndex = -1
maxProductEndIndex = -1
sequence.push_front( 0 ) // reuses variable intitialization of the case n == 0

for every index of sequence
   n = sequence[index]
   if n == 0
       posProduct = 0
       negProduct = 0
       posProductStartIndex = index+1
       negProductStartIndex = -1
   else
       if n < 0
           swap( posProduct, negProduct )
           swap( posProductStartIndex, negProductStartIndex )
           if -1 == posProductStartIndex // start second sequence on sign change
               posProductStartIndex = index
           end if
           n = -n;
       end if
       logN = log2(n) // as indicated all arithmetic is done on the logarithms
       posProduct += logN
       if -1 < negProductStartIndex // start the second product as soon as the sign changes first
          negProduct += logN
       end if
       if maxProduct < posProduct // update current best solution
          maxProduct = posProduct
          maxProductStartIndex = posProductStartIndex
          maxProductEndIndex = index
       end if
   end if
end for

// output solution

print "The maximum product is " 2^maxProduct "."
print "It is reached by multiplying the numbers from sequence index " 
print maxProductStartIndex " to sequence index " maxProductEndIndex

Complexity

The algorithm uses a single loop over the sequence so its O(n) times the complexity of the loop body. The most complicated operation of the body is log2. Ergo its O(n) times the complexity of log2. The log2 of a number of bounded size is O(1) so the resulting complexity is O(n) aka linear.

Peter G.
The actual arithmetic should be done with the logarithm approach described by egbokul, but as for finding a maximal sublist, then looking between successive 0s is absolutely the way to do it. The point is that no sublist which contains a 0 can have a product other than 0. Also, no sub-sublist of a sublist which does not contain a 0 can have a greater absolute value of its product, since you can't make the total smaller by multiplying by nonzero integers. You need a bit of tweaking to deal with negative products, but probably not a lot.
Tom Anderson
Hmm, do `maxProductStartIndex` and `maxProductEndIndex` ever do anything? I don't see their values being used.
Carl Smotricz
@Free Styler. The algorithm uses a single loop over the sequence so its O(n) times the complexity of the loop body. The most complicated operation of the body is log2. Ergo its O(n) times the complexity of log2. log2 of a machine size number often is O(1) so the resulting complexity would be O(n) aka linear in this case.@Carl The variables are part of the result, the algorithm itself only writes to them. I replaced the end comment about the result with explicit output of the result.
Peter G.
Ah, I get it. They let you print out the start and end positions. I hadn't scrolled down there :)
Carl Smotricz
@Free Styler Great you tried it. Upon executing on paper I found a bug in detecting the first sign change in case of +-1. I fixed that in the code now.
Peter G.
+3  A: 

Since k <= 30, any integer i = 2k will fit into a Java int. However the product of such two integers might not necessarily fit into a Java int since 2k * 2k = 22*k <= 260 which fill into a Java long. This should answer Your question regarding the "(multiplication of) two numbers...".

In case that You might want to multiply more than two numbers, which is implied by Your assignment saying "...largest product of a CONTINUOUS SUBLIST..." (a sublist's length could be > 2), have a look at Java's BigInteger class.

Dave
Well that depends on the algorithm You're using to solve the problem defined in Your assignment. How about posting Your first approach? - I'm sure the SO community will willingly help You to arrive at a correct algorithm if You don't have one yet.
Dave
@Free Styler I've addressed this in a separate answer.
Dave
+2  A: 

Hmmm.. since they're all powers of 2, you can just add the exponent instead of multiplying the numbers (equivalent to taking the logarithm of the product). For example, 2^3 * 2^7 is 2^(7+3)=2^10. I'll leave handling the sign as an exercise to the reader.

Regarding the sublist problem, there are less than n^2 pairs of (begin,end) indices. You can check them all, or try a dynamic programming solution.

Amnon
+1 very nice tips for someone wanting to handle the specific multiplication described in the assignment efficiently or approaching the design of an algorithm that solves it or both.
Dave
+3  A: 

Actually, the most efficient way of multiplication is doing addition instead. In this special case all you have is numbers that are powers of two, and you can get the product of a sublist by simply adding the expontents together (and counting the negative numbers in your product, and making it a negative number in case of odd negatives).

Of course, to store the result you may need the BigInteger, if you run out of bits. Or depending on how the output should look like, just say (+/-)2^N, where N is the sum of the exponents.

Parsing the input could be a matter of switch-case, since you only have 30 numbers to take care of. Plus the negatives.

That's the boring part. The interesting part is how you get the sublist that produces the largest number. You can take the dumb approach, by checking every single variation, but that would be an O(N^2) algorithm in the worst case (IIRC). Which is really not very good for longer inputs.

What can you do? I'd probably start from the largest non-negative number in the list as a sublist, and grow the sublist to get as many non-negative numbers in each direction as I can. Then, having all the positives in reach, proceed with pairs of negatives on both sides, eg. only grow if you can grow on both sides of the list. If you cannot grow in both directions, try one direction with two (four, six, etc. so even) consecutive negative numbers. If you cannot grow even in this way, stop.

Well, I don't know if this alogrithm even works, but if it (or something similar) does, its an O(N) algorithm, which means great performance. Lets try it out! :-)

Gabor Kulcsar
Well no, not so easy, what if you can either grow left and right, or right two times? You need to grow towards the larger result. What if a local maximum is not a global one? Is it possible?Its a really nice homework, you can take your time and think about it :-)
Gabor Kulcsar
+1 for explaining the multiplication of multiple integers of the form 2^k in more detail.
Dave
@egbokul Although I'm not sure, I intuitively think that the assignment allows to make use of the principle of optimality.
Dave
Here is an example list where the local maximum is not part of the solution: 4,4,4,-1,8. 8 is the largest number, but the result is 4*4*4. I'm guessing O(N) was too optimistic, perhaps O(N*logN) is a more realistic goal.
Gabor Kulcsar
+5  A: 

In my first answer I addressed the OP's problem in "multiplying two big big numbers". As it turns out, this wish is only a small part of a much bigger problem which I'm going to address now:

"I still haven't arrived at the final skeleton of my algorithm I wonder if you could help me with this."

(See the question for the problem description)

All I'm going to do is explain the approach Amnon proposed in little more detail, so all the credit should go to him.

You have to find the largest product of a continuous sublist from a list of integers which are powers of 2. The idea is to:

  1. Compute the product of every continuous sublist.
  2. Return the biggest of all these products.

You can represent a sublist by its start and end index. For start=0 there are n-1 possible values for end, namely 0..n-1. This generates all sublists that start at index 0. In the next iteration, You increment start by 1 and repeat the process (this time, there are n-2 possible values for end). This way You generate all possible sublists.

Now, for each of these sublists, You have to compute the product of its elements - that is come up with a method computeProduct(List wholeList, int startIndex, int endIndex). You can either use the built in BigInteger class (which should be able to handle the input provided by Your assignment) to save You from further trouble or try to implement a more efficient way of multiplication as described by others. (I would start with the simpler approach since it's easier to see if Your algorithm works correctly and first then try to optimize it.)

Now that You're able to iterate over all sublists and compute the product of their elements, determining the sublist with the maximum product should be the easiest part.

If it's still to hard for You to make the connections between two steps, let us know - but please also provide us with a draft of Your code as You work on the problem so that we don't end up incrementally constructing the solution and You copy&pasting it.

edit: Algorithm skeleton

public BigInteger listingSublist(BigInteger[] biArray)
{       
    int start = 0;
    int end = biArray.length-1;
    BigInteger maximum;

    for (int i = start; i <= end; i++)
    {
        for (int j = i; j <= end; j++)
        {
            //insert logic to determine the maximum product.
            computeProduct(biArray, i, j);
        }
    }

    return maximum;                
}

public BigInteger computeProduct(BigInteger[] wholeList, int startIndex, 
                                                         int endIndex)
{
    //insert logic here to return
    //wholeList[startIndex].multiply(wholeList[startIndex+1]).mul...(
    //    wholeList[endIndex]);       
}
Dave
+1 for being tolerant and patient
Amnon
@Amnon I appreciate the fact that other experienced people shared their knowledge with me and helped me when I was a beginner. It's nice to be able to pay some of that back.
Dave
That's why I provided a link to its documentation. It isn't anything harder to apply the "multiply" method than the "*" operator. If You manage to solve it using "*", the solution using multiply isn't far away
Dave
A: 

useless code. I will put up the final implementation after fixing some bugs.

Free Styler
2 issues: 1. in order to iterate over all sublists you'll need 2 for loops. the outer for loop should increment start from 0 to n-1 and the inner one should increment end from start to n-1. 2. Try to make computeProduct work without accessing global variables, it shouldn't really care about max and current, all it should do is solely compute the product of all elements inside the sublist (You're going to need a loop for that). Save the max/current headache for the caller of computeProduct. (Please make this answer community wiki)
Dave
very nice progress!!!. however computeProducts isnt 100% right yet:should be long current=1; and then inside the loop current *= wholeList[j] - think about it. That's it, only maximum determination misses from now on.
Dave
+1  A: 

I'd like to combine Amnon's observation about multiplying powers of 2 with one of mine concerning sublists.

Lists are terminated hard by 0's. We can break the problem down into finding the biggest product in each sub-list, and then the maximum of that. (Others have mentioned this).

This is my 3rd revision of this writeup. But 3's the charm...

Approach

Given a list of non-0 numbers, (this is what took a lot of thinking) there are 3 sub-cases:

  1. The list contains an even number of negative numbers (possibly 0). This is the trivial case, the optimum result is the product of all numbers, guaranteed to be positive.
  2. The list contains an odd number of negative numbers, so the product of all numbers would be negative. To change the sign, it becomes necessary to sacrifice a subsequence containing a negative number. Two sub-cases:

    a. sacrifice numbers from the left up to and including the leftmost negative; or

    b. sacrifice numbers from the right up to and including the rightmost negative.

    In either case, return the product of the remaining numbers. Having sacrificed exactly one negative number, the result is certain to be positive. Pick the winner of (a) and (b).

Implementation

The input needs to be split into subsequences delimited by 0. The list can be processed in place if a driver method is built to loop through it and pick out the beginnings and ends of non-0 sequences.

Doing the math in longs would only double the possible range. Converting to log2 makes arithmetic with large products easier. It prevents program failure on large sequences of large numbers. It would alternatively be possible to do all math in Bignums, but that would probably perform poorly.

Finally, the end result, still a log2 number, needs to be converted into printable form. Bignum comes in handy there. There's new BigInteger("2").pow(log); which will raise 2 to the power of log.

Complexity

This algorithm works sequentially through the sub-lists, only processing each one once. Within each sub-list, there's the annoying work of converting the input to log2 and the result back, but the effort is linear in the size of the list. In the worst case, the sum of much of the list is computed twice, but that's also linear complexity.

Carl Smotricz
Took much longer than 3 minutes. I would have done it in Java directly, but that would have been cheating :)
Carl Smotricz
A: 

See this code. Here I implement exact factorial of a huge large number. I am just using integer array to make big numbers. Download the code from Planet Source Code.

chanchal1987