Wrong number of permutations nPr(5,3) on a list - python

The goal of this program is to make as many permutations of x in size 3 (nPr(5,3), hence the iterations of (i, j, k)).
My effort on trying to achieve the permutations nPr(5,3), where 5 is the length of the list x and 3 is the length of the tuple (i,j,k):
# x will never have any repeats
x = [1, 2, 3, 4, 5]
# nPr(5,3) = 60
y = []
for i in x:
for j in x:
for k in x:
y.append((i, j, k))
print(f"Len of y = {len(y)}")
I'm expecting len(y) to be 60, as nPr(5,3) = 60. But i get the output Len of y = 125. Also, making y = set() does not fix this issue
What have I done wrong?
How do I fix this code to work (without using itertools)
Answer TL;DR: I was allowing duplicates (1,1,1)

You are allowing repeats (for example, [1,1,1] and [2,2,2]). The value of 60 is for permutations without repeats. You do that by checking that you aren't repeating a value.
NOTE that this code only works if there are no repeats in x. If there are duplicates, then you would have to use indexes instead (that is, for i in range(len(x)):).
x = [1,2,3,4,5]
y = []
for i in x:
for j in x:
if i == j:
continue
for k in x:
if i!=k and j!= k:
y.append((i,j,k))
print(y)

As pointed out by Tim Roberts, I was adding repeats of i,j or k, (1,1,1). My fix is to just make sure i,j and k are different:
for i in x:
for j in x:
for k in x:
# If i,j,k are different
if len(set((i, j, k))) == 3:
y.append((i, j, k))
As set((i, j, k)) will remove the duplicates in the tuple (i, j, k), so the length must equal 3. This is also helpful if I need to make this recursive for nPr(n,r) as set((i, j, k ... r)) == r.

This will work, though it's a bit too deeply nested for my taste:
y = []
for i in x:
for j in x:
if i != j:
for k in x:
if i != k and j != k:
y.append((i, j, k))
assert list(itertools.permutations(x, 3)) == y
Negating the conditions and using continue increases readability:
y = []
for i in x:
for j in x:
if i == j:
continue
for k in x:
if i == k or j == k:
continue
y.append((i, j, k))
assert list(itertools.permutations(x, 3)) == y
But this will only work if all members of x are unique. Better would be to check that the indices are different:
y = []
for i in range(len(x)):
for j in range(len(x)):
if i == j:
continue
for k in range(len(x)):
if i == k or j == k:
continue
y.append((x[i], x[j], x[k]))
assert list(itertools.permutations(x, 3)) == y
We could also do the job with recursion, parameterizing r (number of items in each permutation) in the process, though without dynamic programming, this approach will do a lot of extra work for large x and r:
# if x were hashable, i.e. a tuple in this case, we could use the
# #functools.cache decorator to avoid repeated work
def permutations(x, r):
if not r:
return [(),]
res = []
for i in range(len(x)):
perms_without_x_i = permutations(x[:i] + x[i + 1 :], r - 1)
res += [(x[i],) + p for p in perms_without_x_i]
return res
assert permutations(x, 3) == list(itertools.permutations(x, 3))
But probably the best way of all is to steal the answer directly from the docs:
def permutations(iterable, r=None):
# permutations('ABCD', 2) --> AB AC AD BA BC BD CA CB CD DA DB DC
# permutations(range(3)) --> 012 021 102 120 201 210
pool = tuple(iterable)
n = len(pool)
r = n if r is None else r
if r > n:
return
indices = list(range(n))
cycles = list(range(n, n-r, -1))
yield tuple(pool[i] for i in indices[:r])
while n:
for i in reversed(range(r)):
cycles[i] -= 1
if cycles[i] == 0:
indices[i:] = indices[i+1:] + indices[i:i+1]
cycles[i] = n - i
else:
j = cycles[i]
indices[i], indices[-j] = indices[-j], indices[i]
yield tuple(pool[i] for i in indices[:r])
break
else:
return

