Find all numbers that sum closest to a given number python - python

I have a script below that gives the closest 2 values to a given sum. It also iterates through a list of given sums and after each iteration removes the numbers that have already been used.
I need to modify this script so that it produces a necessary amount of values closest to each sum, rather than 2. The script needs to accept float values and cannot re-use values. Effectively it needs to pick the most efficient set closest to target, update the set to remove values used, then move on to next target etc..
With pairs it it doesn't work very well for specific use cases/sets that require 3 numbers or 4 etc. to actually get closest to the sum. I need this script to also be able to accept float values, which this script currently does.
Any suggestions would be much appreciated. Also if someone knows a better script for this, please let me know.
import sys
def find_closese_sum(numbers, target):
start = 0
end = len(numbers) - 1
result = sys.maxint
result_tuple = None
while start < end:
if numbers[start] + numbers[end] == target:
print 0, (numbers[start], numbers[end])
return
elif numbers[start] + numbers[end] > target:
if abs(numbers[start] + numbers[end] - target) < result:
result = abs(numbers[start] + numbers[end] - target)
result_tuple = (numbers[start], numbers[end])
end -= 1
else:
if abs(numbers[start] + numbers[end] - target) < result:
result = abs(numbers[start] + numbers[end] - target)
result_tuple = (numbers[start], numbers[end])
start += 1
for i in result_tuple:
numbers.remove(i)
return result_tuple
if __name__ == "__main__":
target = [14,27,39]
numbers = [1,5,5,10,7,8,11,13,66,34]
print numbers
numbers = sorted(numbers)
for i in target:
result_shown = find_closese_sum(numbers, i)
print result_shown

I don't see any elegant way to do this, so you probably are going to have to brute force the solution. Make all possible subsets of numbers, sum them, and check which is the closest to the target. It would look something like this.
from itertools import permutations
def find_closest_sum(numbers, target, n):
permlist = list(permutations(numbers, n))
sumlist = [sum(l) for l in permlist]
maxpos = 0
for i in range(1, len(sumlist)):
if abs(sumlist[i] - target) < abs(sumlist[maxpos]-target):
maxpos = i
return permlist[maxpos]
numbers = [1,5,5,10,7,8,11,13,66,34]
result_shown = find_closest_sum(numbers, 20, 4)
print result_shown
Using permutations makes a lot of the code you wrote unnecessary.

My answer, using python 3 but you should be able to port it easily.
from itertools import combinations as c
if __name__ == "__main__":
target = [14,27,39]
numbers = [1,5,5,10,7,8,11,13,66,34]
for combo in range(1,4):
for i in target:
#lambda to find the difference between sum and target
diff = lambda x: abs(sum(x) - i)
#get all unique combinations
combos = {tuple(sorted(c)) for c in c(numbers, combo)}
#sort them
combos = sorted(combos, key = diff)
#get the smallest difference
smallest = diff(combos[0])
#filter out combos larger than the smaller difference
result = [c for c in combos if diff(c) == smallest]
print('results for {}, best combinations are off by {}:'.format(i, smallest))
print(result)
You stated you need to exclude numbers used for a previous result. To do that just remove them from the list:
#after print(result), inside the "for i in target" loop
#if you want to exclude all numbers used in all combinations
numbers = [n for n in numbers if n not in [b for a in result for b in a]]
#if you only need to remove the first match
numbers = [n for n in numbers if n not in result[0]]

import numpy as np
import itertools
def find_closese_sum(numbers, targets):
numbers = numbers[:]
for t in targets:
if not numbers:
break
combs = sum([list(itertools.combinations(numbers, r))
for r in range(1, len(numbers)+1)], [])
sums = np.asarray(list(map(sum, combs)))
bestcomb = combs[np.argmin(np.abs(np.asarray(sums) - t))]
numbers = list(set(numbers).difference(bestcomb))
print("Target: {}, combination: {}".format(t, bestcomb))
Example
In [101]: import numpy as np
...: import itertools
...:
...: def find_closese_sum(numbers, targets):
...: numbers = numbers[:]
...: for t in targets:
...: if not numbers:
...: break
...: combs = sum([list(itertools.combinations(numbers, r))
...: for r in range(1, len(numbers)+1)], [])
...: sums = np.asarray(list(map(sum, combs)))
...: bestcomb = combs[np.argmin(np.abs(np.asarray(sums) - t))]
...: numbers = list(set(numbers).difference(bestcomb))
...: print("Target: {}, combination: {}".format(t, bestcomb))
...:
...: targets = [14,27.,39]
...: numbers = [1.0,5,5,10,7,8,11,13,66,34]
...: find_closese_sum(numbers, targets)
...:
Target: 14, combination: (1.0, 13)
Target: 27.0, combination: (5, 5, 10, 7)
Target: 39, combination: (8, 34)

