How to find indices and combinations that adds upto given sum? - python-3.x

How to find the combinations and corresponding indices that adds upto given sum ?
And also, can it be handled list of elements of size 500000 (higher size) ?
Input:
l1 = [9,1, 2, 7, 6, 1, 5]
target = 8
**Constraints**
1<=(len(l1))<=500000
1<=each_list_element<=1000
Output:
Format : {index:element}
{1:1, 5:1, 4:6} #Indices : 1,5,4 Elements : 1,1,6
{1:1, 2:2, 6:5}
{5:1, 2:2, 6:5}
{1:1, 3:7}
{5:1, 3:7}
{2:2, 4:6}
Tried:
from itertools import combinations
def test(l1, target):
l2 = []
l3 = []
if len(l1) > 0:
for r in range(0,len(l1)+1):
l2 += list(combinations(l1, r))
for i in l2:
if sum(i) == target:
l3.append(i)
return l3
l1 = [9,1, 2, 7, 6, 1, 5]
target = 8
print(test(l1,target))
[(1, 7), (2, 6), (7, 1), (1, 2, 5), (1, 6, 1), (2, 1, 5)]
Can someone guide me ?
UPDATE
Apart from above, code fails to handle these scenarios
Input = [4,6,8,5,3]
target = 3
Outputs {} , need to output {4:3}
Input = [4,6,8,3,5,3]
target = 3
Outputs {} , need to output {5:3,3:3} #corrected index
Input = [1,2,3,15]
target = 15
Outputs = {}, need to output {3:15}

Your code was close, i would use enumerate to get the index and value as tuple pairs. I am always dropping any of the index and value tuples where that value is greater than the target since that cannot possible be a match. this will generate less combinations. Then like you i just iterate through the permutations of tuples and sum the values in each permutation, if it sums to the target then yield that permutation. lastly in the loop to output the values i give the perm to dict to convert into the dict format you wanted
from itertools import combinations
def find_sum_with_index(l1, target):
index_vals = [iv for iv in enumerate(l1) if iv[1] < target]
for r in range(1, len(index_vals) + 1):
for perm in combinations(index_vals, r):
if sum([p[1] for p in perm]) == target:
yield perm
l1 = [9, 1, 2, 7, 6, 1, 5]
target = 8
for match in find_sum_with_index(l1, target):
print(dict(match))
OUTPUT
{1: 1, 3: 7}
{2: 2, 4: 6}
{3: 7, 5: 1}
{1: 1, 2: 2, 6: 5}
{1: 1, 4: 6, 5: 1}
{2: 2, 5: 1, 6: 5}

You can just use index function to get index and store them as key:value pair with help of dictionary in another list such as following,
from itertools import combinations
def test(l1, target):
l2 = []
l3 = []
l4=[]
dict1={}
a=0
if len(l1) > 0:
for r in range(0,len(l1)+1):
l2 += list(combinations(l1, r))
for i in l2:
dict1={}
if sum(i) == target:
for j in i:
a=l1.index(j)
dict1[a]=j
l4.append(dict1)
l3.append(i)
return l4
l1 = [4,6,8,5,3]
target = 3
print(test(l1,target))
output:
[{4: 3}]
as you can see the condition l1 = [4,6,8,5,3] target = 3 works which is previously not working.
Hope this helps!

Related

How do you append a value to a list by a certain frequency?

I'm writing a program to analyse a frequency table with different functions (mean, median, mode, range, etc) and I have the user inputting their data in two lists and then converting those answers into lists of integers
values_input = input('First, enter or paste the VALUES, separated by spaces (not commas): ')
freq_input = input('Now enter the corresponding FREQUENCIES, separated by spaces: ')
values = values_input.split()
freq = freq_input.split()
data_list = []
For every value, I want the program to append it to data_input by the corresponding frequency.
For example (desired result):
If values was: 1 2 3 4
and frequency was: 2 5 7 1
I want data_list to be:
[1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4]
At the moment I have this:
for i in range(len(values)):
j = 3
while j != 0:
data_input.append(values[i])
j -= 1
But that only appends the values to data_input 3 times instead of the frequency
a = input("First, enter or paste the VALUES, separated by spaces (not commas): ").split()
b = input("Now enter the corresponding FREQUENCIES, separated by spaces: ").split()
a = [int(i) for i in a]
b = [int(i) for i in b]
x = []
y = 0
for elem in a:
temp = []
temp.append(elem)
x.append(temp * b[0+y])
y += 1
final = []
for lst in x:
for elem in lst:
final.append(elem)
print(final)
I am also a newbie. I know there are more efficient ways, but for now I have come up with this.
You can use list multiplication to create a list of each value with the appropriate frequency. zip the two lists together to get the matching values for each index:
values = [1,2,3,4]
freq = [2,5,7,1]
result = []
for v, f in zip(values, freq):
result += [v] * f
Output:
[1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4]

