I have a list of numbers (example: [-1, 1, -4, 5]
) and I have to remove numbers from the list without changing the total sum of the list. I want to remove the numbers with biggest absolute value possible, without changing the total, in the example removing [-1, -4, 5]
will leave [1]
so the sum doesn't change.
I wrote the naive approach, which is finding all possible combinations that don't change the total and see which one removes the biggest absolute value. But that is be really slow since the actual list will be a lot bigger than that.
Here's my combinations code:
from itertools import chain, combinations
def remove(items):
all_comb = chain.from_iterable(combinations(items, n+1)
for n in xrange(len(items)))
biggest = None
biggest_sum = 0
for comb in all_comb:
if sum(comb) != 0:
continue # this comb would change total, skip
abs_sum = sum(abs(item) for item in comb)
if abs_sum > biggest_sum:
biggest = comb
biggest_sum = abs_sum
return biggest
print remove([-1, 1, -4, 5])
It corectly prints (-1, -4, 5)
. However I am looking for some clever, more efficient solution than looping over all possible item combinations.
Any ideas?
How do I remove integers from a list? We can remove integers or digits from the list by using python function isinstance(). By using this method, we can get items or elements which does not satisfies the function condition.
In Python, an inbuilt function sub() is present in the regex module to delete numbers from the Python string. The sub() method replaces all the existences of the given order in the string using a replacement string.
if you redefine the problem as finding a subset whose sum equals the value of the complete set, you will realize that this is a NP-Hard problem, (subset sum)
so there is no polynomial complexity solution for this problem .
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright © 2009 Clóvis Fabrício Costa
# Licensed under GPL version 3.0 or higher
def posneg_calcsums(subset):
sums = {}
for group in chain.from_iterable(combinations(subset, n+1)
for n in xrange(len(subset))):
sums[sum(group)] = group
return sums
def posneg(items):
positive = posneg_calcsums([item for item in items if item > 0])
negative = posneg_calcsums([item for item in items if item < 0])
for n in sorted(positive, reverse=True):
if -n in negative:
return positive[n] + negative[-n]
else:
return None
print posneg([-1, 1, -4, 5])
print posneg([6, 44, 1, -7, -6, 19])
It works fine, and is a lot faster than my first approach. Thanks to Alon for the wikipedia link and ivazquez|laptop on #python irc channel for a good hint that led me into the solution.
I think it can be further optimized - I want a way to stop calculating the expensive part once the solution was found. I will keep trying.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With