Recursion for combinations of objects but with limited spaces - object

In Python, I'm trying to develop a recursive for loop that can produce a list of lists with X objects in Y combinations. For instance, if X= 26 (say the alphabet) and Y=5 (length of word), I need to generate a list of all possible 5 letter words. The program is not using letters, however, it's using objects that are already in a list X long. Any suggestions?
I presume I need a repetitive counter for Y and an iterative recursive for loop for X, but I keep getting hung up on the details. I've tried drawing it out on paper, but the recursive nature is making my head hurt.
Edit: Per the answer below, I developed the following script that does not require recursion:
list1 = ["v", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n"]
def combo(object_list, spots):
pool = len(object_list)
total = pool**spots
permlist = list()
wordlist = list()
z = []
for i in range(total):
print("top", wordlist)
wordlist = []
z = base(i,pool,spots)
for q in z:
j=-1
for a in object_list:
j+=1
if int(q) == int(j):
wordlist.append(a)
permlist.append(wordlist)
return permlist
def base(number, base, digits):
remainder = []
result = [0,0]
while number >0:
dividend = number // int(base)
remainder.append(str(number % int(base)))
number = dividend
result = list(reversed(remainder))
while len(result) < digits:
result.insert(0,0)
return result
print (combo(list1,4))

One easy way to generate all the possibilities here requires only one loop, and no recursion.
You can think of this as a simple problem in counting. Using your analogy, we can think of a 5 letter word as a 5-digit number in base 26. The number of possible results for this example, would be 265. 265 = 11,881,376. So, we count from 0 to 11,881,376, then convert each value to base 26, and treat each base-26 digit as an index into (in this case) the alphabet.
In your case, the number of digits and the base are both probably different (and it sounds like the "base" you're using may not be a constant either), but neither of those poses a particularly difficult problem.
Based on your comments, you only want "numbers" in which each digit is unique. Given how easy it is to just generate all the combinations (simple counting) it's probably easiest to generate every number in the computed range, then filter out any that have repeated "digits".

Related

I don't know why the correct answer isn't coming up

I'm novice programmer.
I want the smallest of the input values ​​to be output, but I don't know what's wrong.
Input example :
10
10 4 2 3 6 6 7 9 8 5
Output example :
2
n = int(input())
a = input().split()
min=a[0]
for i in range(n) :
if a[i] < min :
min = a[i]
print(min)
what is the problem? please help me
Your code should work (and it does for me).
Nevertheless, min is a reserved Python word. Taking that into consideration, I also recommend the following changes for it to be more idiomatic:
a = input().split()
min_num = a[0]
for element in a:
if element < min :
min = element
print(min)
Variables can be either number or strings. For example "2" is different from 2.
The function split returns an array of strings. You would need to convert each to a number if you want to do number comparison, like:
n = int(input())
a = input().split()
min=int(a[0])
for i in range(n) :
if int(a[i]) < min :
min = int(a[i])
print(min)
Note: you already did that for n (first line in original code), but you did not do the same when you access a.
So, min is actually a python built-in, which would be very useful in this scenario. Also, you are not making the input values in a into integers, so we can do that too:
n = int(input())
a = list(map(int, input().split()))
print(min(a))
We use map to turn all the values from the split list into integers, then turn the map object back into a list. Then, using min, we can find the smallest number very easily, without a for loop.
I think you should convert each element to integers before comparing them.
a = [int(i) for i in input().split()]
Your code should work, but it will compare strings against strings instead of integers against integers.

How to limit number of permutations

I have about 20 short strings that I want to permutate. I want only permutations that have len == 8.
I would like to avoid calculating every possible permutation, as seen below:
import itertools
p = itertools.permutations([s1, s2, s3, s4, s5, s6,...])
for i in p:
s = ''.join(j for j in i)
if len(s)==8:
print(s)
But that's too slow right? How can I decrease the number of calculations? (to not spend processing and RAM).
The first, obvious thing to do is filter out any strings with length > 8:
newList = [i for i in [s1, s2, s3, s4, s5, s6, ...] if len(i) <= 8]
Then, you can use the second argument of itertools.permutations to set the number of items you want. If you have no empty strings in your list, you'll never need more than 8 items, so we can use 8 as the second argument:
p = itertools.permutations(newList, 8)
However, if any of your strings are longer than one character, this won't get you what you want, since it will only return permutations of exactly 8 items. One way to resolve this is to iterate through the various lengths:
pList = [itertools.permutations(newList, length) for length in range(1, 9)]
Yet here you end up with an enormous amount of permutations to filter through: P(20, 8) + P(20, 7) + ... P(20, 1) = roughly 5.5 billion, which is impractical to work with.
A different direction
Instead of using permutations, let's use combinations, of which there are far fewer ("only" 263,949). Recall that in combinations, the order of the combined items doesn't matter, while in permutations it does. Thus we can use the smaller set of combinations to filter for the length 8 that we want:
cList = (combo for length in range(1, 9)
for combo in itertools.combinations(newList, length)
if len(''.join(combo)) == 8)
Using () instead of [] will make this a generator rather than a list, to delay evaluation until we really need it. And now we are close!
We can get our final result by taking the permutations of the items in cList:
result = [''.join(perm) for combo in cList
for perm in itertools.permutations(combo)]

Non-recursive Most Efficient Big-O Permutation Alghoritm Python3 (non-built-in)

Hi Guys For my Data Structure assignment I have to find the most efficient way (big-o wise) to calculate permutations of a list of objects.
I found recursive examples on the web but this doesn't seem to be the most efficient way; I tried my own code but then I realized that when I count the number of possible permutations I'm actually making my algorithm O(!n). Any suggestions? .-.
from random import sample
import time
start = time.time()
testList = list(x for x in range(7))
print('list lenght: %i objects' % len(testList))
nOfPerms = 1
for i in range(1,len(testList)+1):
nOfPerms *= i
print('number of permutations:', nOfPerms)
listOfPerms = []
n = 1
while n <= nOfPerms:
perm = tuple(sample(testList, len(testList)))
listOfPerms.append(perm)
permutations = set(listOfPerms)
if len(permutations) == len(listOfPerms):
n += 1
else:
del(listOfPerms[-1])
end = time.time() - start
print('time elapsed:', end)
OUTPUT:
list lenght: 7 objects
number of permutations: 5040
time elapsed: 13.142292976379395
If instead of 7 I put 8 or 9, or 10, those are the number of permutations (I won't show the time cause it's taking too long):
list lenght: 8 objects
number of permutations: 40320
list lenght: 9 objects
number of permutations: 362880
list lenght: 10 objects
number of permutations: 3628800
I believe this will be the best you can do. Generating the number of permutations of a list generates n! permutations. As you need to generate them all this is also how much time it will take (O(n!)). What you could try to do is to make it a python generator function so you will always only generate exactly as many as you need instead of precalculating them all and storing them in memory. If you want an example of this i could give you one.
Im sorry this might be a quite negative answer. It's a good question but im pretty sure this is about the best that you can do, asymptotically. You could optimize the code itself a bit to use less instructions but in the end that wont help too much.
Edit:
This is a python implementation of Heap's algorithm which i promised
(https://en.wikipedia.org/wiki/Heap%27s_algorithm) generating N! permutations where the generation of every one permutation takes amortized O(1) time and which uses O(n) space complexity (by alteri
def permute(lst, k=None):
if k == None:
k = len(lst)
if k == 1:
yield lst
else:
yield from permute(lst, k-1)
for i in range(k-1):
if i % 2 == 0:
#even
lst[i], lst[k-1] = lst[k-1], lst[i]
else:
#odd
lst[0], lst[k-1] = lst[k-1], lst[0]
yield from permute(lst, k-1)
for i in permute([1, 2, 3, 4]):
print(i)

Algorithm for generating all string combinations

Say I have a list of strings, like so:
strings = ["abc", "def", "ghij"]
Note that the length of a string in the list can vary.
The way you generate a new string is to take one letter from each element of the list, in order. Examples: "adg" and "bfi", but not "dch" because the letters are not in the same order in which they appear in the list. So in this case where I know that there are only three elements in the list, I could fairly easily generate all possible combinations with a nested for loop structure, something like this:
for i in strings[0].length:
for ii in strings[1].length:
for iii in strings[2].length:
print(i+ii+iii)
The issue arises for me when I don't know how long the list of strings is going to be beforehand. If the list is n elements long, then my solution requires n for loops to succeed.
Can any one point me towards a relatively simple solution? I was thinking of a DFS based solution where I turn each letter into a node and creating a connection between all letters in adjacent strings, but this seems like too much effort.
In python, you would use itertools.product
eg.:
>>> for comb in itertools.product("abc", "def", "ghij"):
>>> print(''.join(comb))
adg
adh
adi
adj
aeg
aeh
...
Or, using an unpack:
>>> words = ["abc", "def", "ghij"]
>>> print('\n'.join(''.join(comb) for comb in itertools.product(*words)))
(same output)
The algorithm used by product is quite simple, as can be seen in its source code (Look particularly at function product_next). It basically enumerates all possible numbers in a mixed base system (where the multiplier for each digit position is the length of the corresponding word). A simple implementation which only works with strings and which does not implement the repeat keyword argument might be:
def product(words):
if words and all(len(w) for w in words):
indices = [0] * len(words)
while True:
# Change ''.join to tuple for a more accurate implementation
yield ''.join(w[indices[i]] for i, w in enumerate(words))
for i in range(len(indices), 0, -1):
if indices[i - 1] == len(words[i - 1]) - 1:
indices[i - 1] = 0
else:
indices[i - 1] += 1
break
else:
break
From your solution it seems that you need to have as many for loops as there are strings. For each character you generate in the final string, you need a for loop go through the list of possible characters. To do that you can make recursive solution. Every time you go one level deep in the recursion, you just run one for loop. You have as many level of recursion as there are strings.
Here is an example in python:
strings = ["abc", "def", "ghij"]
def rec(generated, k):
if k==len(strings):
print(generated)
return
for c in strings[k]:
rec(generated + c, k+1)
rec("", 0)
Here's how I would do it in Javascript (I assume that every string contains no duplicate characters):
function getPermutations(arr)
{
return getPermutationsHelper(arr, 0, "");
}
function getPermutationsHelper(arr, idx, prefix)
{
var foundInCurrent = [];
for(var i = 0; i < arr[idx].length; i++)
{
var str = prefix + arr[idx].charAt(i);
if(idx < arr.length - 1)
{
foundInCurrent = foundInCurrent.concat(getPermutationsHelper(arr, idx + 1, str));
}
else
{
foundInCurrent.push(str);
}
}
return foundInCurrent;
}
Basically, I'm using a recursive approach. My base case is when I have no more words left in my array, in which case I simply add prefix + c to my array for every c (character) in my last word.
Otherwise, I try each letter in the current word, and pass the prefix I've constructed on to the next word recursively.
For your example array, I got:
adg adh adi adj aeg aeh aei aej afg afh afi afj bdg bdh bdi
bdj beg beh bei bej bfg bfh bfi bfj cdg cdh cdi cdj ceg ceh
cei cej cfg cfh cfi cfj

Best algorithm for delete duplicates in array of strings

Today at school the teacher asked us to implement a duplicate-deletion algorithm. It's not that difficult, and everyone came up with the following solution (pseudocode):
for i from 1 to n - 1
for j from i + 1 to n
if v[i] == v[j] then remove(v, v[j]) // remove(from, what)
next j
next i
The computational complexity for this algo is n(n-1)/2. (We're in high school, and we haven't talked about big-O, but it seems to be O(n^2)). This solution appears ugly and, of course, slow, so I tried to code something faster:
procedure binarySearch(vector, element, *position)
// this procedure searches for element in vector, returning
// true if found, false otherwise. *position will contain the
// element's place (where it is or where it should be)
end procedure
----
// same type as v
vS = new array[n]
for i from 1 to n - 1
if binarySearch(vS, v[i], &p) = true then
remove(v, v[i])
else
add(vS, v[i], p) // adds v[i] in position p of array vS
end if
next i
This way vS will contain all the elements we've already passed. If element v[i] is in this array, then it is a duplicate and is removed. The computational complexity for the binary search is log(n) and for the main loop (second snippet) is n. Therefore the whole CC is n*log(n) if I'm not mistaken.
Then I had another idea about using a binary tree, but I can't put it down.
Basically my questions are:
Is my CC calculation right? (and, if not, why?)
Is there a faster method for this?
Thanks
The easiest solution will be to simply sort the array (takes O(n log n) with standard implementation if you may use them. otherwise consider making an easy randomized quicksort (code is even on wikipedia)).
Afterwards scan it for one additional time.
During that scan simple eliminate consecutive identical elements.
If you want to do it in O(n), you can also use a HashSet with elements you have already seen.
Just iterate once over your array, for each element check if it is in your HashSet.
If it isn't in there, add it.
If it is in there, remove it from the array.
Note, that this will take some additional memory and the hashing will have a constant factor that contributes to your runtime. Althought the time complexity is better, the practical runtime will only be onyl be faster once you exceed a certain array size
You can often use a space-time tradeoff and invest more space to reduce time.
In this case you could use a hash table to determine the unique words.
add is O(n), so your CC calculation is wrong. Your algorithm is O(n^2).
Moreover, how would remove be implemented? It also looks like it would be O(n) - so the initial algorithm would be O(n^3).
Binary search will only work if the array you're searching is sorted. I guess that's not the case here, or you wouldn't be looping over your entire array in the inner loop of the original solution.
If the order of the final solution is irrelevant, you could break the array into smaller arrays based on length of the strings, and then remove duplicates from those arrays. Example:
// You have
{"a", "ab", "b", "ab", "a", "c", "cd", "cd"},
// you break it into
{"a", "b", "a", "c"} and {"ab", "ab", "cd", "cd"},
// remove duplicates from those arrays using the merge method that others have mentioned,
// and then combine the arrays back together into
{"a", "b", "c", "ab", "cd"}
This is the shortest algorithm that worked where arrNames and arrScores is parallel arrays and the highest score is taken.
I := 0;
J := 0;
//iCount being the length of the array
for I := 1 to iCount do
for J := I + 1 to iCount do
if arrNames[I] = arrNames[J] then
begin
if arrScores[I] <= arrScores[J] then
arrScores[I] := arrScores[J];
arrScores[J] := arrScores[iCount];
arrNames[J] := arrNames[iCount];
arrScores[iCount] := 0;
arrNames[iCount] := '';
Dec(iCount);
end;
def dedup(l):
ht, et = [(None, None) for _ in range(len(l))], []
for e in l:
h, n = hash(e), h % len(ht)
while True:
if ht[n][0] is None:
et.append(e)
ht[n] = h, len(et) - 1
if ht[n][0] == h and et[ht[n][1]] == e:
break
if (n := n + 1) == len(ht):
n = 0
return et

Resources