Detect ranges of integers in a list in python

I am trying to write a function "detect_range" which detects ranges of integers from a list let's say:
a = [2, 4, 5, 6, 7, 8, 10, 12, 13]
import itertools
def detect_range(L):
for i, j in itertools.groupby(enumerate(L), lambda x: x[1] - x[0]):
j = list(j)
yield j[0][1], j[-1][1]
print(list(detect_range(a)))
It prints:
[(2, 2), (4, 8), (10, 10), (12, 13)]
However, I do not want the single integer like 2 and 10 to be printed in a pair, but single. So the output which I am looking from this code is:
[2, (4, 9), 10, (12, 14)]
If you insist on using itertools, you should add an if-statement to differenciate between the different cases.
To make the code more readable, I added the temporal variables start and length.
import itertools
def detect_range(L):
for i, j in itertools.groupby(enumerate(L), lambda x: x[1] - x[0]):
j = list(j)
start = j[0][1]
length = len(j)
if length == 1:
yield start
else:
yield (start, start+length)
print(list(detect_range(a)))
[2, (4, 9), 10, (12, 14)]
Otherwise, you could scrap itertools and simply implement your own algorithm:
def detect_range(input_list):
start = None
length = 0
for elem in input_list:
# First element
if start is None:
start = elem
length = 1
continue
# Element in row, just count up
if elem == start + length:
length += 1
continue
# Otherwise, yield
if length == 1:
yield start
else:
yield (start, start+length)
start = elem
length = 1
if length == 1:
yield start
else:
yield (start, start+length)
print(list(detect_range(a)))
[2, (4, 9), 10, (12, 14)]
Change it to
if j[0][1] == j[-1][1]:
yield j[0][1]
else:
yield j[0][1], j[-1][1]
You can change the yield statement to have a condition -
def detect_range(L):
for i, j in itertools.groupby(enumerate(L), lambda x: x[1] - x[0]):
j = list(j)
yield (j[0][1], j[-1][1]) if j[0][1]!=j[-1][1] else j[0][1]
Output:
[2, (4, 8), 10, (12, 13)]
Also the expected output is different from the given output (apart from single 2 and 10). So, this code assumes that it was a typo

Counting instances of N in an array using Recursion in Python

