I need every combination of three positive integers with the sum of 1000.
This was my attempt but I'm unsure if this is correct since I have no way to validate it.
def getSum():
l = []
for x in range(1, 999):
total = 1000-x
for y in range(1, 999):
total = total-y
if total>0:
l.append([x, y, total])
return l
print len(getSum())
I get 28776 different combinations. Is that correct?
Since 1+998+1 and 1+1+998 are not the same thing, there are some incredible amount of combinations:
This line can generate them all:
[(i, 1000-i-k, k) for i in range(1,999) for k in range(1,1000-i)]
Results:
[...
(1, 4, 995),
(1, 3, 996),
(1, 2, 997),
(1, 1, 998),
(2, 997, 1),
(2, 996, 2),
...]
The length of this list is:
498501
No, that number is not correct. The problem with your code is this line:
total = total-y
Here, you decrease total further and further with each value of y that you try, never resetting it to the value after just subtracting x. To fix it, create a new variable, e.g. total2, and use that in the inner loop.
total2 = total-y
This way, you get 498501 combinations. Also, you can break from the inner loop as soon as total2 < 0.
If you need just the number of combinations: Note that there are N-1 combinations to sum two numbers to N, e.g. for N==4: 1+3, 2+2, 3+1 (assuming you consider 1+3 and 3+1 different). You can extend this to the case of three numbers as partitioning the number in two parts two times. This way, you only need a single loop. And this can be simplified further to an O(1) formula.
Example, with naive approach using product as reference:
>>> N = 100 # to make reference faster
>>> sum(1 for t in product(range(1, N+1), repeat=3) if sum(t)==N)
4851
>>> sum(N-1-i for i in range(1, N-1))
4851
>>> ((N-2)*(N-1))//2
4851
Of course, also works for N = 1000 (or much, much larger):
>>> N = 1000
>>> sum(N-1-i for i in range(1, N-1))
498501
>>> ((N-2)*(N-1))//2
498501
If you treated [1,1,998] and [1,998,1] the same (no unique integers):
def getSum():
l = []
for x in range(1, 999):
total = 1000-x
for y in range(1, 999):
total = total-y
if total>0:
z = [x, y, total]
z.sort()
if z not in l:
l.append(z)
return l
a = getSum()
print(len(a))
If you want 3 unique integers:
def getSum():
l = []
for x in range(1, 999):
total = 1000-x
for y in range(1, 999):
total = total-y
if total>0:
z = [x, y, total]
z.sort()
if (z not in l) and (not((len(set(z)) < len(z)))):
l.append(z)
return l
a = getSum()
print(len(a))
Otherwise your code is (in my sense) ok. I haven't check your answer yet...
EDIT : I have checked it using brutal force. The correct answer is actually 498501 if you treated (1,1,998) and (998,1,1) differently. Currently I don't know why...
Try this:
def getSum():
l = []
for x in range(1, 6):
for y in range(1, 6):
total = 6-(y+x)
if total>0:
s = set([x, y, total])
if s not in l:
l.append(s)
print(x, y, total)
return l
print (len(getSum()))
This is my algorithm, Although there is better ways. In this case I wrote code for number 6 and printed all combinations to show how it works. You can set 1000 or any number instead of 6 in this code(in 3 position) and ignore print() line.
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_)
def problem(n):
myList = []
for i in range(2, n):
if n % i == 0:
myList.append(i)
return myList
with this code I was wondering how you would get the factors for example 12 to print out as[[6,2],[3,4]] something like this dosnt have to be in same order thanks.
This should work for you:
import math
def problem(n):
myList = []
for i in range(2, int(math.sqrt(n) + 1)):
if n % i == 0:
myList.append([i, int(n/i)])
return myList
To get the factor pair, this divides n by i, if i is a factor, which will by i's pair.
example:
print(problem(12)) #output: [[2, 6], [3, 4]]
Another way. Loop using range and check if is_integer
num = 12
set([tuple(sorted(j)) for j in [[i, int(num/i)] for i in range(2,num) if (num/i).is_integer()]]
)
#Output:
#{(2, 6), (3, 4)}
You are almost correct . Using range you are not taking the number. Just add n+1 instead of n. That should work. Also you are not stroing the divident in the list. I added that too.
def problem(n):
myList = []
for i in range(2, n+1):
if n % i == 0 and [int(n/i),i] not in myList:
myList.append([i,int(n/i)])
return myList
In order to do the division only once:
for i in range(2, int(math.sqrt(n) + 1)):
d, m = divmod(n, i)
if m == 0:
myList.append([i, d])
You will not get duplicates with upper limit sqrt(n)
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 solved a problem where I had to find sum of numbers whose digits are made of only 4, 5 and 6.. The numbers have at most x fours, at most y fives and at most z sixes. I successfully passed some of the sample tests. But, I cannot get past other test cases as i keep getting segfault errors. Also, I think my programs run time is seriously long. Any help in reducing the run time, optimizing the solution and -reventing segfault will be appreciated.
Here is my code in Python:
from itertools import permutations
x , y, z = raw_input().split(' ')
x = int(x)
y = int(y)
z = int(z)
max = ''
for a in range(z):
max += '6'
for b in range(y):
max += '5'
for c in range(x):
max += '4'
perms = [''.join(p) for p in permutations(max)]
chances = []
def substring(string):
possible = []
for x in range(len(string) + 1):
for y in range(x, len(string) + 1):
possible.append(string[x:y])
return possible
for d in perms:
chances += list(set(substring(d)))
chances = list(set(chances))
chances.remove('')
sum = 0
for nstr in chances:
sum += int(nstr)
print sum
It would be helpful to know what part of the program is consuming the most time. Looking at the second half, after the call to permutations, I see that you are creating potentially huge lists (in chances and permutations. After building them you convert to a set (to eliminate duplicates I suppose) and back again to a list. Why not use a single set instead, like this:
chances = set()
def substring(string):
for x in range(len(string) + 1):
for y in range(x, len(string) + 1):
chances.add(string[x:y])
for d in perms:
substring(d)
chances.remove('')
sum = 0
for nstr in chances:
sum += int(nstr)
print sum
I don't know if that will solve all your problems, but it should help.
I am looking into a problem: given an arbitrary list, in this case it is [9,15,1,4,2,3,6], find any two numbers that would sum to a given result (in this case 10). What would be the most efficient way to do this? My solution is n2 in terms of big O notation and even though I have filtered and sorted the numbers I am sure there is a way to do this more efficiently. Thanks in advance
myList = [9,15,1,4,2,3,6]
myList.sort()
result = 10
myList = filter(lambda x:x < result,myList)
total = 0
for i in myList:
total = total + 1
for j in myList[total:]:
if i + j == result:
print i,j
break
O(n log n) solution
Sort your list. For each number x, binary search for S - x in the list.
O(n) solution
For each number x, see if you have S - x in a hash table. Add x to the hash table.
Note that, if your numbers are really small, the hash table can be a simple array where h[i] = true if i exists in the hash table and false otherwise.
Use a dictionary for this and for each item in list look for total_required - item in the dictionary. I have used collections.Counter here because a set can fail if total_required - item is equal to the current item from the list. Overall complexity is O(N):
>>> from collections import Counter
>>> def find_nums(total, seq):
c = Counter(seq)
for x in seq:
rem = total - x
if rem in c:
if rem == x and c[rem] > 1:
return x, rem
elif rem != x:
return x, rem
...
>>> find_nums(2, [1, 1])
(1, 1)
>>> find_nums(2, [1])
>>> find_nums(24, [9,15,1,4,2,3,6])
(9, 15)
>>> find_nums(9, [9,15,1,4,2,3,6])
(3, 6)
I think, this solution would work....
list = [9,15,1,4,2,3,6]
result = 10
list.sort()
list = filter(lambda x:x < result,list)
myMap = {}
for i in list:
if i in myMap:
print myMap[i], i
break
myMap[result - i] = i