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?
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)
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.
Related
import random
def mainlist(list, size, min, max):
for i in range(size):
list.append(random.randint(min, max))
print(list)
def counterlist(list):
for i in list:
if i<0:
x=sum(list[(list.index(i)+1):])
print('Reqemlerin cemi:', x)
break
list = []
mainlist(list, 10, -10, 30)
counterlist(list)
I need to calculate sum of numbers after 1st negative number in this random list, did it in second function but want to know is there way not using the sum() function?
Explicitly using an iterator makes it nicer and more efficient:
def counterlist(lst):
it = iter(lst)
for i in it:
if i < 0:
print('Reqemlerin cemi:', sum(it))
No idea why you wouldn't want to use the sum function, that's absolutely the right and best way to do it.
Try this:
import random
lst = [random.randint(-10, 30) for _ in range(10)]
print(sum(lst[next(i for i, n in enumerate(lst) if n < 0) + 1:]))
First you generate the list lst. Then, you iterate over your list and you find the first negative element with next(i for i, n in enumerate(lst) if n < 0). Finally, you compute the sum of the portion of the list you're interested about.
If you really don't want to use sum but keep things concise (and you're using python >= 3.8):
import random
lst = [random.randint(-10, 30) for _ in range(10)]
s = 0
print([s := s + x for x in lst[next(i for i, n in enumerate(lst) if n < 0) + 1:]][-1])
Assuming there's a negative value in the list, and with a test list "a":
a = [1,2,3,-7,2,3,4,-1,23,3]
sum(a[(a.index([i for i in a if i < 0][0]) + 1):])
Evaluates to 34 as expected. Could also add a try/except IndexError with a simple sum to catch if there's no negative value.
Edit: updated the index for the search.
Yes, you can iterate over the elements of the list and keep adding them to some var which would store your result. But what for? sum approach is much more clear and python-ish.
Also, don't use list as a list name, it's a reserved word.
# After you find a first negative number (at i position)
j = i + 1
elements_sum = 0
while j < len(list):
elements_sum += list[j]
j += 1
Not as good as the marked answer, but just to know how to make use of numpy, being sure there is a negative number in the list.
Sample list: lst = [12, 2, -3, 4, 5, 10, 100]
You can get your result using np.cumsum:
import numpy as np
np_lst = np.array(lst)
cum_sum = np.cumsum(np_lst)
res = cum_sum[-1] - cum_sum[np_lst<0][0]
res #=> 119
First of all don't use list as a variable name, it's a reserved keyword. Secondly, make your loop as follows:
for index, x in enumerate(list_):
if x < 0:
sum_ = sum(list_[(index + 1):])
print('Reqemlerin cemi:', sum_)
break
That way, you don't need to find a value.
At last if you don't want to use sum
found_negative = false
sum_ = 0
for x in list_:
if found_negative:
sum_ += x
elif x < 0:
found_negative = true
print('Reqemlerin cemi:', sum_)
New to coding and am trying to solve this coding problem to learn.
Prompt:
If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. Find the sum of all the multiples of 3 or 5 below 1000.
three = []
five = []
def threeList():
n = 1
while (n*3<1000):
result = n*3
three.append(result)
n += 1
return three
def fiveList():
n = 1
while (n*5<1000):
result = n*5
five.append(result)
n += 1
return five
threeList()
fiveList()
print(three,five)
This results in printing [3] [5] to the console.
Your return is part of the loop which means that at the end of iteration, instead of doing another iteration you just return from the function. Move it out of a loop, i.e.:
def threeList():
n = 1
while (n*3<1000):
result = n*3
three.append(result)
n += 1
return three
Also this return makes little sense, because you are returning global variables. No point to return something that is already available (I suggest you read about variable scope), so it's safe to get rid of these returns completely:
def threeList():
n = 1
while (n*3<1000):
result = n*3
three.append(result)
n += 1
In fact, as both your functions differ very little, you should refactor your code and have just one function accepting the multiplier (as this is the only difference) and return populated list. This time we go with local variable to create the result list, so this time you need to return it otherwise result list will not be available outside the function:
def my_func(multiplier):
result = []
n = 1
while (n*multiplier < 1000):
result.append(n*multiplier)
n += 1
return result
and then replace
threeList()
fiveList()
with
three = my_func(3)
five = my_func(5)
In fact, you could merge this with print() as there's no other use for three and five, so your final code would then look like this:
def my_func(multiplier):
result = []
n = 1
while (n*multiplier < 1000):
result.append(n*multiplier)
n += 1
return result
print(my_func(3), my_func(5))
In addition to Marcin's fantastic answer, note that you can also do the math for which elements to use ahead of time and avoid the while loop entirely. range is your friend here.
multiples_of_five = range(5, 1001, step=5)
multiples_of_three = range(3, 1001, 3)
Since range's stop is exclusive, but we want all the multiples of three and five up to 1000 inclusive, we have to stop at 1001 instead. This simplifies the my_func that Marcin lays out above.
def list_multiples(n):
result = []
for i in range(n, 1001, n):
result.append(i)
return result
Though if we examine this more closely, you'll see we're basically just casting to list and returning. Let's do that directly.
def list_multiples(n):
return list(range(n, 1001, n))
From there we can find the multiples of five and multiples of three
fives = list_multiples(5)
threes = list_multiples(3)
Cast to set to remove duplicates (15 is a multiple of both 5 and 3, but shouldn't be summed twice)
all_nums = set(fives + threes)
And sum the result
result = sum(all_nums)
To solve your problem in Pythonic way, use sum() with a generator expression like:
Code:
sum(i for i in range(1000) if i % 5 == 0 or i % 3 == 0)
Test Code:
max_num = 1000
print(sum(i for i in range(max_num) if i % 5 == 0 or i % 3 == 0))
Results:
233168
Hello guys here is the problem. I have something like this in input [[1,2,3],[4,5,6],[7,8,9]]...etc
And i want to generate all possible combination of product of those list and then multiply each elements of the resulting combination beetween them to finally filter the result in a interval.
So first input a n list [[1,2,3],[4,5,6],[7,8,9],[10,11,12]]...etc
Which will then give (1,4,7,10)
(1,4,7,11)
(1,4,7,12)
and so on
Then combination of those result for k in n like (1,4,7)(1,4,10)(1,7,10) for the first row
The multiplication of x as 1*4*7 = 28, 1*4*10 = 40, 1*7*10 = 70
And from this get only the unique combination and the result need in the interval choosed beforehand : if x > 50 and x < 100 i will get (1,7,10) : 70
I did try
def mult(lst): #A function mult i'm using later
r = 1
for element in lst:
r *= element
return round(r)
s = [] #Where i add my list of list
for i in range(int(input1)):
b = input("This is line %s : " % (i+1)).split()
for i in range(len(b)):
b[i] = float(b[i])
s.append(b)
low_result = input("Expected low_result : ")
high_result = input("Expected high_result : ")
combine = []
my_list = []
for element in itertools.product(*s):
l= [float(x) for x in element]
comb = itertools.combinations([*l], int(input2))
for i in list(comb):
combine.append(i)
res = mult(i)
if res >= int(low_result) and res <= int(high_result):
my_list.append(res)
f = open("list_result.txt","a+")
f.write("%s : result is %s\n" % (i, res))
f.close()
And it always result in memory error cause there is too many variation with what i'm seeking.
What i would like is a way to generate from a list of list of 20 elements or more all the product and resulting combination of k in n for the result(interval) that i need.
As suggested above, I think this can be done without exploding your memory by never holding an array in memory at any time. But the main issue is then runtime.
The maths
As written we are:
Producing every combination of m rows of n items n ** m
Then taking a choice of c items from those m values C(m, c)
This is very large. If we have m=25 rows, of n=3 items each and pick c=3 items in them we get:
= n ** m * C(m, c)
= 3 ** 25 * 2300 - n Choose r calculator
= 1.948763802×10¹⁵
If instead we:
Choose c rows from the m rows: C(m, c) as before
Then pick every combination of n items from these c rows: n ** c
With m=25 rows, of n=3 items each and pick c=3 items in them we get:
= n ** c * C(m, c)
= 3 ** 3 * 2300
= 20700
This is now a solvable problem.
The code
from itertools import product, combinations
def mult(values, min_value, max_value):
"""
Multiply together the values, but return None if we get too big or too
small
"""
output = 1
for value in values:
output *= value
# Early return if we go too big
if output > max_value:
return None
# Early return if we goto zero (from which we never return)
if output == 0 and min_value != 0:
return None
if output < min_value:
return None
return output
def yield_valid_combos(values, choose, min_value, max_value):
# No doubt an even fancier list compression would get this too
for rows in combinations(values, choose):
for combos in product(*rows):
value = mult(combos, min_value, max_value)
if value is not None:
yield combos, value
values = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
with open('list_result.txt', 'w') as fh:
for selection, value in yield_valid_combos(
values, choose=3, min_value=50, max_value=100):
fh.write('{}: result is {}\n'.format(selection, value))
This solution also returns no duplicate answers (unless the same value appears in multiple rows).
As an optimisation the multiplication method attempts to return early if we detect the result will be too big or small. We also only open the file once and then keep adding rows to it as they come.
Further optimisation
You can also optimise your set of values ahead of time by screening out values which cannot contribute to a solution. But for smaller values of c, you may find this is not even necessary.
The smallest possible combination of values is c items from the set of the smallest values in each row. If we take the c - 1 smallest items from the set of smallest values, mutliply them together and then divide the maximum by this number, it gives us an upper bound for the largest value which can be in a solution. We can then then screen out all values above this value (cutting down on permutations)
What I'm looking to do:
I need to make a function that, given a list of positive integers (there can be duplicate integers), counts all triples (in the list) in which the third number is a multiple of the second and the second is a multiple of the first:
(The same number cannot be used twice in one triple, but can be used by all other triples)
For example, [3, 6, 18] is one because 18 goes evenly into 6 which goes evenly into 3.
So given [1, 2, 3, 4, 5, 6] it should find:
[1, 2, 4] [1, 2, 6] [1, 3, 6]
and return 3 (the number of triples it found)
What I've tried:
I made a couple of functions that work but are not efficient enough. Is there some math concept I don't know about that would help me find these triples faster? A module with a function that does better? I don't know what to search for...
def foo(q):
l = sorted(q)
ln = range(len(l))
for x in ln:
if len(l[x:]) > 1:
for y in ln[x + 1:]:
if (len(l[y:]) > 0) and (l[y] % l[x] == 0):
for z in ln[y + 1:]:
if l[z] % l[y] == 0:
ans += 1
return ans
This one is a bit faster:
def bar(q):
l = sorted(q)
ans = 0
for x2, x in enumerate(l):
pool = l[x2 + 1:]
if len(pool) > 1:
for y2, y in enumerate(pool):
pool2 = pool[y2 + 1:]
if pool2 and (y % x == 0):
for z in pool2:
if z % y == 0:
ans += 1
return ans
Here's what I've come up with with help from y'all but I must be doing something wrong because it get's the wrong answer (it's really fast though):
def function4(numbers):
ans = 0
num_dict = {}
index = 0
for x in numbers:
index += 1
num_dict[x] = [y for y in numbers[index:] if y % x == 0]
for x in numbers:
for y in num_dict[x]:
for z in num_dict[y]:
print(x, y, z)
ans += 1
return ans
(39889 instead of 40888) - oh, I accidentally made the index var start at 1 instead of 0. It works now.
Final Edit
I've found the best way to find the number of triples by reevaluating what I needed it to do. This method doesn't actually find the triples, it just counts them.
def foo(l):
llen = len(l)
total = 0
cache = {}
for i in range(llen):
cache[i] = 0
for x in range(llen):
for y in range(x + 1, llen):
if l[y] % l[x] == 0:
cache[y] += 1
total += cache[x]
return total
And here's a version of the function that explains the thought process as it goes (not good for huge lists though because of spam prints):
def bar(l):
list_length = len(l)
total_triples = 0
cache = {}
for i in range(list_length):
cache[i] = 0
for x in range(list_length):
print("\n\nfor index[{}]: {}".format(x, l[x]))
for y in range(x + 1, list_length):
print("\n\ttry index[{}]: {}".format(y, l[y]))
if l[y] % l[x] == 0:
print("\n\t\t{} can be evenly diveded by {}".format(l[y], l[x]))
cache[y] += 1
total_triples += cache[x]
print("\t\tcache[{0}] is now {1}".format(y, cache[y]))
print("\t\tcount is now {}".format(total_triples))
print("\t\t(+{} from cache[{}])".format(cache[x], x))
else:
print("\n\t\tfalse")
print("\ntotal number of triples:", total_triples)
Right now your algorithm has O(N^3) running time, meaning that every time you double the length of the initial list the running time goes up by 8 times.
In the worst case, you cannot improve this. For example, if your numbers are all successive powers of 2, meaning that every number divides every number grater than it, then every triple of numbers is a valid solution so just to print out all the solutions is going to be just as slow as what you are doing now.
If you have a lower "density" of numbers that divide other numbers, one thing you can do to speed things up is to search for pairs of numbers instead of triples. This will take time that is only O(N^2), meaning the running time goes up by 4 times when you double the length of the input list. Once you have a list of pairs of numbers you can use it to build a list of triples.
# For simplicity, I assume that a number can't occur more than once in the list.
# You will need to tweak this algorithm to be able to deal with duplicates.
# this dictionary will map each number `n` to the list of other numbers
# that appear on the list that are multiples of `n`.
multiples = {}
for n in numbers:
multiples[n] = []
# Going through each combination takes time O(N^2)
for x in numbers:
for y in numbers:
if x != y and y % x == 0:
multiples[x].append(y)
# The speed on this last step will depend on how many numbers
# are multiples of other numbers. In the worst case this will
# be just as slow as your current algoritm. In the fastest case
# (when no numbers divide other numbers) then it will be just a
# O(N) scan for the outermost loop.
for x in numbers:
for y in multiples[x]:
for z in multiples[y]:
print(x,y,z)
There might be even faster algorithms, that also take advantage of algebraic properties of division but in your case I think a O(N^2) is probably going to be fast enough.
the key insight is:
if a divides b, it means a "fits into b".
if a doesn't divide c, then it means "a doesn't fit into c".
And if a can't fit into c, then b cannot fit into c (imagine if b fitted into c, since a fits into b, then a would fit into all the b's that fit into c and so a would have to fit into c too.. (think of prime factorisation etc))
this means that we can optimise. If we sort the numbers smallest to largest and start with the smaller numbers first. First iteration, start with the smallest number as a
If we partition the numbers into two groups, group 1, the numbers which a divides, and group 2 the group which a doesn't divide, then we know that no numbers in group 1 can divide numbers in group 2 because no numbers in group 2 have a as a factor.
so if we had [2,3,4,5,6,7], we would start with 2 and get:
[2,4,6] and [3,5,7]
we can repeat the process on each group, splitting into smaller groups. This suggests an algorithm that could count the triples more efficiently. The groups will get really small really quickly, which means its efficiency should be fairly close to the size of the output.
This is the best answer that I was able to come up with so far. It's fast, but not quite fast enough. I'm still posting it because I'm probably going to abandon this question and don't want to leave out any progress I've made.
def answer(l):
num_dict = {}
ans_set = set()
for a2, a in enumerate(l):
num_dict[(a, a2)] = []
for x2, x in enumerate(l):
for y2, y in enumerate(l):
if (y, y2) != (x, x2) and y % x == 0:
pair = (y, y2)
num_dict[(x, x2)].append(pair)
for x in num_dict:
for y in num_dict[x]:
for z in num_dict[y]:
ans_set.add((x[0], y[0], z[0]))
return len(ans_set)
I have a 3x3 array with numbers and zeroes. I need to take the absolute difference between the next point, ls[i+1], and the point before it, ls[i]. Here is an example of my list:
ls=[(98.6,99,0),(98.2,98.4,97.1),(97.6,0,98.3)]
The zeroes are faulty data. I need a loop that will:
Take the absolute difference between the future number and the current number in each row,
Make the differences greater than the max difference zero
(max diff=1.9 in this case given that the zeroes are faulty data),
Sum together the differences in each row so that I'm left with a list of the sums.
As it stands now, the end result will be:
result=[(0.4,99),(0.2,1.3),(97.6,98.3)]
Given that the zeroes are not good data, differences greater than 1.9 are not an accurate result.
If you're happy with setting differences over a given maximum difference value to 0, perhaps implement that logic in a 2nd step:
ls=[(98.6,99,0),(98.2,98.4,97.1),(97.6,0,98.3)]
unfiltered = [tuple(abs(x1 - x2) for x1, x2 in zip(tup, tup[1:]))
for tup in ls]
max_diff = 1.9
results = [tuple((x if x < max_diff else 0) for x in tup)
for tup in unfiltered]
If you have objects that are not native python lists/tuples but do support indexing, it might be better to do this:
ls=[(98.6,99,0),(98.2,98.4,97.1),(97.6,0,98.3)]
unfiltered = [tuple(abs(item[i] - item[i+1]) for i in range(len(item)-1))
for item in ls]
max_diff = 1.9
results = [tuple((x if x < max_diff else 0) for x in tup)
for tup in unfiltered]
Not sure why the numbers get all messed up when doing the absolute difference, probably something to do with floating point numbers...
ls=[(98.6,99,0),(98.2,98.4,97.1),(97.6,0,98.3)]
def abs_diff(lst, max_diff=1.9):
n = len(lst)
if n < 2:
return lst
res = []
for i in range(n-1):
diff = abs(lst[i] - lst[i+1])
if diff > max_diff:
res.append(0)
else:
res.append(diff)
return res
result = map(tuple, map(abs_diff, ls))
print result
# [(0.40000000000000568, 0), (0.20000000000000284, 1.3000000000000114), (0, 0)]
This should do you. I've broken out your awkward subtraction/clearing of bad values, but you can tail recursively move through the list, building the needed values as you go, filtering out 0s.
def awkward_subtract(a, b):
if (a is None) or (b is None) or (a == 0) or (b == 0):
return 0
else:
return abs(a - b)
def compare_lists(ls):
head, *tail = ls
if not tail:
return [list(filter(int(0).__ne__, head))]
else:
values = [awkward_subtract(head[x], tail[0][x]) for x in range(0, len(head))]
return [list(filter(int(0).__ne__, values))] + compare_lists(tail)
You can test it in the REPL*:
>>> ls = [[98.6,99,0],[98.2,98.4,97.1],[97.6,0,98.3]]
>>> compare_lists(ls)
[[0.3999999999999915, 0.5999999999999943], [0.6000000000000085, 1.2000000000000028], [97.6, 98.3]]
(*) I think your test is not quite right, btw.
Note that this uses embedded lists for ease, but it is dead simple to fix that:
ts = [(98.6,99,0),(98.2,98.4,97.1),(97.6,0,98.3)]
ls = [list(t) for t in ts]