Without making it complex here is my approach, Try this :
import itertools
def finding_closet(ls,target,depth):
closest = []
for i in itertools.combinations(ls, depth):
if sum(i) == target:
return i
else:
closest.append((abs(sum(i) - target), i))
return min(closest)[1]
Test_case 0:
print(finding_closet([1,5,5,10,7,8,11,13,66,34],20,2))
output:
(7, 13)
Test_case 1:
print(finding_closet([1,5,5,10,7,8,11,13,66,34],20,4))
output:
(1, 5, 5, 8)
test_case_3:
print(finding_closet([1,5,5,10,7,8,11,13,66,34],20,8))
output:
(1, 5, 5, 10, 7, 8, 11, 13)

Related

Given an array of N integers, and an integer K, find the number of pairs of elements in the array whose sum is equal to K

Problem Statement:- Given an array of N integers, and an integer K, find the number of pairs of elements in the array whose sum is equal to K.
**def countpairs(x,length,sum):
count = 0
for i in range(0,length):
for j in range(i+1,length):
print(x[i],x[j])
if(x[i]+x[j]==sum):
count+=1
print(count)
x = [1, 1, 1, 1]
sum = 2
length=len(x)
countpairs(x,length,sum)
Output:= 6**
This is My solution used in VS code.
My Question:- whenever I am running the same code in gfg it is not accepting the code giving me this error. I even have tried the same code in the online compiler there also it is running correctly.
This Is the gfg code which i have written
class Solution:
def getPairsCount(self, arr, K, N):
count = 0
for i in range(0,N):
for j in range(i+1,N):
if(arr[i]+arr[j]==K):
count+=1
return count
#Initial Template for Python 3
if __name__ == '__main__':
tc = int(input())
while tc > 0:
n, k = list(map(int, input().strip().split()))
arr = list(map(int, input().strip().split()))
ob = Solution()
ans = ob.getPairsCount(arr, n, k)
print(ans)
tc -= 1
Error
if(arr[i]+arr[j]==K):
IndexError: list index out of range
There's no added value in using a class for this. You just need:-
def getPairsCount(arr, K):
count = 0
for i in range(len(arr)-1):
if arr[i] + arr[i+1] == K:
count += 1
return count
EDIT:
Previous answer assumed that only adjacent elements were to be considered. If that's not the case then try this:-
import itertools
def getPairsCount(arr, K):
count = 0
for c in itertools.combinations(sorted(arr), 2):
if c[0] + c[1] == K:
count += 1
return count
data = [1, 2, 1, 4, -1]
print(getPairsCount(data, 3))
We do not need two loops for this question. Here is something that runs in O(n):
def countpairs(list_,K):
count = 0
set_ = set(list_)
pairs_ = []
for val in list_:
if K - val in set_:
# we ensure that pairs are unordered by using min and max
pairs_.append ( (min(val, K-val), max(val, K-val)) )
count+=1
set_pairs = set(pairs_)
print ("Pairs which sum up to ",K," are: ", set_pairs)
return len(set_pairs)
x = [1,4,5,8,2,0,24,7,6]
sum_ = 13
print ("Total count of pairs summming up to ", sum_, " = ", countpairs(x, sum_))
Output:
Pairs which sum up to 13 are: {(6, 7), (5, 8)}
Total count of pairs summming up to 13 = 2
The idea is that if two values should sum to a value K, we can iterate through the array and check if there is another element in the array which when paired with the current element, sums up to K. The inner loop in your solution can be replaced with a search using the in. Now, we need this search to be fast (O(1) per element), so we create a set out of our input array (set_ in my example).
def solve(a,K):
freq = {}
for v in a:
if v in freq:
freq[v] += 1
else:
freq[v] = 1
for i in range(len(set(a))):
res += freq[a[i]] * freq[K - a[i]]
return res
a = [int(v) for v in input().split()]
K = int(input())
print(solve(a,K))
# Time Complexity : O(N)
# Space Complexity : O(1)
def solve(a,K):
freq = {}
for v in a:
if v in freq:
freq[v] += 1
else:
freq[v] = 1
for i in range(len(set(a))):
res += freq[a[i]] * freq[K - a[i]]
return res
a = [int(v) for v in input().split()]
K = int(input())
print(solve(a,K))