I would like to count the number of instances of a given number N in an array using recursion. For example, given:
array = [1, 2, 3, 1, 1, 4, 5, 2, 1, 8, 1]
and N = 1, the function should return 5.
This problem can be solved using the .counter attribute as shown here. However, I am looking to not use any in-built functions or attributes.
Here's my attempt to solve this using recursion but I get a count of 1 and not 5. What am I doing wrong?
def count_val(array, n, count=0):
if len(array) == 0:
return None
# Base Case
if len(array) == 1:
if array[0] == n:
count += 1
else:
count_val(array[1:], n, count)
if array[0] == n:
count += 1
return count
print(count_val2(array, 1))
1
I think for an empty array, the value should be 0 (len == 0 should be the base case), and, you don't need to have a count parameter if you just return the count, your function could be reduced to this:
def count_val(array, n):
if len(array) == 0:
return 0
return (array[0] == n) + count_val(array[1:], n)
array = [1, 2, 3, 1, 1, 4, 5, 2, 1, 8, 1]
print(count_val(array, 1))
Output:
5
You can have it as a one-liner as well (as suggested by #blhsing):
def count_val(array, n):
return len(array) and (array[0] == n) + count_val(array[1:], n)
What am I doing wrong?
The function you wrote will always keep only the last few characters, so after a while it will be [1, 8, 1], after that [8, 1] and after that [1], which returns 1. The array never contains just any of the other 1s.
An easy way to do this is to loop over all elements in a list and test if they are equal to N.
array = [1, 2, 3, 1, 1, 4, 5, 2, 1, 8, 1]
def count_val(array, n):
if len(array) == 0:
return 0
count=0
for i in array:
if i==n:
count += 1
return count
print(count_val(array, 1))
This returns 5.

All test cases passed except one which is throwing key error

We are given a directed graph, with nodes labeled 0, 1, ..., n-1 and each edge is either red or blue, and there could be self-edges or parallel edges.
Each [i, j] in red_edges denotes a red directed edge from node i to node j. Similarly, for blue_edges.
Return an array 'answer' of length n, where each answer[X] is the length of the shortest path from node 0 to node X such that the edge colors alternate along the path (or -1 if such a path doesn't exist).
I'm not getting where's the problem in the code.
n = 3, red_edges = [[0,1],[1,2]], blue_edges = []
n = 3, red_edges = [[0,1]], blue_edges = [[2,1]]
n = 3, red_edges = [[0,1]], blue_edges = [[1,2]]
n = 3, red_edges = [[1,0]], blue_edges = [[2,1]]
d={}
result=[-1]*(n)
for i in red_edges:
if i[0] not in d:
d[i[0]]=[i[1]]
else:
d[i[0]].append(i[1])
if i[0]==0:
result[i[1]]=1
#print(d)
for j in blue_edges:
if j[0] not in d:
d[j[0]]=[j[1]]
else:
d[j[0]].append(j[1])
if j[0]==0:
result[j[1]]=1
elif j[0]!=0:
if j[0] in d[0]: #This is Line 24 which is throwing error.
result[j[1]]=2
else:
pass
if 0 in d[0]:
result[0]=1
else:
result[0]=0
#print(d)
print(result)
Key in 'd' is equal to the starting node and value in a key-value pair has a list in which all the endpoints corresponding to that key are inserted.
Moreover, in each for loop, I'm also preparing my result array if any edge is present between node zero and node equal to the index of the result array. e.g, I'll insert 1 in result array for index 1 if it has a direct link with node zero else I'll keep it as -1. And if any index has indirect link( as in test case 2: blue_edges =[[2,1]]) then I'll check in d[0] if first element of blue_edges is present. If it is present then I'll insert 2 else -1.
expected=actual=[0,1,-1]
expected=actual=[0,1,-1]
expected=actual=[0,1,1]
expected=[0,-1,-1] but in 4th case it is throwing an error.
Line 24: KeyError: 0
I switched to using defaultdict(list) so that if d is empty for some value, you'll just get an empty list.
In your case - it eliminates the error and logically matches the if.
it also help clean up many if/elses.
try this:
from collections import defaultdict
cases = [
(3, [[0, 1], [1, 2]], []),
(3, [[0, 1]], [[2, 1]]),
(3, [[0, 1]], [[1, 2]]),
(3, [[1, 0]], [[2, 1]]),
]
for n, red_edges, blue_edges in cases:
d = defaultdict(list)
result = [-1] * n
result[0] = 0
for src, dst in red_edges:
d[src].append(dst)
if src == 0:
result[dst] = 1
for src, dst in blue_edges:
d[src].append(dst)
if src == 0:
result[dst] = 1
else:
if src in d[0]:
result[dst] = 2
print(result)
Output:
[0, 1, -1]
[0, 1, -1]
[0, 1, 2]
[0, -1, -1]

How to iterate through a numbered list and every time value 1 appears start over?

Given a list:
list1 = [1,2,3,4,5,6,1,2,3,4,1,2,3,4,5,6,7]
While iterating though list1, every time the integer 1 is hit, start the loop over but increment it by 1.
Tried the two examples below but it only returns a list of 1's for the length of list1.
digit = []
i = 0
for num in list1:
num = i
if num != 1:
i += 1
digit.append(i)
elif num == 1:
digit.append(num)
digit = []
i = 0
for num in list1:
num = i
if num == 1:
digit.append(num)
continue
elif num != 1:
i += 1
digit.append(i)
digit
Looking to get something like the list below
digit = [1,1,1,1,1,1,2,2,2,2,3,3,3,3,3,3,3]
You're overthinking this. Initialise a variable to zero. Append it to a list at each iteration. Increment if the corresponding list value is 1.
values = []
i = 0
for l in list1:
if l == 1: # The check must come before appending. Can you explain why?
i += 1
values.append(i)
values
# [1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3]
If you were to ask me for a pythonic solution to this problem, I'd suggest itertools.accumulate:
from itertools import accumulate
from operator import add
list(accumulate((int(x == 1) for x in list1), add))
# [1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3]

Resources