Python combination with a twist - python

Assume I have following:
arr = [0, 0, 0], [20, 20, 90], [30, 30, 50], [40, 40, 80], [10, 75, 10], [100, 100, 0]
I build a combination from it
print(list(itertools.combinations(arr, 2)))
So I get a nice all combinations - only moving "forward" which is great:
[([0, 0, 0], [20, 20, 90]), ([0, 0, 0], [30, 30, 50]), ([0, 0, 0], [40, 40, 80]), ([0, 0, 0], [10, 75, 10]), ([0, 0, 0], [100, 100, 0]), ([20, 20, 90], [30, 30, 50]), ([20, 20, 90], [40, 40, 80]), ([20, 20, 90], [10, 75, 10]), ([20, 20, 90], [100, 100, 0]), ([30, 30, 50], [40, 40, 80]), ([30, 30, 50], [10, 75, 10]), ([30, 30, 50], [100, 100, 0]), ([40, 40, 80], [10, 75, 10]), ([40, 40, 80], [100, 100, 0]), ([10, 75, 10], [100, 100, 0])]
BUT there is a trick - I have to add all additional costs for the points I've skipped.
Say in the case of ([0, 0, 0], [40, 40, 80])
I have skipped these ones [20, 20, 90], [30, 30, 50]
For which have the additional accumulated cost of distance from 0,0,0 until current which is 140, which is adding the skipped's third values which is (50+90). These distances individually I would like to put into a list of tuples. Accessing the previous items in the list I've done below, but accumulating for each combination gave me the headache.
What data structure or evtl. hijacked combination algorithm would maintain the skipped previous additional costs still creating the combination for an n list?
So I thought about a more manual approach as well:
`def combination(given_list, possibilities):
penalty = 0
final_list = []
if possibilities == 0: return [[]]
for item in range(0, len(given_list)):
current_item = given_list[item]
remaining_from_given_list = given_list[item + 1:]
for prev in combination(remaining_from_given_list, possibilities -1):
penalty += previous_item[2]
final_list.append([current_item] + prev)
penalty = 0
return final_list
`
But this only gave me the previous penalties which then is not true for all cases (0 if I don't miss the previous, all the previous additional cost(s) which I have missed)
But not sure how to hijack the above to measure distance and get the accumulation for all previous values.
The final result would look like:
ways = [("0", "1", 0), ("0", "2", 90), ("0", "3", 140), ("0", "4", 220), ("0", "5", 230), ("1", "2", 0), ("1", "3", 50), ("1", "4", 130), ("1", "5", 140), ("2", "3", 0), ("2", "4", 80), ("2", "5", 900), ("3", "4", 0), ("3", "5", 10), ("4", "5", 0)]
The optimal path I am using a dijkstra to calculate, I have that part already and if I create the list of tuples manually like this, it works as expected.
I feel like I am missing something obvious - maybe I should use a dictionary to achieve this? I thought about a double linked list as well but that's likely not the best approach either.

Try an iterative approach:
arr = [[0, 0, 0], [20, 20, 90], [30, 30, 50], [40, 40, 80], [10, 75, 10], [100, 100, 0]]
ways = []
total = len(arr)
for i in range(total):
for j in range(i + 1, total):
ways.append((i, j, sum(i[-1] for i in arr[i+1:j])))
print(ways)
Returns:
[(0, 1, 0), (0, 2, 90), (0, 3, 140), (0, 4, 220),
(0, 5, 230), (1, 2, 0), (1, 3, 50), (1, 4, 130),
(1, 5, 140), (2, 3, 0), (2, 4, 80), (2, 5, 90),
(3, 4, 0), (3, 5, 10), (4, 5, 0)]

Related

Change Array starting at spefic index

my lists look something like this:
A_List= [0,1,2,3,4,5,6,7,8]
B_List=[0,10,20,30,40,50,60,70,80]
C_List=[0,100,200,300,400,500,600,700,800]
D_List = ...
And so on.
Each value is type np.float64.
The values here are random, I only wanted to show that they are all the same length.
I now tried to write a loop that changes these lists starting at a specific index in that way, that all numbers including and above index 4 are subtracted by the value written in index 4.
i=4
while i <= len(A_List):
A_List[i] = A_List[i]+A_List[4]
B_List[i] = B_List[i]+B_List[4]
C_List[i] = C_List[i]+C_List[4]
D_List[i] = D_List[i]+D_List[4]
...
i=i+1
Which just won't work. Error: can only concatenate str (not "numpy.float64") to str). I don't quite understand that, because I thought I'm substituting a float value with another float value.
Why don't you simply use numpy and not lists?
First create a 2D array:
A_List= [0,1,2,3,4,5,6,7,8]
B_List=[0,10,20,30,40,50,60,70,80]
C_List=[0,100,200,300,400,500,600,700,800]
a = np.c_[A_List, B_List, C_List]
array([[ 0, 0, 0],
[ 1, 10, 100],
[ 2, 20, 200],
[ 3, 30, 300],
[ 4, 40, 400],
[ 5, 50, 500],
[ 6, 60, 600],
[ 7, 70, 700],
[ 8, 80, 800]])
Then perform your subtraction:
>>> a-a[4]
array([[ -4, -40, -400],
[ -3, -30, -300],
[ -2, -20, -200],
[ -1, -10, -100],
[ 0, 0, 0],
[ 1, 10, 100],
[ 2, 20, 200],
[ 3, 30, 300],
[ 4, 40, 400]])
If you want to apply your transform only on rows ≥ 4:
mask = np.tile((np.arange(len(a))>=4), (a.shape[1], 1)).T
np.where(mask, a-a[4], a)
output:
array([[ 0, 0, 0],
[ 1, 10, 100],
[ 2, 20, 200],
[ 3, 30, 300],
[ 0, 0, 0],
[ 1, 10, 100],
[ 2, 20, 200],
[ 3, 30, 300],
[ 4, 40, 400]])

Generate K random numpy arrays given N variables and ranges

I have N variables (defining N-dimensional space), with their defined ranges (normally or uniformly distributed in the given range):
each variable is defined by a list containing its possible integer values.
v1_range = [1, 2, 3, 4, 5, 6]
v2_range = [10, 20, 30, 40, 50, 60]
v3_range = [100, 200, 300, 400, 500]
v4_range = [15, 16, 17, 18]
i want to generate K lists (numpy arrays/vectors..), each list contains one random number from the given ranges, with no duplicates.
so i-th element in each list, is selected from i-th list of ranges.
example (K = 6, from given ranges):
output = [
[1, 20, 300, 15],
[5, 10, 200, 15],
[3, 60, 100, 18],
[4, 30, 200, 16],
[4, 40, 100, 17],
[2, 30, 400, 15],
]
What is the best way to do so,using numpy.random() or scipy assuming the N-dimensional space could be very large (~13 lists of ranges, each containing ~20 values) and no duplicates are allowed ?
[EDIT]
duplicate entries in the ranges lists are fine, but i don't want to have two duplicate/exact same lists in the resulting list of lists e.g.:
indices 0, 2 are duplicated...
res = [[ 4, 40, 400, 18],
[ 4, 60, 400, 17],
[ 4, 40, 400, 18]]
Will simple iteration work? I think for 13 lists x 20 values it will work just fine.
import random
d = {
"v1_range" : [1, 2, 3, 4, 5, 6],
"v2_range" : [10, 20, 30, 40, 50, 60],
"v3_range" : [100, 200, 300, 400, 500],
"v4_range" : [15, 16, 17, 18]
}
def give(k):
out = [[] for i in range(k)]
for l in out:
for i in d.keys():
l.append(random.choice(d[i]))
return out
Which gives, for example:
>>> give(6)
[[5, 50, 500, 15], [1, 60, 400, 16], [5, 20, 200, 15], [1, 40, 400, 18], [2, 20, 100, 15], [6, 20, 400, 16]]
Or, with numpy
>>> import numpy as np
>>> np.array(give(6))
array([[ 4, 40, 400, 18],
[ 4, 60, 400, 17],
[ 5, 20, 400, 17],
[ 4, 10, 300, 16],
[ 5, 10, 300, 16],
[ 4, 30, 500, 18]])

Balance two lists till their sums are equal & with min swaps within the two lists in Python

a = [70, 30, 33, 23, 4, 4, 34, 95]
b = [50, 10, 10, 7]
I've tried this, but i know this is not accurate enough
if sum(a) > sum(b):
a.sort()
b.sort()
temp = [int(i) for i in a]
i=0
while(sum(b) <= sum(temp) and (i <= len(a) - 1)):
b.append(a[i])
temp.remove(a[i])
i = i+1
a = [int(i) for i in temp]
if sum(b) > sum(a):
a.sort()
b.sort()
temp = [int(i) for i in b]
i=0
while(sum(a) <= sum(temp) and (i <= len(b) - 1)):
a.append(b[i])
temp.remove(b[i])
i = i+1
b = [int(i) for i in temp]
Result was:
sums = 186, 184
lists = [7, 10, 70, 95, 4], [4, 10, 23, 30, 33, 34, 50]
Required Answer :
sums = 185, 185
lists = [7, 10, 23, 50, 95], [4, 4, 10, 30, 33, 34, 70]
The problem you are trying to solve is called the subset sum problem and is NP complete, so you are not going to get much better than brute force search.
This at least gives a solution using brute force:
from itertools import chain, combinations
def powerset(iterable):
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
def lst_difference(lst, other):
s = list(lst)
for el in other:
s.remove(el)
return s
a = [70, 30, 33, 23, 4, 4, 34, 95]
b = [50, 10, 10, 7]
lst = a + b
total = sum(lst)
for subset in powerset(lst):
if sum(subset) == total // 2:
other_subset = lst_difference(lst, subset)
print('subset1 = {}, subset2 = {}'.format(subset, other_subset))
Which gives these solutions:
subset1 = (70, 95, 10, 10), subset2 = [30, 33, 23, 4, 4, 34, 50, 7]
subset1 = (30, 95, 50, 10), subset2 = [70, 33, 23, 4, 4, 34, 10, 7]
subset1 = (30, 95, 50, 10), subset2 = [70, 33, 23, 4, 4, 34, 10, 7]
subset1 = (33, 23, 34, 95), subset2 = [70, 30, 4, 4, 50, 10, 10, 7]
subset1 = (33, 95, 50, 7), subset2 = [70, 30, 23, 4, 4, 34, 10, 10]
subset1 = (30, 33, 23, 4, 95), subset2 = [70, 4, 34, 50, 10, 10, 7]
subset1 = (30, 33, 23, 4, 95), subset2 = [70, 4, 34, 50, 10, 10, 7]
subset1 = (23, 95, 50, 10, 7), subset2 = [70, 30, 33, 4, 4, 34, 10]
subset1 = (23, 95, 50, 10, 7), subset2 = [70, 30, 33, 4, 4, 34, 10]
subset1 = (70, 23, 4, 4, 34, 50), subset2 = [30, 33, 95, 10, 10, 7]
subset1 = (30, 33, 95, 10, 10, 7), subset2 = [70, 23, 4, 4, 34, 50]
subset1 = (70, 30, 33, 4, 4, 34, 10), subset2 = [23, 95, 50, 10, 7]
subset1 = (70, 30, 33, 4, 4, 34, 10), subset2 = [23, 95, 50, 10, 7]
subset1 = (70, 4, 34, 50, 10, 10, 7), subset2 = [30, 33, 23, 4, 95]
subset1 = (70, 4, 34, 50, 10, 10, 7), subset2 = [30, 33, 23, 4, 95]
subset1 = (70, 30, 23, 4, 4, 34, 10, 10), subset2 = [33, 95, 50, 7]
subset1 = (70, 30, 4, 4, 50, 10, 10, 7), subset2 = [33, 23, 34, 95]
subset1 = (70, 33, 23, 4, 4, 34, 10, 7), subset2 = [30, 95, 50, 10]
subset1 = (70, 33, 23, 4, 4, 34, 10, 7), subset2 = [30, 95, 50, 10]
subset1 = (30, 33, 23, 4, 4, 34, 50, 7), subset2 = [70, 95, 10, 10]
Some numbers need to move from a to b, and some numbers need to move from b to a. The set of numbers that move is a subset of the set of all numbers from both lists. In your example, there are only 12 numbers, which means 2**12 == 4096 subsets. As it is a small number, a brute force approach should succeed.
Well, here's my take on this. As the others said, this problem cannot be solved in polynomial time, so this gets significantly slow when each list needs to give multiple items to the other. Anyway, here is the code:
from itertools import combinations
from collections import Counter
a = [70, 30, 33, 23, 4, 4, 34, 95]
b = [50, 10, 10, 7]
def balance(small, big):
small.sort()
big.sort()
diff = sum(big) - sum(small)
if diff < 0:
small, big = big, small
stack = {(0, 1)} # each element is (# of elements to take from small, # of elements to take from big)
while stack:
i, j = sorted(stack, key=lambda t: t[0]+t[1]).pop(0)
stack.remove((i, j))
diff = sum(big) - sum(small)
if i is 0:
matches = [(Counter(), Counter(x)) for x in combinations(big, j) if diff - 2 * sum(x) is 0]
else:
matches = [(Counter(x), Counter(y)) for x in combinations(small, i) for y in combinations(big, j) if
diff - 2 * sum(y) + 2 * sum(x) is 0]
if matches:
return list((Counter(small) - matches[0][0] + matches[0][1]).elements()), \
list((Counter(big) - matches[0][1] + matches[0][0]).elements())
else:
if sum(big[-j:]) * 2 - sum(small[-i:]) > diff and i + 1 < len(small):
stack.add((i + 1, j))
if sum(big[:j]) * 2 - sum(small[:i]) < diff and j + 1 < len(big):
stack.add((i, j + 1))
balance(a, b) returns ([7, 10, 50, 23, 95], [4, 4, 30, 33, 34, 70, 10])

How to sort only few values inside a list in Python

Suppose
A = [9, 5, 34, 33, 32, 31, 300, 30, 3, 256]
I want to sort only a particular section in a list. For example, here I want to sort only [300, 30, 3] so that overall list becomes:
A = [9, 5, 34, 33, 32, 31, 3, 30, 300, 256]
Suppose B = [300, 30, 400, 40, 500, 50, 600, 60] then after sorting it should be B = [30, 300, 40, 400, 50, 500, 60, 600].
Main idea if the leftmost digit is same 300, 30, 30 and right most digits contain only zeros then we should arrange it in increasing order.
Another example:
A = [100, 10, 1, 2000, 20, 2]
After sorting it should be A = [1, 10, 100, 2, 20, 2000]
Could anyone suggest some techniques to approach such issue. The values in my list will always be arranged in this way [200, 20, 2, 300, 30, 3, 400, 40, 4].
Code:
nums = [3, 30, 31, 32, 33, 34, 300, 256, 5, 9]
nums = sorted(nums, key=lambda x: str(x), reverse=True)
print nums
>> [9, 5, 34, 33, 32, 31, 300, 30, 3, 256]
But my final output should be [9, 5, 34, 33, 32, 31, 3, 30, 300 256].
Here is a big example:
A = [9, 5, 100, 10, 30, 3, 265, 200, 20, 2]
After sorting it should be:
A = [9, 5, 10, 100, 3, 30, 265, 2, 20, 200]
Since each expected sequence is contains the numbers which are a common coefficient of power of then, You can use a scientific_notation function which returns the common coefficient.Then you can categorize your numbers based on this function and concatenate them.
>>> from operator import itemgetter
>>> from itertools import chain,groupby
>>> def scientific_notation(number):
... while number%10 == 0:
... number = number/10
... return number
>>> A = [9, 5, 34, 33, 32, 31, 300, 30, 3, 256]
>>> G=[list(g) for _,g in groupby(A,key=scientific_notation)]
>>> list(chain.from_iterable(sorted(sub) if len(sub)>1 else sub for sub in G))
[9, 5, 34, 33, 32, 31, 3, 30, 300, 256]
Note that since we are categorizing the numbers based on coefficient of power of then if the length of a sub-list be more than 1 means that it's a sequence of expected numbers which need to be sort.So instead of checking the length of each sequence you can simply apply the sort on all the generators in group by:
>>> list(chain.from_iterable(sorted(g) for _,g in groupby(A,key=scientific_notation)))
[9, 5, 34, 33, 32, 31, 3, 30, 300, 256]
Really got twisted with this one , still don't know if it would work for all cases.
nums = [3, 30, 31, 32, 33, 34, 300, 256, 5, 9]
map(lambda x: x[1],sorted(zip(range(0,len(nums)),sorted(nums, key=lambda x: str(x), reverse=True)), key=lambda x: str(x[1]) if not str(x[1]).endswith('0') else str(str(x[1]-1)[0])+str(x[0]), reverse=True))
O/P : [9, 5, 34, 33, 32, 31, 3, 30, 300, 256]
Now I guess it would work with everything:
Earlier one wouldn't work with this nums = [30, 3, 31, 32, 33, 34, 330, 256, 5, 9]
nums = [30, 3, 31, 32, 33, 34, 330, 256, 5, 9]
map(lambda x: x[1],sorted(zip(range(0,len(nums)),sorted(nums, key=lambda x: str(x), reverse=True)), key=lambda x: str(x[1]) if not str(x[1]).endswith('0') else str(int(str(x[1]).strip("0"))-1)+str(x[0]), reverse=True))
O/P: [9, 5, 34, 33, 330, 32, 31, 3, 30, 256]
Here what I have done is taking your sorted list function , applied a zip after enumerating it So I would get [(0, 9), (1, 5), (2, 34), (3, 33), (4, 32), (5, 31), (6, 300), (7, 30), (8, 3), (9, 256)] for your input then if an element endswith a zero I would remove trailing zeros and subtract 1 and then convert it to string and append sorted index to the string which would give me a sorted list as per your case.
For e.g
Step one : zip index [(0, 9), (1, 5), (2, 34), (3, 33), (4, 32), (5, 31), (6, 300), (7, 30), (8, 3), (9, 256)]
Step two : strip trailing zeros [(0, 9), (1, 5), (2, 34), (3, 33), (4, 32), (5, 31), (6, 3), (7, 3), (8, 3), (9, 256)]
Step three : subtract 1 from those elements which had trailing zeros [(0, 9), (1, 5), (2, 34), (3, 33), (4, 32), (5, 31),(6, 3-1), (7, 3-1) (8, 3), (9, 256)]
Step four : if had trailing zeros sort them by reversed index ie first value in tuple
[(0, 9), (1, 5), (2, 34), (3, 33), (4, 32), (5, 31), (8, 3), (7, 2), (6, 2), (9, 256)]
Step five : get sorted [9, 5, 34, 33, 32, 31, 3, 30, 300, 256]
OR :
More correct and Simpler solution :
sorted(map(lambda x : str(x),[9, 5, 100, 10, 30, 3, 265, 200, 20, 2]),key=lambda x : x.strip("0")+x.rjust(len(x)+(len(x)-len(x.strip("0"))),'0'),reverse=True)
You can accomplish this by padding with zeros:
>>> nums = [3, 30, 31, 32, 33, 34, 300, 256, 5, 9]
>>> sorted(nums, key=lambda x: str(x).ljust(10,' '), reverse=True)
[9, 5, 34, 33, 32, 31, 3, 30, 300, 256]
EDIT: Change '0' in just to ' ' so that it comes lexicographically before '0' and does not compare equal with it (that it worked was a bit lucky, I think).
This solution is not exactly what you ask for, but may help:
nums = [3, 30, 31, 10, 100, 32, 33, 1, 34, 300, 256, 5, 9]
sorted(nums, key=lambda x: str((x*10 in nums or x/10. in nums)*x))
> [31, 32, 33, 34, 256, 5, 9, 1, 10, 100, 3, 30, 300]
This is generic with any continous numbers.
import re
k=[300, 30, 400, 40, 500, 50, 600, 60]
i=0
j=0
t1=[]
t2=[]
while i < len(k):
a=re.match(r"(\d)0*$|",str(k[i])).group(1)
j=i+1
t2.append(k[i])
while a and j<len(k):
b=re.match(a+r"0*$",str(k[j]))
if b:
t2.append(k[j])
j=j+1
else:
break
if len(t2)>1:
t2.sort()
[t1.append(m) for m in t2]
t2=[]
i=j-1
else:
t1.append(k[i])
t2=[]
i=i+1
print t1
Output:[30, 300, 40, 400, 50, 500, 60, 600]
how to accomplish:
1) try comparing list[n] with list[n+1] when % of list[n+1]%list[n]==0(remainder)
2) if yes, interchange their index numbers
You are done