Grab 'n' numbers from a given list of numbers with minimum difference between them

I put up a similar question a few hours ago, albeit with a few mistakes, and my poor understanding, admittedly
So the question is, from a given list of indefinite numbers, I'm supposed to take an input from the user, say 3, and grab 3 numbers wherein the numbers have the least difference between them.
def findMinDiff(arr):
# Initialize difference as infinite
diff = 10**20
n = len(arr)
# Find the min diff by comparing difference
# of all possible pairs in given array
for i in range(n-1):
for j in range(i+1,n):
if abs(arr[i]-arr[j]) < diff:
diff = abs(arr[i] - arr[j])
# Return min diff
return diff
def findDiffArray(arr):
diff = 10**20
arr_diff = []
n = len(arr)
for i in range(n-1):
arr_diff.append(abs(arr[i]-arr[i+1]))
return arr_diff
def choosingElements(arr, arr_diff):
arr_choose = []
least_index = 0
least = arr_diff[0]
least_index_array = []
flag = 0
flag2 = 0
for z in range(0,3):
for i in range(0,len(arr_diff)-1):
if arr_diff[i] < least:
if flag > 0:
if i == least_index:
continue
least = arr_diff[i]
least_index = i
least_index_array.append(i)
arr_choose.append(arr[i])
flag += 1
arr_choose.append(arr[i+1])
flag += 1
print("least index is", least_index)
return arr_choose
# Driver code
arr = [1, 5, 3, 19, 18, 25]
arr_diff = findDiffArray(arr)
arr_diff2 = arr_diff.copy()
item_number = int(input("Enter the number of gifts"))
arr_choose = choosingElements(arr, arr_diff2)
print("Minimum difference is " + str(findMinDiff(arr)))
print("Difference array")
print(*arr_diff, sep = "\n")
print("Numbers with least difference for specified items are", arr_choose)
This is how much I've tried, and I've thought to find the difference between numbers, and keep picking ones with the least difference between them, and I realised that my approach is probably wrong.
Can anybody kindly help me out? Thanks!
Now, I'm sure the time complexity on this isn't great, and it might be hard to understand, but how about this:
arr = [1, 18, 5, 19, 25, 3]
# calculates length of the overall path
def calc_path_difference(arr, i1, i2, i3):
return abs(arr[i1] - arr[i2]) + abs(arr[i2] - arr[i3])
# returns dictionary with differences to other numbers in arr from each number
def differences_dict(arr):
return {
current: [
abs(number - current) if abs(number - current) != 0 else float("inf")
for number in arr
]
for current in arr
}
differences = differences_dict(arr)
# Just to give some starting point, take the first three elements of arr
current_path = [calc_path_difference(arr, 0, 1, 2), 0, 1, 2]
# Loop 1
for i, num in enumerate(arr):
# Save some time by skippin numbers who's path
# already exceeds the min path we currently have
if not min(differences[num]) < current_path[0]:
continue
# Loop 2
for j, num2 in enumerate(arr):
# So you can't get 2 of the same index
if j == i:
continue
# some code for making indices i and j of differences
# infinite so they can't be the smallest, but not sure if
# this is needed without more tests
# diff_arr_copy = differences[num2].copy()
# diff_arr_copy[i], diff_arr_copy[j] = float("inf"), float("inf")
# Get index of number in arr with smallest difference to num2
min_index = differences[num2].index(min(differences[num2]))
# So you can't get 2 of the same index again
if min_index == i or min_index == j:
continue
# Total of current path
path_total = calc_path_difference(arr, i, j, min_index)
# Change current path if this one is shorter
if path_total < current_path[0]:
current_path = [path_total, i, j, min_index]
Does this work for you? I played around with the order of the elements in the array and it seemed to give the correct output each time but I would have liked to have another example to test it on.

