2

I had a quick search but couldn't find anything that helped my problem.

I'm trying to make a program that takes the first 5 numbers and sources their product, and if that product is the largest found thus far it is set as such.

My code is:

string = str(integer)

x = 0
largest = 0
stringlength = len(string)

while x < stringlength:
    a = int(string[x])
    b = int(string[x+1])
    c = int(string[x+2])
    d = int(string[x+3])
    e = int(string[x+4])
    if (a*b*c*d*e > largest):
        largest = a*b*c*d*e
        print(largest)
    x += 1

print(largest)

I excluded the integer value itself, but for reference it is 1000 digits long. Whenever I try to run this code I get "IndexError: string index out of range". Can anyone help?

1
  • 1
    This is already a nicely-written question, but for future reference, it would have been even better if, instead of leaving the integer out, you had tested with a smaller integer (say, 12345678987654321), and verified that it had the same problem. Then you could post complete, running (up to the error you want help with) code, and describe the expected output, instead of explaining why you gave us incomplete code. Commented Jun 18, 2013 at 22:39

2 Answers 2

7
string = str(integer)

x = 0
largest = 0
stringlength = len(string)

while x < stringlength-4: # going to -5 would be out of rangue
    a = int(string[x])
    b = int(string[x+1])
    c = int(string[x+2])
    d = int(string[x+3])
    e = int(string[x+4])
    if (a*b*c*d*e > largest):
        largest = a*b*c*d*e
        print(largest)
    x += 1

print(largest)
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for that btoueg, I get where I was going wrong as well. Oh the woes of being new to something.
The comparison is strict, x < stringlength-4 means that, at most, x+4 will be equal to stringlength-1.
2

This is a classic off-by-one error (or, in this case, off-by-4 error).

When x reaches stringlength-4, x+4 is stringlength, which is past the end of string. So, you need x < stringlength-4, not x < stringlength.

But you might want to consider rewriting your code to use higher-level abstractions, to make these problems harder to run into and easier to think about.


First, instead of this:

x= 0
while x < stringlength:
    # ...
    x += 1

Just do this:

for x in range(stringlength):

You could then solve your problem with this:

for x in range(stringlength-4):

But let's take it farther.


If you slice the string, you won't get an IndexError:

for x in range(len(stringlength)):
    a, b, c, d, e = map(int, string[x:x+4])

However, now you'll get a ValueError in the unpacking. But really, you have no need to unpack into 5 separate variables here. Just keep the sequence and multiply it out. (You can do that with a loop, but in my opinion, this is one of the few cases reduce is the most readable way to write something in Python.)

for x in range(len(stringlength)):
    values = map(int, string[x:x+4])
    prod = reduce(operator.mul, values)
    if prod > largest:
        largest = prod
        print(largest)

Now there are no more errors—but that's because you're multiplying together the last 4, 3, 2, and 1 numbers. And that's exactly the problem: you never decided what should happen there.

So, now, you can make the decision explicit. Do you want to count them as batches, or skip them?

If you want to push even further forward, you can write sliding-window grouper functions using itertools, one version that acts like zip (stopping when the right edge of the window goes off the end of the list), one that acts like zip_longest (stopping only when the left edge of the window goes off):

def groupwise(iterable, n):
    groups = itertools.tee(iterable, n)
    for i, group in enumerate(groups):
        next(itertools.islice(group, i, i), None)
    return zip(*groups)

def groupwise_longest(iterable, n, fillvalue=None):
    groups = itertools.tee(iterable, n)
    for i, group in enumerate(groups):
        next(itertools.islice(group, i, i), None)
    return itertools.zip_longest(*groups, fillvalue=fillvalue)

Now, you can just do this:

for group_of_five in groupwise_longest(string, 5, 1):
    values = map(int, group)
    prod = reduce(operator.mul, values)
    if prod > largest:
        largest = prod
        print(largest)

Then, if you decide you'd rather not compare the incomplete groups at the end, just change the first line to:

for group_of_five in groupwise(string, 5):

Then you can move all the work outside the for loop:

groups = groupwise_longest(string, 5, 1)
intgroups = (map(int, group) for group in groups)
prods = (reduce(operator.mul, group) for group in groups)

And now that we have a sequence of products, it should be obvious that to find the highest one, that's just:

print(max(prods))

For example:

>>> string = '12345678987654321'
>>> groups = groupwise(string, 5)
>>> intgroups = (map(int, group) for group in groups)
>>> prods = (reduce(operator.mul, group) for group in groups)
>>> max(prods)
28224

And notice that there's nowhere you could make an off-by-one errors, or any other "small" error. Of course you could still get something completely wrong, or just have no idea how to write it, but at least your errors will be obvious big errors, which are easier to debug.

1 Comment

By the way, in a quick search through various projects on my computer, variations of groupwise (which can be seen as a generalization of pairwise from the itertools recipes, or of chunked from more_itertools) are known as windowed, slide, and n_wise

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.