Implementation of Depth-First-Search on a permutation tree in Python - python-3.x

I have a quadratic Matrix of size n, say A, with non-negative real entries a_ij.
Furthermore I have a permutation tree. For n = 3 it looks like this: .
Now I would like to do a Depth-search (I don't know really, whether "Depth-search" is the correct description for this, but let's use it for now) along the branches of the tree in the following way:
On the first partial tree on the very left do the following starting with an "empty" Permutation (x,x,x):
If a_12 > a_21 set (1,2,x) and then check whether a_23 > a_32. If this is true as well, save (1,2,3) in a list, say P. Then go back to the first Level and check whether a_13 > a_31 and so on.
If a_21 > a_12 or a_32 > a_23 do not save the Permutation in P and go back to the first Level and check whether a_13 > a_31. If this is true set (1,3,x) and then check whether a_23 > a_32. If this is true save (1,3,2) in P and continue with the next partial tree. If a_31 > a_13 or a_32 > a_23 do not save the Permutation in P and continue with the same procedure for the next partial tree.
This procedure/algorithm I would like to implement for an arbitrary natural n > 0 with Input just the Matrix A and n and as an Output all permutations of size n that fullfill these conditions. By now I am not able to implement this in a general way.
Preferably in Python, but Pseudo Code would be nice as well. I also want to avoid functions like "itertools Permutation", because in the use case I Need to apply this for large n, for example n = 100, and then itertools Permutation is very slow.

If I understand correctly, this should get you what you want:
import numpy as np
from itertools import permutations
def fluboxing_permutations(a, n):
return [p for p in permutations(range(n))
if all(a[i, j] > a[j, i] for i, j in zip(p, p[1:]))]
n = 3
a = np.random.random([n, n])
fluboxing_permutations(a, n)
itertools.permutations will yield permutations in lexicographical order, which corresponds to your tree; then we check that for each consecutive pair of indices in the permutation, the element in the matrix is greater than the element at swapped indices. If so, we retain the permutation.
(No idea how to describe what the function does, so I made a new name. Hope you like it. If anyone knows a better way to describe it, please edit! :P )
EDIT: Here's a recursive function that should do the same, but with pruning:
def fluboxing_permutations_r(a, n):
nset = set(range(n))
def inner(p):
l = len(p)
if l > 1 and a[p[-2]][p[-1]] <= a[p[-1]][p[-2]]:
return []
if l == n:
return [p]
return [r for i in nset - set(p)
for r in inner(p + (i,))]
return inner(())
p starts as empty tuple, but it grows in recursion. Once there's at least two elements in the partial permutation, we can test the last two elements and see if it fails the test, and reject it if it does (pruning its subtree out of the search space). If it is a full permutation that wasn't rejected, we return it. If it's not full yet, we append to it all possible indices that are not already in there, and recurse.
tinyEDIT: BTW, parameter n is kind of redundant, because n = len(a) at the top of the function should take care of it.

Related

Number of sub sequences of length K having total sum S, given 2d array