Related

How do I find the index of variable b from list a?

How do I find the index of variable b from list a?
I suspect that the problem is in the data types.
a=[-202516736, '-202516736', '13886', '678280946', '14514', '330251838', '14511', '639566631', '14510', '542472303', '14506']
b=['678280946']
a_INT = [int(item) for item in a]
b_INT = [int(item) for item in b]
j = 0
while True:
try:
i = a_INT.index(b_INT, j)
print(i)
j = i + 1
except:
break
Let's take this a step further and add another value to the b list and also add a duplicate in the a list. Then:
a=[-202516736, '-202516736', '13886', '678280946', '14514', '678280946', '330251838', '14511', '639566631', '14510', '542472303', '14506']
b=['678280946', 13886]
ai = list(map(int, a))
for n in map(int, b):
offset = 0
r = []
while True:
try:
i = ai[offset:].index(n)
r.append(offset+i)
offset += i + 1
except ValueError:
break
print(f'{n} occurs at {r}')
Output:
678280946 occurs at [3, 5]
13886 occurs at [2]
Version 2:
The first piece of code is functionally correct. However, it could be very inefficient if the list being searched is very large.
Python's built-in sort function is very fast. So, let's build a list of 2-tuples each made up of a value from the list and its original index. Then sort the new list. Now that it's sorted we can perform a binary search and move on from there.
Added some more values to the OP's original list for demonstration purposes:
a = [-202516736, '-202516736', '13886', '678280946', '14514', '678280946',
'330251838', '14511', '639566631', '14510', '542472303', '14506', '678280946']
b = ['678280946', 13886, 14514, '-202516736', 99]
def bsearch(lst, x):
L = 0
R = len(lst) - 1
while L <= R:
m = (L + R) // 2
if (v := lst[m][0]) == x:
return m
if v < x:
L = m + 1
else:
R = m - 1
return -1
def findall(list_, n):
templist = sorted((v, i) for i, v in enumerate(list_))
result = None
if (i := bsearch(templist, n)) >= 0:
result = [templist[i][1]]
for j in range(i-1, -1, -1):
if templist[j][0] != n:
break
result.append(templist[j][1])
for j in range(i+1, len(templist)):
if templist[j][0] != n:
break
result.append(templist[j][1])
return result
ai = list(map(int, a))
for n in map(int, b):
print(f'{n} -> {findall(ai, n)}')
Output:
678280946 -> [5, 3, 12]
13886 -> [2]
14514 -> [4]
-202516736 -> [0, 1]
99 -> None
a=[-202516736, '-202516736', '13886', '678280946', '14514', '330251838', '14511', '639566631', '14510', '542472303', '14506']
b=['678280946']
for item in b:
print(a.index(item))
Since b has only one element the output is 3.

The number of combinations of the number 196225