How to use recursion to deal with variable numbers of nested for loop?

I am dealing with the following question:
Create a function that accepts two arguments, the number of dice rolled, and the outcome of the roll. The function returns the number of possible combinations that could produce that outcome. The number of dice can vary from 1 to 6.
And below is my code for 4 dice (x=4). But I am not able to extend this to any number of dices (x).
def dice_roll(x, y):
all_comb = []
for i in range(1, 7):
for j in range(1, 7):
for q in range(1, 7):
for r in range(1, 7):
total = i+j+q+r
all_comb.append(total)
return all_comb.count(y)
Is there a way to use recursion to deal with variable numbers of nested loops? And are there other more elegant ways apart from recursion for this question?
As mentioned in the comments you should use itertools.product like this:
import itertools
def dice_roll(dices: int, result: int) -> int:
combos = [x for x in itertools.product(range(1, 7), repeat=dices) if sum(x) == result]
return len(combos)
# combos = [(1, 3), (2, 2), (3, 1)]
if __name__ == '__main__':
print(dice_roll(2, 4))
# returns 3
With itertools.product you get all possible combinations of the given amount of dices. with the list comprehension we filter the values by the correct sum.
Here's a version that calculates the number of rolls for each total in O(n*s) time where n is the number of dice, and s the number of sides. It uses O(n) storage space.
If R[k, t] is the number of rolls of k dice that total t (keeping the number of sides fixed), then the recurrence relations are:
R[0, t] = 1 if t=0, 0 otherwise
R[k, t] = 0 if t < 0
R[k, t] = R[k-1, t-1] + R[k-1, t-2] + ... + R[k-1, t-s]
Then we solving this with dynamic programming.
def dice_roll(n, sides):
A = [1] + [0] * (sides * n)
for k in range(n):
T = sum(A[k] for k in range(max(0, sides*k), sides*(k+1)))
for i in range(sides*(k+1), -1, -1):
A[i] = T
if i > 0:
T -= A[i-1]
if i - sides - 1 >= 0:
T += A[i - sides - 1]
return A
print(dice_roll(9, 4))
The program returns an array A with A[i] storing the number of ways of rolling n dice with s sides that sum to i.
a more mathematical approach using sympy.
for larger numbers of dice this will scale way better than iterating over all possibilities; for small numbers of dice the start-up time of sympy will probably not be worth the trouble.
from sympy.utilities.iterables import partitions, multiset_permutations
def dice_roll(n_dice, total):
ret = 0
for item in partitions(total, k=6, m=n_dice):
if sum(item.values()) != n_dice:
continue
print(f"{item}")
# this for loop is only needed if you want the combinations explicitly
for comb in multiset_permutations(item):
print(f" {comb}")
ret += sum(1 for _ in multiset_permutations(item))
return ret
i get the number of partitions of total first (limiting the maximum value with k=6 as needed for a dice) and then count the number of possible multiset partitions.
the example:
r = dice_roll(n_dice=3, total=5)
print(r)
outputs:
{3: 1, 1: 2}
[1, 1, 3]
[1, 3, 1]
[3, 1, 1]
{2: 2, 1: 1}
[1, 2, 2]
[2, 1, 2]
[2, 2, 1]
6
meaning there are 6 ways to get to 5 with 3 dice. the combinations are shown.
in order to speed up things you could calculate the number of multiset combinations directly (you loose the explicit representation of the possibilities, though):
from sympy.utilities.iterables import partitions, multiset_permutations
from math import comb
def number_multiset_comb(dct):
ret = 1
n = sum(dct.values()) # number of slots
for v in dct.values():
ret *= comb(n, v)
n -= v
return ret
def dice_roll(n_dice, total):
ret = 0
for item in partitions(total, k=6, m=n_dice):
if sum(item.values()) != n_dice:
continue
ret += number_multiset_comb(dct=item)
return ret
def combin_list(n):
# n is number of dice
if n == 1:
return [1,2,3,4,5,6]
else:
temp = combin_list(n-1)
resualt = []
for i in range(1,7):
for item in temp:
resualt.append(i+item)
return resualt
def dice_roll(x, y):
list = combin_list(x)
return list.count(y)