I wish to find Number of sub sequences of length K having total sum S, given an array.
Sample Input:
a=[1,1,1,2,2] & K=2 & S=2
Sample Output:
3 {because a[0],a[1]; a[1]a[2]; a[0]a[2] are only three possible for the case}
I have tried to write a recursive loop in Python for starter but it isn't giving output as expected.Please can you help me find a loophole I might be missing on.
def rec(k, sum1, arr, i=0):
#print('k: '+str(k)+' '+'sum1: '+str(sum1)) #(1) BaseCase:
if(sum1==0 and k!=0): # Both sum(sum1) required and
return 0 # numbers from which sum is required(k)
if(k==0 and sum1 !=0): # should be simultaneously zero
return 0 # Then required subsequences are 1
if(k==0 and sum1==0 ): #
return 1 #
base_check = sum1!=0 or k!=0 #(2) if iterator i reaches final element
if(i==len(arr) and base_check): # in array we should return 0 if both k
return 0 # and sum1 aren't zero
# func rec for getting sum1 from k elements
if(sum1<arr[0]): # takes either first element or rejects it
ans=rec(k-1,sum1,arr[i+1:len(arr)],i+1) # so 2 cases in else loop
print(ans) # i is taken in as iterator to provide array
else: # input to rec func from 2nd element of array
ans=rec(k-1, sum1-arr[0], arr[i+1:len(arr)],i+1)+rec(k, sum1, arr[i+1:len(arr)],i+1)
#print('i: '+str(i)+' ans: '+str(ans))
return(ans)
a=[1,1,1,2,2]
print(rec(2,2,a))
I am still unable to process how to make changes. Once this normal recursive code is written I might go to DP approach accordinlgy.
Using itertools.combinations
Function itertools.combinations returns all the subsequences of a given lengths. Then we filter to keep only subsequences who sum up to the desired value.
import itertools
def countsubsum(a, k, s):
return sum(1 for c in itertools.combinations(a,k) if sum(c)==s)
Fixing your code
Your code looks pretty good, but there are two things that appear wrong about it.
What is this if for?
At first I was a bit confused about if(sum1<arr[0]):. I think you can (and should) always go to the else branch. After thinking about it some more, I understand you are trying to get rid of one of the two recursive calls if arr[0] is too large to be taken, which is smart, but this makes the assumption that all elements in the array are nonnegative. If the array is allowed to contain negative numbers, then you can include a large a[0] in the subsequence, and hope for a negative element to compensate. So if the array can contain negative numbers, you should get rid of this if/else and always execute the two recursive calls from the else branch.
You are slicing wrong
You maintain a variable i to remember where to start in the array; but you also slice the array. Pretty soon your indices become wrong. You should use slices, or use an index i, but not both.
# WRONG
ans=rec(k-1, sum1-arr[0], arr[i+1:len(arr)],i+1)+rec(k, sum1, arr[i+1:len(arr)],i+1)
# CORRECT
ans = rec(k-1, sum1-arr[i], arr, i+1) + rec(k, sum1, arr, i+1)
# CORRECT
ans = rec(k-1, sum1-arr[0], arr[1:]) + rec(k, sum1, arr[1:])
To understand why using both slicing and an index gives wrong results, run the following code:
def iter_array_wrong(a, i=0):
if (a):
print(i, a)
iter_array_wrong(a[i:], i+1)
def iter_array_index(a, i=0):
if i < len(a):
print(i, a)
iter_array_index(a, i+1)
def iter_array_slice(a):
if a:
print(a)
iter_array_slice(a[1:])
print('WRONG')
iter_array_wrong(list(range(10)))
print()
print('INDEX')
iter_array_index(list(range(10)))
print()
print('SLICE')
iter_array_slice(list(range(10)))
Also note that a[i:len(a)] is exactly equivalent to a[i:] and a[0:j] is equivalent to a[:j].
Clean version of the recursion
Recursively count the subsequences who use the first element of the array, and the subsequences who don't use the first element of the array, and add the two counts. To avoid explicitly slicing the array repeatedly, which is an expensive operation, we keep a variable start to remember we are only working on subarray a[start:].
def countsubsum(a, k, s, start=0):
if k == 0:
return (1 if s == 0 else 0)
elif start == len(a):
return 0
else:
using_first_element = countsubsum(a, k-1, s-a[start], start+1)
notusing_first_elem = countsubsum(a, k, s, start+1)
return using_first_element + notusing_first_elem

Execution timed out (12000ms) kata Generate Numbers from Digits #2 on Code Wars (Python)

Could you give me a hint where the time consuming part of this code is?
It's my temporary solutions for the kata Generate Numbers from Digits #2 from codewars.com.
Thanks!
from collections import Counter
from itertools import permutations
def proc_arrII(arr):
length = Counter(arr).most_common()[-1][1]
b = [''.join(x) for x in list(set(permutations(arr,length)))]
max_count = [max(Counter(x).values()) for x in b]
total = 0
total_rep = 0
maximum_pandigit = 0
for i in range(len(b)):
total+=1
if max_count[i] > 1:
total_rep+=1
elif int(b[i]) > maximum_pandigit:
maximum_pandigit = int(b[i])
if maximum_pandigit == 0:
return([total])
else:
return([total,total_rep,maximum_pandigit])
When posting this,
it would have been helpful to offer example input,
or link to the original question,
or include some python -m cProfile output.
Here is a minor item, it inflates the running time very very slightly.
In the expression [''.join(x) for x in list(set(permutations(arr, length)))]
there's no need to call list( ... ).
The join just needs an iterable, and a set works fine for that.
Here is a bigger item.
permutations already makes the promise that
"if the input elements are unique, there will be no repeat values in each permutation."
Seems like you want to dedup (with set( ... )) on the way in,
rather than on the way out,
for an algorithmic win -- reduced complexity.
The rest looks nice enough.
You might try benching without the elif clause,
using the expression max(map(int, b)) instead.
If there's any gain it would only be minor,
turning O(n) into O(n) with slightly smaller coefficient.
Similarly, you should just assign total = len(b) and be done with it,
no need to increment it that many times.

Not showing output python, no error showing