Sorting by value in a dictionary if the value is a list

I know that dictionaries aren't sortable, that being said, I want the representation of the dictionary that is sorted upon that value which is a list. Example:
some_dict= {
"a": [0, 0, 0, 0, 0],
"b": [1400, 50, 30, 18, 0],
"c": [800, 30, 14, 14, 0],
"d": [5000, 100, 30, 50, .1],
"for fun": [140000, 1400, 140, 140, .42]
}
I want to sort by the first item in the list of each.
I tried something like:
sorted(some_dict.items(), key = lambda for x in some_dict: some_dict[x][0])
but I get invalid syntax.
Thanks in advance.
You're on the right track, but you can't put a for-loop in a lambda (hence the error):
>>> sorted(some_dict.items(), key=lambda x: x[1][0])
[('a', [0, 0, 0, 0, 0]), ('c', [800, 30, 14, 14, 0]), ('b', [1400, 50, 30, 18, 0]), ('d', [5000, 100, 30, 50, 0.1]), ('for fun', [140000, 1400, 140, 140, 0.42])]
If you want to keep this order in a dictionary, you can use collections.OrderedDict:
>>> from collections import OrderedDict
>>> mydict = OrderedDict(sorted(some_dict.items(), key=lambda x: x[1][0]))
>>> print(mydict)
OrderedDict([('a', [0, 0, 0, 0, 0]), ('c', [800, 30, 14, 14, 0]), ('b', [1400, 50, 30, 18, 0]), ('d', [5000, 100, 30, 50, 0.1]), ('for fun', [140000, 1400, 140, 140, 0.42])])
>>> print(mydict['a'])
[0, 0, 0, 0, 0]
lambdas are anonymous functions, but they can only execute expressions in Python:
lambda pair: pair[1][0]
You could also write it verbosely:
def sorting_func(pair):
key, value = pair
return value[0]
sorted(some_dict.items(), key=sorting_func)

Categories