arrangements backtracking python - python

I have a hard time trying to figure out how to generate the arrangements via backtracking on python, it is something they asked us at university
A group of n (n<=10) persons, numbered from 1 to n are placed on a row
of chairs, but between every two neighbor persons some conflict of
interests appeared. Display all the possible modalities to replace the
persons, such that between any two persons in conflict stays one or at
most two other persons.
I managed to modify the code for the permutations and the queens but i don`t really know where to put the condition for example k is the number and k must be different from the previous number in the string +1 and must bee different from the next number+1
The list of person sitting on the chairs is 1 2 3 4 (is impossible for less then 3 persons)
one right solution will be 1 3 4 2 and 3 1 4 2
Here is the code:
class Permutations(Backtracking):
def __init__(self, n):
Backtracking.__init__(self, n)
def _init_value(self, k):
return 0
def _next_value(self, n, k, v):
if v < n:
return v + 1
return None
def _cond(self, k, possible, v):
if v is None:
return False
try:
possible[:k].index(v)
return False
except ValueError:
return True
def _solution(self, n, k, possible):
return k == n-1
def _handle_solution(self, n, k, possible):
print(possible)

def chairs(soln, i=0):
if i == len(soln):
yield tuple(soln)
for j in xrange(i, len(soln)):
if i == 0 or soln[j] not in (soln[i - 1] + 1, soln[i - 1] - 1):
soln[i], soln[j] = soln[j], soln[i]
for s in chairs(soln, i + 1):
yield s
soln[i], soln[j] = soln[j], soln[i]
print list(chairs(range(1, 5)))

Code:
def possible_solution(remaining, sol=None):
sol = sol or []
if not remaining:
yield sol
else:
for i, candidate in enumerate(remaining):
if not sol or abs(sol[-1] - candidate) != 1:
new_sol = sol + [candidate]
new_remaining = remaining[:i] + remaining[i+1:]
for x in possible_solution(new_remaining, new_sol):
yield x
Test code:
def possible_solutions(neighbors):
for solution in possible_solution(neighbors):
print solution
print '-' * 30
possible_solutions([1, 2, 3])
print '-' * 30
possible_solutions([1, 2, 3, 4])
print '-' * 30
possible_solutions([1, 2, 3, 4, 5])
Results:
------------------------------
------------------------------
[2, 4, 1, 3]
[3, 1, 4, 2]
------------------------------
[1, 3, 5, 2, 4]
[1, 4, 2, 5, 3]
[2, 4, 1, 3, 5]
[2, 4, 1, 5, 3]
[2, 5, 3, 1, 4]
[3, 1, 4, 2, 5]
[3, 1, 5, 2, 4]
[3, 5, 1, 4, 2]
[3, 5, 2, 4, 1]
[4, 1, 3, 5, 2]
[4, 2, 5, 1, 3]
[4, 2, 5, 3, 1]
[5, 2, 4, 1, 3]
[5, 3, 1, 4, 2]

Related

How do I count the number of a list appears in another list?

I have a list named A:
A = [1, 2, 1, 2, 1, 2, 3, 4, 5]
and I want to count the number of list [1, 2] appears in A.
It will be 3 because:
A = [1, 2, 1, 2, 1, 2, 3, 4, 5]
[1, 2][1, 2][1, 2] <- appears 3 times
Here is my code:
result = 0
while len(A) > 0:
temp, tA = [1, 2], A
for i in temp:
if len(tA) == 0: break
if i in tA: tA.pop(0)
result += 1
return result
And I wonder know is there any faster way to accomplish the same function?

Count the number of times the positions of two lists have the same element

If you have two lists,
a = [1, 2, 3, 4, 5]
b = [1, 3, 2, 4, 7]
how can you count the number of times elements at a certain position coincide? For example 1 and 4 in the above example would have 2 cases of elements coinciding.
sum(a_ == b_ for a_, b_ in zip(a, b))
zip can give you the elements that share a position, and you can use sum to count the number of times they match:
a = [1, 2, 3, 4, 5]
b = [1, 3, 2, 4, 7]
print(sum(x == y for x, y in zip(a, b))) # 2
You can use below code and you will get positions which coincide and get sum of them as well.
a = [1, 2, 3, 4, 5]
b = [1, 3, 2, 4, 7]
print(len([i for i,val in enumerate(zip(a,b)) if val[0]==val[1]]))
to get positions you can use
print([i for i,val in enumerate(zip(a,b)) if val[0]==val[1]])
one more version:
a = [1, 2, 3, 4, 5]
b = [1, 3, 2, 4, 7]
print(sum(a[i] == b[i] for i in range(len(a))))
How about this?
# lists:
a = [1, 2, 3, 4, 5]
b = [1, 3, 2, 4, 7]
# initialize variables:
number_of_collisions = 0
# iterate over each element:
for i in range(len(a)):
if a[i] == b[i]:
number_of_collisions += 1
print(number_of_collisions)

Slicing a list at integer of when an element is different from the previous

I need to be able to slice a list of points into multiple sublists, to act as a guide for slicing another list.
a = 1 # just an example
b = 2 # just an example
c = 3 # just an example
# My list right now
y_vals = [a, a, a, a, a, a, b, b, b, b, b, b, b, b, b, b, b, c, c, c, c, c]
and I need it to slice every time the number changes. a, b and c are actual numbers, but the numbers are rather long, so I typed it out in a,b,c.
I wanted to use the slicing method of [:x], but it's a list of 5000 over numbers, and I'm not sure how to slice a list. Thank you in advance!
If you simply want to get sublists with only the same number then don't bother with slicing. A good approach is itertools.groupby:
from itertools import groupby
li = [3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 2, 2]
for _, group in groupby(li):
print(list(group))
Outputs
[3, 3, 3, 3, 3, 3]
[1, 1, 1, 1, 1]
[2, 2]
EDIT getting from this to your required list of lists is a straightforward one-liner:
output = [list(group) for _, group in groupby(li)]
print(output)
Outputs
[[3, 3, 3, 3, 3, 3], [1, 1, 1, 1, 1], [2, 2]]
This algorithm isn't really beautiful, but it should work:
a = 1
b = 2
c = 3
y_vals = [a,a,a,a,a,a,b,b,b,b,c,c,c,c,c]
last_break = 0
for i in range(1, len(y_vals)):
if y_vals[i - 1] != y_vals[i]:
print(y_vals[last_break: i])
last_break = i
if i == len(y_vals) - 1:
print(y_vals[last_break: i + 1])
Result:
[1, 1, 1, 1, 1, 1]
[2, 2, 2, 2]
[3, 3, 3, 3, 3]
Edit: It will also work for lists like that:
y_vals = [1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,1,1,1]
The algorithm by #GotCubes won't.
A solution without slicing:
y_vals = [1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,1,1,1]
sub_list = []
for i in range(0, len(y_vals)):
if ((i < len(y_vals)-1 ) and (y_vals[i] == y_vals[i+1])):
sub_list.append(y_vals[i])
else:
sub_list.append(y_vals[i])
print(sub_list)
sub_list=[]
Output:
[1, 1, 1, 1, 1, 1]
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
[3, 3, 3, 3, 3]
[1, 1, 1]
#DeepSpace has the answer you're most likely looking for. However, if you're insistent on slicing, or otherwise getting the indices on which to slice, this might help:
# Six 1's, Eleven 2's, Five 3's
y_vals = [1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3]
split_points = [y_vals.count(v) for v in set(y_vals)]
print(split_points)
ind = 0
for i in split_points:
segment = y_vals[ind:ind+i]
ind = ind + i
print(segment)
Which gives you:
[6, 11, 5]
[1, 1, 1, 1, 1, 1]
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
[3, 3, 3, 3, 3]

Developing an indexing scheme for list of all non-decreasing sequences

Suppose that we are considering a sorted list of all non-decreasing sequences with values in the range (1, max_num) and num_slots elements in each sequence, how can I find the index of some given member sequence in O(1) time complexity? I am not actually given the entire list up-front, I just want to find the index of some member sequence were the list of all sequences to exist.
For a concrete example, suppose that max_num = 3 and num_slots = 4. Then there are 15 sequences (or generally, there are (max_num + num_slots - 1) choose (num_slots) sequences):
[[1, 1, 1, 1],
[1, 1, 1, 2],
[1, 1, 1, 3],
[1, 1, 2, 2],
[1, 1, 2, 3],
[1, 1, 3, 3],
[1, 2, 2, 2],
[1, 2, 2, 3],
[1, 2, 3, 3],
[1, 3, 3, 3],
[2, 2, 2, 2],
[2, 2, 2, 3],
[2, 2, 3, 3],
[2, 3, 3, 3],
[3, 3, 3, 3]]
So given inputs of a sequence like [1, 2, 2, 3] along with the information max_num = 3, I am trying to write a function that would return its correct index of 7. I do not actually have the list of all sequences to work with.
Background info
I have come up with an algorithm to generate all non-decreasing sequences I care about, but this doesn't seem completely relevant for generating the index of a particular member sequence without the whole list of sequences materialized.
def gen(max_num, num_slots, l = None):
if l is None:
l = [[1] * num_slots]
cur = l[-1].copy()
for i in reversed(range(num_slots)):
if cur[i] < max_num:
cur[i] += 1
for j in range(i+1, num_slots):
cur[j] = cur[i]
l.append(cur)
return gen(max_num, num_slots, l)
return l
This one is O(|seq| + max_num). Note that this is still much faster than the naive generate all and search approach, which is exponential in |seq|.
The idea is that you count the sequences before the input sequence. For example,
you want to know what's the index of [2, 4, 5, 6] when max_num = 6.
Count [1, *, *, *]
Count [2, 2, *, *]
Count [2, 3, *, *]
(Note: you cannot count [2, 4, *, *], because then you would include [2, 4, 6, 6] which comes after your input. You should always go until one less than your input at the given index)
Count [2, 4, 4, *]
Count [2, 4, 5, 5]
(for each row, you can use your formula, (max_num + num_slots - 1) choose (num_slots) and sum them up)
def combinations(slots, available):
return choose(slots + available - 1, slots)
def find_index(seq, max_num):
res = 0
for digit_index in xrange(len(seq)):
prev = seq[digit_index - 1] if digit_index > 0 else 1
for digit in xrange(prev, seq[digit_index]):
res += combinations(len(seq) - digit_index - 1, max_num - digit + 1)
return res
print find_index([1, 2, 2, 3], 3)
I'll elaborate on #DavidFrank's answer on why it is O(length+max_num) and give a more easily understand example (a bit more complex too).
To start with, observe:
Assume the total series possibility in F(length, max_num) = X
Then for all of possibilities in X that starts with 1, e.g. [1, ....], we have a count of F(length-1, max_num) within this group.
For all the possibility in X that does not start with 1, e.g. [2, ....] or [3, ....], we have a count of F(length, max_num-1).
Thus we can use recursion to get this in O(length*max_num) (can become O(length+max_num) if we use memoization) number of complexity:
# This calculate the total number of X of possible entry given (length, max_num)
def calc_sum(length, max_num):
if max_num == 1:
return 1
elif length == 1:
return max_num
else:
total = calc_sum(length-1, max_num) + calc_sum(length, max_num-1)
return total
Now we examine the result to see if we can make it O(1):
# This is clearly not going to make it O(1), so now we need some generalizations to NOT run this recursion.
import numpy as np
arr = np.zeros((6,6))
for i in range(6):
for j in range(6):
arr[i, j] = calc_sum(i+1, j+1)
print(arr)
The result is:
[[ 1. 2. 3. 4. 5. 6.]
[ 1. 3. 6. 10. 15. 21.]
[ 1. 4. 10. 20. 35. 56.]
[ 1. 5. 15. 35. 70. 126.]
[ 1. 6. 21. 56. 126. 252.]
[ 1. 7. 28. 84. 210. 462.]]
This is a pascal's triangle, if you look diagonally to the top right. The diagonals of pascal's triangle are defined by (x choose y)
This makes it clear that it cannot be O(1), and will at least be O(length+max_num) because this is the general complexity of (Choose) function.
We've went all the way to prove that an O(1) solution is impossible, unless we constrain (length + max_num) to be constant.
# We can expand by solving it now:
from scipy.special import comb # this is choose function.
def get_index(my_list, max_num):
my_list = np.array(my_list)
if len(my_list) == 1:
return my_list[0] - 1
elif my_list[0] == 1:
return get_index(my_list[1:], max_num)
elif my_list[0] != 1:
return get_index(my_list - 1, max_num - 1) + comb(len(my_list)-2+max_num, max_num-1)
get_index([1,2,2,3],3) # 7
The aggregated complexity of the final function with the comb() is still O(length + max_num) as the complexity of everything outside comb is O(length + max_num) as well.
There is bijection from the k-subsets of {1...n} (with repetition) to k-subsets of {1...n + k − 1} (without repetition) by mapping {c_0, c_1...c_(k−1)} to {c_0, c_(1+1), c_(2+2)...c_(k−1+k−1)} (see here).
Once converted, just use your favourite combination ranking utility.
[3, 3, 3, 3] --> [3, 4, 5, 6]
[2, 3, 3, 3] --> [2, 4, 5, 6]
[2, 2, 3, 3] --> [2, 3, 5, 6]
[2, 2, 2, 3] --> [2, 3, 4, 6]
[2, 2, 2, 2] --> [2, 3, 4, 5]
[1, 3, 3, 3] --> [1, 4, 5, 6]
[1, 2, 3, 3] --> [1, 3, 5, 6]
[1, 2, 2, 3] --> [1, 3, 4, 6]
[1, 2, 2, 2] --> [1, 3, 4, 5]
[1, 1, 3, 3] --> [1, 2, 5, 6]
[1, 1, 2, 3] --> [1, 2, 4, 6]
[1, 1, 2, 2] --> [1, 2, 4, 5]
[1, 1, 1, 3] --> [1, 2, 3, 6]
[1, 1, 1, 2] --> [1, 2, 3, 5]
[1, 1, 1, 1] --> [1, 2, 3, 4]
import pyncomb
def convert(m, S):
return (m + len(S) - 1, [ x-1 + i for x,i in zip(S, list(xrange(len(S)))) ])
def rank(m, S):
k, s = convert(m, S)
return pyncomb.ksubsetcolex.rank(k, s)
print rank(3, [1,2,2,3])
# 7
For each digit, find the difference between that and the lowest digit. Add 1 for each changed position to the right of any altered digit
idx = 0;
for i in range(0,num_slots):
d = SEQ[i]
idx += d-min_num
if (d > min_num):
idx += num_slots-1 - i
For example:
[1,1,1,3] is 0 + 0 + 0 + (2+0) or 2
[1,2,3,3] is 0 + (1+2) + (2+1) + (2+0) or 8
[3,3,3,3] is (2+3) + (2+2) + (2+1) + (2+0) or 14

Algorithm to return a reordered range for data rendering

Here are examples:
given: 1,2,3 [list or range of numbers]
return: 2,1,3 [reordered list]
given: 1,2,3,4,5
return: 3 1 5 2 4
given: 1,2,3,4,5,6,7
return: 4 1 7 2 6 3 5 OR 4 7 1 5 3 2 6 or similar
given: 1,2,4,5,6,7,8,9
return: 5,1,9,3,7,2,8,4,6 or similar
In rendering you start with the center, then the most extreme cases, and become more and more detailed. This is NOT random. I'm in python, but theres got to be a name for this in comp sci. Help appreciated.
Edit to add
even case -
given: 1234
return: 2,1,4,3 OR 3,1,4,2 OR 2,4,1,3 OR 3,4,1,2
A valid, although ungraceful solution:
def sigorder(lst):
result = []
l = len(lst)
if l <= 2:
return lst
if l > 2:
result.append(lst[l/2])
result.append(lst[0])
result.append(lst[-1])
right = sigord(lst[l/2+1:-1])
left = sigord(lst[1:l/2])
result.extend(slicezip(left, right))
return result
Inner, recursive function:
def sigord(lst):
result = []
if len(lst) < 3:
return lst
else:
l = len(lst)
result.append(lst[l/2])
left = sigord(lst[0:l/2])
right = sigord(lst[l/2 + 1:len(lst)])
result.extend(slicezip(left, right))
return result
slicezip() (Note: conveniently handles the potential unevenness of the left/right lists automagically)
def slicezip(a, b):
result = [0]*(len(a)+len(b))
result[::2] = a
result[1::2] = b
return result
Outputs for lists length 4-9 :
[3, 1, 4, 2]
[3, 1, 5, 2, 4]
[4, 1, 6, 2, 5, 3]
[4, 1, 7, 2, 5, 3, 6]
[5, 1, 8, 3, 6, 2, 7, 4]
[5, 1, 9, 3, 7, 2, 6, 4, 8]
This should do it:
def extreme_cases(upd_itrr, new_itrr):
new_itrr.append(min(upd_itrr))
new_itrr.append(max(upd_itrr))
upd_itrr.remove(min(upd_itrr))
upd_itrr.remove(max(upd_itrr))
if len(upd_itrr) >= 2:
extreme_cases(upd_itrr, new_itrr)
return upd_itrr, new_itrr
def reordered_range(itr):
new_itr = []
center = 0
if len(itr) % 2 != 0:
center = itr[len(itr) // 2]
elif len(itr) % 2 == 0:
center = itr[(len(itr) // 2) - 1]
new_itr.append(center)
upd_itr = itr[:]
upd_itr.remove(center)
upd_itr, new_itr = extreme_cases(upd_itr, new_itr)
if upd_itr:
new_itr.append(upd_itr[0])
return new_itr
print(reordered_range([1, 2, 3]))
print(reordered_range([1, 2, 3, 4]))
print(reordered_range([1, 2, 3, 4, 5]))
print(reordered_range([1, 2, 3, 4, 5, 6, 7]))
print(reordered_range([1, 2, 4, 5, 6, 7, 8, 9]))
Output:
[2, 1, 3]
[2, 1, 4, 3]
[3, 1, 5, 2, 4]
[4, 1, 7, 2, 6, 3, 5]
[5, 1, 9, 2, 8, 4, 7, 6]
Another solution:
import numpy as np
from copy import copy
def bisecting_order(lst):
# bisecting order of an unordered list
result = []
l = len(lst)
if l < 3:
return lst
result.append(closest(lst,np.mean(lst)))
result.append(min(lst))
result.append(max(lst))
# get bisections
while len(result)!=len(lst):
temp_list = copy(result)
temp_list.sort()
for i in xrange(len(temp_list)-1):
newnum = closest(lst,np.mean([temp_list[i],temp_list[i+1]]))
if newnum in result:
continue
else:
result.append(newnum)
return result
def closest(mylist,mynum):
return min(mylist, key=lambda x:abs(x-mynum))

Categories