Let us consider polynomials in a single variable x with integer coefficients: for instance, 3x^4 - 17x^2 - 3x + 5. Each term of the polynomial can be represented as a pair of integers (coefficient,exponent). The polynomial itself is then a list of such pairs.
We have the following constraints to guarantee that each polynomial has a unique representation:
Terms are sorted in descending order of exponent
No term has a zero coefficient
No two terms have the same exponent
Exponents are always nonnegative
For example, the polynomial introduced earlier is represented as
[(3,4),(-17,2),(-3,1),(5,0)]
The zero polynomial, 0, is represented as the empty list [], since it has no terms with nonzero coefficients.
Write Python functions for the following operations:
addpoly(p1,p2) ?
def addpoly(p1,p2):
p1=[]
p2=[]
for i in range(0,len(p1)):
for j in range(0,len(p2)):
L=[]
if p1[i][1]==p2[j][1]:
L=L[p1[i][0]+p2[j][0]][p1[i][1]]
elif p1[i][1]!=p2[j][1]:
L=L+p1[i][j]
L=L+p2[i][j]
print("L")
You are reassigning the p1 and p2 arguments to empty lists at the top of your function. This means you will always be checking for i in range(0, 0), which is an empty range. In other words, nothing in your loop will be executed. This is why you are not seeing any output. You are not seeing any error messages, because there is nothing wrong with your syntax, the problem is with the logic.
My math skills are nonexistent, so I cannot comment on the accuracy of most of the logic in your code, but for sure you need to get rid of the first two lines of your function (p1 = [] and p2 = []) or your function will do nothing.
Also, make sure to print the variable L rather than the string "L" to print your list:
print(L)
try this code
def addpoly(p1,p2):
L=[]
for i in range(0,len(p1)):
for j in range(0,len(p2)):
if p1[i][1] == p2[j][1] and p1[i][0]+p2[j][0] != 0 :
L.append((p1[i][0]+p2[j][0],p1[i][1]))
elif p1[i][1] == p2[j][1] and p1[i][0]+p2[j][0] == 0 :
pass
elif i == j:
L.append(p2[i])
L.append(p1[i])
return (L)

A Pythonic approach to a list comparison/generation script

Consider I've got a list of 2-tuples named listuple and another simple list named list0. I want to generate a list of 1s and -1s based on comparing my two given lists.
def Vectomparison (listuple, list0):
result = []
for EachElement in listuple:
if EachElement [0] in list0:
result.append (1)
else:
result.append (-1)
return result
But I really think that this not a Pythonic approach. Any idea for making this Pythonically more compressed?
A direct rewrite using a list comprehesion, and ternary operator:
def Vectomparison (listuple, list0):
return [1 if item[0] in list0 else -1 for item in listuple]
Note that item[0] in list0 uses a linear search if list0 is a list. That makes the algorithms time complexity O(N*M) where N = len(listuple), M = len(list0)
You can make it faster:
def Vectomparison (listuple, list0):
set0 = set(list0)
return [1 if item[0] in set0 else -1 for item in listuple]
This version has a time complexity of O(N+M)

Haskell not in scope list comprehension

all_nat x = [ls| sum ls == x]
I'd like to write a function that given an integer x it returns all the lists that the result of their elements when summed is the integer x but I always get the error "not in scope: 'ls' " for both times it apperas. I'm new to haskell. What's the syntax error here?
The problem is that you need to define all used variables somewhere, but ls is undefined. Moreover, it can't be defined automatically, because the compiler doesn't know about the task — how the list should be generated? Ho long can it be? Are terms positive or not, integral or not? Unfortunately your code definition of the problem is quite vague for modern non-AI languages.
Let's help the compiler. To solve such problems, it's often useful to involve some math and infer the algorithm inductively. For example, let's write an algorithm with ordered lists (where [2,1] and [1,2] are different solutions):
Start with a basis, where you know the output for some given input. For example, for 0 there is only an empty list of terms (if 0 could be a term, any number could be decomposed as a sum in infinitely many ways). So, let's define that:
allNats 0 = [[]] --One empty list
An inductive step. Assuming we can decompose a number n, we can decompose any number n+k for any positive k, by adding k as a term to all decompositions of n. In other words: for numbers greater than 0, we can take any number k from 1 to n, and make it the first term of all decompositions of (n­-k):
allNats n = [ k:rest --Add k as a head to the rest, where
| k <- [1 .. n] --k is taken from 1 to n, and
, rest <- allNats (n - k)] --rest is taken from solutions for (n—k)
That's all! Let's test it:
ghci> allNat 4
[[1,1,1,1],[1,1,2],[1,2,1],[1,3],[2,1,1],[2,2],[3,1],[4]]
Let's break this up into two parts. If I've understood your question correctly, the first step is to generate all possible (sub)lists from a list. There's a function to do this, called subsequences.
The second step is to evaluate the sum of each subsequence, and keep the subsequences with the sum you want. So your list comprehension looks like this:
all_nat x = [ls| ls <- subsequences [1..x], sum ls == x]
What about
getAllSums x = [(l,r)| l <- partial_nat, r <- partial_nat, l + r == x ]
where partial_nat = [1..x]

Resources