The sum of numbers

I've been making a program that finds two numbers in a random list and prints them if their sum is 8.
Honestly, I've been sitting here for half an hour and idk what's going on. I think I'm pretty close, but in rare cases it doesn't find an exitsting combination(list = [1,4,4,9] -> No combination). Also in rare cases I will get an error saying
RecursionError: maximum recursion depth exceeded in comparison
Here's my code:
import random
list = []
for i in range(1,5,1):
newNum = random.randint(1,10)
list.append(newNum)
list.sort()
sum = 8
print('\nRandomly generated list:')
print(list)
firstNum = list[0]
lastNum = list[-1]
newList = []
def isSum(a,b):
if a + b == sum:
if list.index(a) == list.index(b):
print('\nThere isnt a combination.')
else:
newList.append(a)
newList.append(b)
print('\nCombination:')
print(newList)
elif a + b < sum:
temp = list.index(a)
temp += 1
if temp > list.index(lastNum):
print('\nThere isnt a combination.')
else:
a = list[temp]
isSum(a,b)
else:
temp = list.index(b)
temp -= 1
if temp < list.index(firstNum):
print('\nThere isnt a combination.')
else:
b = list[temp]
isSum(a,b)
isSum(firstNum,lastNum)
I'm just a beginner, don't get angry if I made a stupid mistake :3
You can use itertools module for generating all combinations of your list, then filter that by calculating the sum of each combination, for example this:
import itertools
a = [1, 4, 4, 9] # any list of nums
groups = 2
result = 8
combinations = [combination for combination in itertools.combinations(a, groups)]
output = [combination for combination in combinations if sum(combination) == result]
print(output)
>>> [(4, 4)]
Recursion really isn't ideal in Python, and your code could certainly be simplified.
This should return all the pairs.
import itertools as itt
import random
from typing import List, Tuple
def is_comb_sum(nums: List[int], comb_size: int, target_sum: int) -> List[Tuple[int, ...]]:
combs = []
for curr_pair in itt.combinations(nums, comb_size):
curr_sum = sum(curr_pair)
if curr_sum == target_sum:
combs.append(curr_pair)
return combs
nums_list = [random.randint(0, 10) for _ in range(5)]
print(nums_list)
res = is_comb_sum(nums_list, 2, 8)
print(res)
If you only want to print each combination once, you can use a set to identify the distinct numbers that are present. Then, for each of these number, you determine which complementing value is need to reach your target (8) and if it is also in the set then the pair exists. The only exception to this is when the number is exactly half of the target (i.e. 4) in which case you have to make sure there are at least two instances of that number in the list:
target = 8
count = 4
numbers = [random.randint(1,10) for _ in range(count)]
print(numbers)
numberSet = set(numbers)
for number in numberSet:
other = target-number
if other not in numberSet: continue
if other > number: continue # avoid duplicates such as 2+6=8 and 6+2=8
if other == number and numbers.count(number) < 2: continue
print(number,"+",other,"=",target)
Output:
[7, 2, 6, 1]
6 + 2 = 8
7 + 1 = 8
If you want to print all the combinations, you can use the Counter object from the collection modules and either print the number of occurrences or repeat the printed lines:
target = 12
count = 8
numbers = [random.randint(1,10) for _ in range(count)]
print(numbers)
from collections import Counter
numberCounts = Counter(numbers)
for number in numberCounts:
other = target-number
if other > number: continue
pairCount = numberCounts[number] * numberCounts[other]
if number == other:
pairCount = (pairCount - numberCounts[number]) // 2
if pairCount > 0:
print(number,"+",other,"=",target,"occurred",pairCount,"time(s)")
Output (target of 12 in list of 8):
[7, 6, 5, 5, 6, 6, 3, 4]
7 + 5 = 12 occurred 2 time(s)
6 + 6 = 12 occurred 3 time(s)

Largest subarray with sum equal to 0