I wanted to write a program that will show the number of combinations of the number 196225 but I failed because it showed very strange numbers and I can't quite understand why
my code:
list = []
liczba = [1,9,6,2,2,5]
n_num = 0
iter=0
#ijlkmn
for i in range(6):
n_num = liczba[i]*10**5
for j in range(6):
if j != i:
n_num += liczba[j]*10**4
for l in range(6):
if l != i and l != j:
n_num += liczba[l]*10**3
for k in range(6):
if k != i and k != j and k!= l:
n_num += liczba[k]*10**2
for m in range(6):
if m != i and m != j and m != l and m!= k:
n_num += liczba[m]*10
for n in range(6):
if n != i and n != j and n != l and n!= k and n!= m:
n_num += liczba[n]
if (n_num in list) == False:
list.append(n_num)
iter += 1
I know it looks very primitive but I only wanted the result which turned out to be incorrect, here are some of its numbers ~
196225, 196277, 196502, 196554, 197076, 197098, 199723, 199775, 200040
could someone tell me where did these numbers come from?
if you want to make all the possible numbers using tose digits , you can use itertools module :
import itertools
liczba = [1, 9, 6, 2, 2, 5]
numbers = itertools.permutations(liczba, 6)
for num in numbers:
print(num)
You are adding to previous answers on each of your internal loops. Because the value of n_num is not reset at each iteration. For example:
digits = [1,2,3]
for k in range(3):
n_num = digits[k]*10**2
for k2 in range(3):
if k2!=k:
n_num += digits[k2]*10
print(n_num)
prints
120
150
210
240
310
330
because when k2 = 2 the first time, 30 is added to 120 yielding 150... etc etc
I see what's wrong with your code - let's look into smaller example to see it clearly and find a solution
liczba = [1, 2, 3]
for i in range(3):
n_num = liczba[i] * 10 ** 2 # it start with 1 * 100 = 100, good one!
for j in range(3):
if j != i: # for j = 0 it just pass
n_num += liczba[j] * 10 # j = 1, n_num is now 100 + 20 = 120
for l in range(3):
if l != i and l != j: we skip 0 and 1
n_num += liczba[l] # n_num = 123, great
list.append(n_num)
Now we back to second loop:
for j in range(3): # j jumps to 2
if j != i:
n_num += liczba[j] * 10 # wait - here is the problem, our n_num should be 100 here again but now it's 123, so we add 30 to previous solution
for l in range(3):
if l != i and l != j: # l = 1
n_num += liczba[l] # adding 2, but instead of 100 + 30 + 2 we have 123 + 30 + 2
list.append(n_num)
How to fix it? Very simple - you can just calculate number once at the end. You have all indexes already, so write
n_num = liczba[i] * 10 ** 5 + liczba[j] * 10 ** 4 + ...
And one small hint for the end - if you use set instead of list you won't need a check if n_num is already in result.

List comprehension with nested for loop, conditional, and accumulator