This is a typical interview question. Given an array that contains both positive and negative elements without 0, find the largest subarray whose sum equals 0. I tried to solve this. This is what I came up with.
def sub_array_sum(array,k=0):
start_index = -1
hash_sum = {}
current_sum = 0
keys = set()
best_index_hash = {}
for i in array:
start_index += 1
current_sum += i
if current_sum in hash_sum:
hash_sum[current_sum].append(start_index)
keys.add(current_sum)
else:
if current_sum == 0:
best_index_hash[start_index] = [(0,start_index)]
else:
hash_sum[current_sum] = [start_index]
if keys:
for k_1 in keys:
best_start = hash_sum.get(k_1)[0]
best_end_list = hash_sum.get(k_1)[1:]
for best_end in best_end_list:
if abs(best_start-best_end) in best_index_hash:
best_index_hash[abs(best_start-best_end)].append((best_start+1,best_end))
else:
best_index_hash[abs(best_start-best_end)] = [(best_start+1,best_end)]
if best_index_hash:
(bs,be) = best_index_hash[max(best_index_hash.keys(),key=int)].pop()
return array[bs:be+1]
else:
print "No sub array with sum equal to 0"
def Main():
a = [6,-2,8,5,4,-9,8,-2,1,2]
b = [-8,8]
c = [-7,8,-1]
d = [2200,300,-6,6,5,-9]
e = [-9,9,-6,-3]
print sub_array_sum(a)
print sub_array_sum(b)
print sub_array_sum(c)
print sub_array_sum(d)
print sub_array_sum(e)
if __name__ == '__main__':
Main()
I am not sure if this will satisfy all the edge case. if someone can comment on that, it would be excellent Also i want to extend this to sum equalling to any K not just 0. How should i go about it. And any pointers to optimize this further is also helpful.
You have given a nice, linear-time solution (better than the two other answers at this time, which are quadratic-time), based on the idea that whenever sum(i .. j) = 0, it must be that sum(0 .. i-1) = sum(0 .. j) and vice versa. Essentially you compute the prefix sums sum(0 .. i) for all i, building up a hashtable hash_sum in which hash_sum[x] is a list of all positions i having sum(0 .. i) = x. Then you go through this hashtable, one sum at a time, looking for any sum that was made by more than one prefix. Among all such made-more-than-once sums, you choose the one that was made by a pair of prefixes that are furthest apart -- this is the longest.
Since you already noticed the key insight needed to make this algorithm linear-time, I'm a bit puzzled as to why you build up so much unnecessary stuff in best_index_hash in your second loop. For a given sum x, the furthest-apart pair of prefixes that make that sum will always be the smallest and largest entries in hash_sum[x], which will necessarily be the first and last entries (because that's the order they were appended), so there's no need to loop over the elements in between. In fact you don't even need a second loop at all: you can keep a running maximum during your first loop, by treating start_index as the rightmost endpoint.
To handle an arbitrary difference k: Instead of finding the leftmost occurrence of a prefix summing to current_sum, we need to find the leftmost occurrence of a prefix summing to current_sum - k. But that's just first_with_sum{current_sum - k}.
The following code isn't tested, but should work:
def sub_array_sum(array,k=0):
start_index = -1
first_with_sum = {}
first_with_sum{0} = -1
best_start = -1
best_len = 0
current_sum = 0
for i in array:
start_index += 1
current_sum += i
if current_sum - k in first_with_sum:
if start_index - first_with_sum{current_sum - k} > best_len:
best_start = first_with_sum{current_sum - k} + 1
best_len = start_index - first_with_sum{current_sum - k}
else:
first_with_sum{current_sum} = start_index
if best_len > 0:
return array[best_start:best_start+best_len-1]
else:
print "No subarray found"
Setting first_with_sum{0} = -1 at the start means that we don't have to treat a range beginning at index 0 as a special case. Note that this algorithm doesn't improve on the asymptotic time or space complexity of your original one, but it's simpler to implement and will use a small amount less space on any input that contains a zero-sum subarray.
Here's my own answer, just for fun.
The number of subsequences is quadratic, and the time to sum a subsequence is linear, so the most naive solution would be cubic.
This approach is just an exhaustive search over the subsequences, but a little trickery avoids the linear summing factor, so it's only quadratic.
from collections import namedtuple
from itertools import chain
class Element(namedtuple('Element', ('index', 'value'))):
"""
An element in the input sequence. ``index`` is the position
of the element, and ``value`` is the element itself.
"""
pass
class Node(namedtuple('Node', ('a', 'b', 'sum'))):
"""
A node in the search graph, which looks like this:
0 1 2 3
\ / \ / \ /
0-1 1-2 2-3
\ / \ /
0-2 1-3
\ /
0-3
``a`` is the start Element, ``b`` is the end Element, and
``sum`` is the sum of elements ``a`` through ``b``.
"""
#classmethod
def from_element(cls, e):
"""Construct a Node from a single Element."""
return Node(a=e, b=e, sum=e.value)
def __add__(self, other):
"""The combining operation depicted by the graph above."""
assert self.a.index == other.a.index - 1
assert self.b.index == other.b.index - 1
return Node(a=self.a, b=other.b, sum=(self.sum + other.b.value))
def __len__(self):
"""The number of elements represented by this node."""
return self.b.index - self.a.index + 1
def get_longest_k_sum_subsequence(ints, k):
"""The longest subsequence of ``ints`` that sums to ``k``."""
n = get_longest_node(n for n in generate_nodes(ints) if n.sum == k)
if n:
return ints[n.a.index:(n.b.index + 1)]
if k == 0:
return []
def get_longest_zero_sum_subsequence(ints):
"""The longest subsequence of ``ints`` that sums to zero."""
return get_longest_k_sum_subsequence(ints, k=0)
def generate_nodes(ints):
"""Generates all Nodes in the graph."""
nodes = [Node.from_element(Element(i, v)) for i, v in enumerate(ints)]
while len(nodes) > 0:
for n in nodes:
yield n
nodes = [x + y for x, y in zip(nodes, nodes[1:])]
def get_longest_node(nodes):
"""The longest Node in ``nodes``, or None if there are no Nodes."""
return max(chain([()], nodes), key=len) or None
if __name__ == '__main__':
def f(*ints):
return get_longest_zero_sum_subsequence(list(ints))
assert f() == []
assert f(1) == []
assert f(0) == [0]
assert f(0, 0) == [0, 0]
assert f(-1, 1) == [-1, 1]
assert f(-1, 2, 1) == []
assert f(1, -1, 1, -1) == [1, -1, 1, -1]
assert f(1, -1, 8) == [1, -1]
assert f(0, 1, -1, 8) == [0, 1, -1]
assert f(5, 6, -2, 1, 1, 7, -2, 2, 8) == [-2, 1, 1]
assert f(5, 6, -2, 2, 7, -2, 1, 1, 8) == [-2, 1, 1]
I agree with sundar nataraj when he says that this must be posted to the code review forum.
For fun though I looked at your code. Though I am able to understand your approach, I fail to understand the need to use Counter.
best_index_hash[start_index] = [(0,start_index)] - Here best_index_hash is of the type Counter. Why are you assigning a list to it?
for key_1, value_1 in best_index_hash.most_common(1) - You trying to get largest subsequence and for that you are using most_common as the answer. This is not intuitive semantically.
I am tempted to post a solution but I will wait for you to edit the code snippet and improve it.
Addendum
For fun, I had a go at this puzzle and I present my effort below. I make no guarantees of correctness/completeness.
from collections import defaultdict
def max_sub_array_sum(a, s):
if a:
span = defaultdict(lambda : (0,0))
current_total = 0
for i in xrange(len(a)):
current_total = a[i]
for j in xrange (i + 1, len(a)):
current_total += a[j]
x,y = span[current_total]
if j - i > y - x:
span[current_total] = i,j
if s in span:
i, j = span[s]
print "sum=%d,span_length=%d,indices=(%d,%d),sequence=%s" %\
(s, j-i + 1, i, j, str(a[i:j + 1]))
return
print "Could not find a subsequence of sum %d in sequence %s" % \
(s, str(a))
max_sub_array_sum(range(-6, -1), 0)
max_sub_array_sum(None, 0)
max_sub_array_sum([], 0)
max_sub_array_sum(range(6), 15)
max_sub_array_sum(range(6), 14)
max_sub_array_sum(range(6), 13)
max_sub_array_sum(range(6), 0)
Here's the solution taken from LeetCode :
def sub_array_sum(nums, k=0):
count, sum = 0, 0
map = dict()
map[0] = 1
for i in range(len(nums)):
sum += nums[i]
if map.__contains__(sum - k):
count += map[sum - k]
map[sum] = map.get(sum, 0) + 1
return count

Categories

Resources