I am trying to convert this piece of code into a list comprehension:
a = np.random.rand(10) #input vector
n = len(a) # element count of input vector
b = np.random.rand(3) #coefficient vector
nb = len(b) #element count of coefficients
d = nb #decimation factor (could be any integer < len(a))
c = []
for i in range(0, n, d):
psum = 0
for j in range(nb):
if i + j < n:
psum += a[i + j]*b[j]
c.append(psum)
I've tried following suggestions from:
List comprehension with an accumulator
nested for loops to list comprehension with differents "if" conditions
For example:
from itertools import accumulate
c = [accumulate([a[i + j] * b[j] for j in range(nb) if i + j < n] ) for i in range(0, n, d)]
Later, when trying to get values from c (e.g. c[:index]):
TypeError: 'NoneType' object is not subscriptable
Or:
from functools import partial
def get_val(a, b, i, j, n):
if i + j < n:
return(a[i + j] * b[j])
else:
return(0)
c = [
list(map(partial(get_val, i=i, j=j, n=n), a, b))
for i in range(0, n, d)
for j in range(nb)
]
in get_val, return(a[i + j] * b[j])
IndexError: invalid index to scalar variable.
Or:
psum_pieces = [[a[i + j] * b[j] if i + j < n else 0 for j in range(nb)] for i in range(0, n, d)]
c = [sum(psum) for psum in psum_pieces]
As well as many other iterations of these approaches. Any guidance would be much appreciated.
You really don't need to be using a list comprehension here. With numpy, you can create a fast pipelined solution that does not run any loops directly in the interpreter.
First convert a into a 2D array shaped (n // d, nb). The missing elements (i.e., where i + j >= n in the loop) can be zero since that will make the corresponding increment to psum zero:
# pre-compute i+j as a 2D array
indices = np.arange(nb) + np.arange(0, n, d)[:, None]
# we only want valid locations
mask = indices < n
t = np.zeros(indices.shape)
t[mask] = a[indices[mask]]
Now you can compute c directly as
(t * b).sum(axis=1)
I suspect that if you benchmark this solution against anything written in vanilla python not compiled with numba, it will be much faster.
If I've understood correctly what you want is something like
res = [sum(a[i+j]*b[j] for j in range(nb) if i+j < n) for i in range(0,n,d)]
For each i, this will add to the resulting list the sum of the products a[i+j]*b[j] for j that varies from 0 to nb-1 when i+j < n

Conversion of iterative algorithm to recursive

Hi I am trying to convert my iterative algorithm to recursive solution to achieve Dynamic Programming after it's done (Do suggest me other ways to reduce time complexity of this triple nested iteration). I am not good with recursion. I had tried to convert it but it is giving me index out of range errors.
Iterative Approach:
def foo(L):
L=sorted(L)
A = 0
for i,x in enumerate(L):
for j,y in enumerate(L):
if x != y:
if (y%x ==0):
for k,z in enumerate(L):
if y != z:
if (z%y==0) and (y%x==0):
A= A+1
return A
Recursive Approach:
A =i =j= k =0 #Initializing globals
def foo(L):
L=sorted(L)
global A ,i,j,k
x=y=z=L
luckyOne(x,y,z)
return A
def luckyOne(x,y,z):
global i,j,k
while(i< len(x) and j < len(y) and k < len(z)):
while(x[i] != y[j]):
luckyTwo(x[i:],y[j:],z[k:])
i+=1
luckyOne(x[i:],y[j:],z[k:])
# i+=1
# return A
i+=1
luckyOne(x[i:],y[j:],z[k:])
return 0
def luckyTwo(x,y,z):
global i,j,k
while (i< len(x) and j < len(y) and k < len(z)):
while(y[j]%x[i]==0):
luckyThree(x[i:],y[j:],z[k:])
j+=1
luckyTwo(x[i:],y[j:],z[k:])
j+=1
luckyTwo(x[i:],y[j:],z[k:])
return 0
def luckyThree(x,y,z):
global A ,i,j,k
while (i< len(x) and j < len(y) and k < len(z)):
while (y[j]!=z[k]):
while((z[k]%y[j]==0) and (y[j]%x[i]==0)):
A+=1
print 'idr aya'
k+=1
luckyThree(x[i:],y[j:],z[k:])
k+=1
luckyThree(x[i:],y[j:],z[k:])
return 0
The input should be like L=['1','2','3']
This is the fastest version I can come up with:
def foo(lst):
edges = {x: [y for y in lst if x != y and y % x == 0] for x in set(lst)}
return sum(len(edges[y]) for x in lst for y in edges[x])
This should be significantly faster (1/7th the time in my test of lists with 100 elements).
The algorithm is essentially to build a directed graph where the nodes are the numbers in the list. Edges go from node A to node B iff the integer values of those nodes are different and A divides evenly into B.
Then traverse the graph. For each starting node A, find all the nodes B where there's an edge from A to B. On paper, we would then go to all the next nodes C, but we don't need to... we can just count how many edges are leaving node B and add that to our total.
EDIT
Depending on the distribution of values in the list, this is probably faster:
def foo(lst):
counts = Counter(lst)
edges = {x: [y for y in counts if x != y and y % x == 0] for x in counts}
return sum(counts[x] * counts[y] * sum(counts[z] for z in edges[y]) for x in counts for y in edges[x])
Here, you can think of nodes as having a numeric value and a count. This avoids duplicate nodes for duplicate values in the input. Then we basically do the same thing but multiply by the appropriate count at each step.
EDIT 2
def foo(lst):
counts = collections.Counter(lst)
edges = collections.defaultdict(list)
for x, y in itertools.combinations(sorted(counts), 2):
if y % x == 0:
edges[x].append(y)
return sum(counts[x] * counts[y] * sum(counts[z] for z in edges[y]) for x in counts for y in edges[x])
Slight improvement thanks to #Blckknght. Sorting the unique values first saves some time in enumeration.
EDIT 3
See comments, but the original code in this question was actually wrong. Here's code that (I think) does the right thing based on the problem description which can be found in the comments:
def foo3(lst):
count = 0
for x, y, z in itertools.combinations(lst, 3):
if y % x == 0 and z % y == 0:
count += 1
return count
print(foo3([1, 2, 3, 4, 5, 6])) # 3
print(foo3([6, 5, 4, 3, 2, 1])) # 0
EDIT 4
Much faster version of the previous code:
def foo4(lst):
edges = [[] for _ in range(len(lst))]
for i, j in itertools.combinations(range(len(lst)), 2):
if lst[j] % lst[i] == 0:
edges[i].append(j)
return sum(len(edges[j]) for i in range(len(lst)) for j in edges[i])
EDIT 5
More compact version (seems to run in about the same amount of time):
def foo5(lst):
edges = [[j for j in range(i + 1, len(lst)) if lst[j] % lst[i] == 0] for i in range(len(lst))]
return sum(len(edges[j]) for i in range(len(lst)) for j in edges[i])
Here's how I'd solve your problem. It should use O(N**2) time.
def count_triple_multiples(lst):
count = collections.Counter(lst)
double_count = collections.defaultdict(int)
for x, y in itertools.combinations(sorted(count), 2):
if y % x == 0:
double_count[y] += count[x] * count[y]
triple_count = 0
for x, y in itertools.combinations(sorted(double_count), 2):
if y % x == 0:
triple_count += double_count[x] * count[y]
return triple_count
My algorithm is very similar to the one smarx is using in his answer, but I keep a count of the number of edges incident to a given value rather than a list.
As far as speeding things up goes (instead of going recursive), testing with 1000 entries, slicing the sorted list at each level cut time by more than half for me (gets rid of numbers less than y, z at their respective levels:
def foo(L):
assert 0 not in L
L=sorted(L)
A = 0
for i,x in enumerate(L):
for j,y in enumerate(L[i + 1:]):
if x != y and not y % x:
for k,z in enumerate(L[i + j + 2:]):
if y != z and not z % y:
A = A + 1
return A

Backtracking permuting a list

I am trying to permute a list but I cannot do it, it goes infinite cycle. I tried different things but somehow the only thing that it shows me is 1 2 3 ... 1 2 3 etc
This is the code:
def prints(v,k):
s = ''
for i in range(k + 1):
s += str(v[i]) + ' '
print(s)
def continuare(v,k):
ok = True
for i in range(k):
if v[i] == v[k]:
ok = False
break
return ok
def back(v,a):
k = 0
caut = False
while k>-1:
caut = False
pos = 0
while pos < len(a) and caut == False:
v[k] = a[pos]
pos += 1
if continuare(v,k):
caut = True
if caut == False:
k -= 1
elif k == len(a) - 1:
prints(v,k)
else:
k += 1
a = [1,2,3]
v = []
for x in range(len(a)):
v.append(a[0])
back(v,a)
Here's a trivial Python transcription from http://www.geeksforgeeks.org/write-a-c-program-to-print-all-permutations-of-a-given-string/ :
def swap(a, i, j):
a[i], a[j] = a[j], a[i]
def permute(a, i, n):
if i == n:
print(a)
return
for j in range(i, n+1):
swap(a, i, j)
permute(a, i+1, n)
swap(a, i, j) # backtrack
def main():
a = list('ABC')
permute(a, 0, 2)
if __name__ == '__main__':
main()
I'd rather have permute be a generator yielding the permutations, with main looping on them and printing them, but this may be closer to the C original and thus easier to follow. Note that one difference is a must: what's being permuted here is a list, not a string as in the C original, because strings are immutable in Python (so swap would require substantially different logic, returning the "string with swapped characters" rather than being able to work in-place as the "backtracking" logic requires).

Categories