Backtrack in python but stop going deeper if condition is met - python

So I understand the concept of backtracking and looked through the code here to implement the simple string permutation algorithm.
My problem however is the following:
I have a list of n lists of the form:
l = [[1,2,3],[4,3,2],[7,6,5],[1,2,9],[6,8]]
Say I sort these lists by least number of occurrence of an element and place in a dictionary where the key represents the number of occurrence of an element, like so:
d = {1:[[7,6,5],[6,8],[4,3,2],[1,2,9]], 2:[[1,2,3],[1,2,9],[4,3,2],[7,6,5],[6,8]], 3:[[1,2,3],[4,3,2],[1,29]]}
So what that dictionary represents is the following:
The numbers 4, 5, 7, 8 and 9 occur only once so all lists containing them are put into a list with the key 1.
The numbers 1, 3 and 6 occur twice and all lists containing them are put into a list with the corresponding key 2.
The number 2 occurs 3 times so all lists containing it are put in a list of lists with key 3.
Now, in order to find a set cover for these the set of numbers from 1 to 9, I know I will NEED to use all the list of lists with the key 1. If this satisfies the entire range from 1 to 9 (in this case it does), my job is done. Otherwise, I will need to backtrack with EACH list in the list of lists corresponding to key 2, if I find a combination that satisfies, I stop. If none of them satisfy, I move onto the lists corresponding to key 3.
How would one achieve this in python and is this a good way of implementing it?
I realize the question is hard to explain in plain English so if it's unclear, please let me know.

Related

Finding consecutive numbers from multiple lists in python

Consider, for example, that I have 3 python lists of numbers, like this:
a = [1,5,7]
b = [2,6,8]
c = [4,9]
I need to be able to check if there are consecutive numbers from these lists, one number from each list, and return true if there are.
In the above example, 7 from list a, 8 from list b and 9 from list c are consecutive, so the returned value should be true.This should be extendable to any number of lists (the number of lists is not known in advance, because they are created on the fly based on prior conditions).
Also, values in a list is not present in any other list. For example, list a above contains the element '1', so '1' is not present in any other list.
Is there a way to accomplish? It seems simple, yet too complex. I am a python newbie, and have been trying all sorts of loops but not even getting close to what I am looking for.
Looking for suggestions. Thanks in advance.
UPDATE: Here is the context for this question.
I am trying to implement a 'phrase search' in a sentence (which is part of a much bigger task).
Here is an example.
The sentence is:
My friend is my colleague.
I have created an index, which is a dictionary having the word as the key and a list of its positions as the value. So for the above sentence, I get:
{
'My': [0,3],
'friend': [1],
'is': [2],
'colleague': [4]
}
I need to search for the phrase 'friend is my' in the above sentence.
So I am trying to do something like this:
First get the positions of words in the phrase from the dictionary, to get:
{
'My': [0,3],
'friend': [1],
'is': [2],
}
Then check if the words in my phrase have consecutive positions, which goes back to my original question of finding consecutive numbers in different lists.
Since 'friend' is in position 1, 'is' is in position 2, and 'my' is in position 3. Hence, I should be able to conclude that the given sentence contains my phrase.
Can you assume
lists are sorted?
O(n) memory usage is acceptable?
As a start, you could merge the lists and then check for consecutive elements. This isn't a complete solution because it would match consecutive elements that all appear in a single list (see comments).
from itertools import chain, pairwise
# from https://docs.python.org/3/library/itertools.html#itertools-recipes
def triplewise(iterable):
"Return overlapping triplets from an iterable"
# triplewise('ABCDEFG') --> ABC BCD CDE DEF EFG
for (a, _), (b, c) in pairwise(pairwise(iterable)):
yield a, b, c
def consecutive_numbers_in_list(*lists: list[list]) -> bool:
big_list = sorted(chain(*lists))
for first, second, third in triplewise(big_list):
if (first + 1) == second == (third - 1):
return True
return False
consecutive_numbers_in_list(a, b, c)
# True
Note itertools.pairwise is py 3.10
If the lists are sorted but you need constant memory, then you can use an n pointer approach in which you have a pointer to the first element of each list, then advance the lowest pointer on each iteration and keep track of the last three values seen at all times.
Ultimately, your question doesn't make that much sense, in that this doesn't seem like a typical programming task. If you are a newbie to programming, you can ask what you are trying to accomplish, instead of how to implement your candidate solution, and we might be able to suggest a better method overall. See https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem
UPDATE
You are implementing phrase search. So an additional requirement, compared to the original question, is that the first list contain the first index of the sequence, the second list contain the second index of the sequence, etc. (As I assume that "friend my is" is not an acceptable search result for the query "my friend is".)
Pseudocode:
for each index i in the j=1th list:
for each list from the jth list to the nth list:
see whether i + j - 1 appears in list j
Depending on the characteristics of your data, you may find there are easier/more efficient approaches
can find all the documents matching n of the search terms in the phrase, then do exact substring matching in the document
if search terms have max token length that is relatively short, then you can add n-grams to your search index
This is a very general problem, you can look at implementations in popular search engines like ElasticSearch.

Finding top 3 products in lists with sub-lists

I am trying to find faster solution than the current brute force one, which assumes looping over all elements and is super inefficient on the larger lists.
To be more specific, I am having a list of N elements. Each of these elements has another list of variable number of elements. The goal is to find top 3 products of all lists, given that only one number from any list might be used at any time.
So for instance, if we had lists(dictionaries):
MAIN LIST: {'a':0.97,'b':0.88,'c':0.77}
A SUB-LIST: {'a1':0.68,'a2':0.13,'a3':0.04}
B SUB-LIST: {'b1':0.77,'b2':0.66,'b3':0.02}
C SUB-LIST: {'c1':0.99,'c2':0.92,'c3':0.13}
RESULT: 1.c*c1 2.c*c2 3.b*b1
I wanna get the top number from all possible products. Sorting the lists, iterating over all elements and adding them to the temporary list with top3 items is the current solution, but becomes problematic while dealing with lists having more than 1,000 elements.
Does anyone know any sort of algorithm that might be used here, given that length of each list can exceed 1000 elements?
Alternatively, if finding 3 is not possible with good time complexity - do you know any algorithm that might be used to finding e.g. top1 product between all the lists?
You can cut down on the multiplications by getting the top 3 of each perfix letter (using heapq nlargest)
Then apply the factors on these lists of top 3s and pick the top 3 out of those.
Something like
from heapq import nlargest
# group the sub list by their key letter
sub_lists = dict()
for sl in [A,B,C]:
k = next(iter(sl))[0]
sub_lists.setdefault(k,[]).extend(sl.items())
top3s = [ (pk,pv*main_list[k]) for k,prods in sub_lists ]
for pk,pv in nlargest(3,prods,key=lambda kv:kv[1]) ]
top3 = nlargest(3,top3s,key=lambda kv:kv[1])
Note: You didn't provide any code or data so I could not actually test this

Find the highest sum combination with conditions from a list

So I have a 360 element list that I want to find the highest sum of 11 numbers combination, but with a condition.To make it a bit clearer:
1-Gets a list as input
2-Create a combination list with 11 numbers
3-Check the list for a specific condition
4-If yes, return the list's sum
I tried to use itertools.combination then check for the condition but it took so long as my list is really big.So I'm wondering if there's a way to check for the condition first rather than creating all the combinations then filtering them out.
EDIT: Guys I think you didn't get my question quite well.I want to get the list's combination first(like permutation), not just the highest 11 numbers
Why not sorted the list, descending, and then pick the first 11? If you need the indices, you can just find the numbers that you need from the original list.
import random
import pprint
original_items = random.choices(range(999), k=360)
pprint.pprint(original_items)
highest_11 = sorted(original_items, reverse=True)[:11]
pprint.pprint(highest_11)
Generally, sort the list and find the first 11 numbers that satisfy your condition.
Even if your condition is non deterministic, you can still probably reduce the runtime to linear for the search itself (and thus the runtime will depend on the condition).

How to only check for two values existence in a list

I have a list of lists say, for example:
directions = [[-1,0,1],[1,0,4],[1,1,2][-1,1,2]]
now, in any of the nested lists the index [2] is of no importance in the test.
I want to try to find if the first two values in any of the nested lists match the inverse of any other, To clarify further by inverse I mean the negative value In python code. preferable with only one line but if that not possible than a work around to get the same effect.
and if is condition is true and the third values of the two nested lists should be added together and stored in the second original list in the check function and the second list which was the inverse one should be deleted.
So
if nested list's first 2 values == -another nested list's first 2 values
add their third values together
list delete(inverse list)
I hope this makes a little more sense.
I have tried this below but I still cant get it to skip the 3 value or index 2
listNum = 0
while len(directions) > listNum:
if (-directions[listNum][0], -directions[listNum][1], anything(Idk)) in directions:
index = index(-directions[listNum][0], -directions[listNum][1], anything(Idk))
directions[listNum][2] += directions[index][2]
directions.del(index)
But I don't know what to put where I put anything(Idk)

Keeping count of values available from among multiple sets

I have the following situation:
I am generating n combinations of size 3 from, made from n values. Each kth combination [0...n] is pulled from a pool of values, located in the kth index of a list of n sets. Each value can appear 3 times. So if I have 10 values, then I have a list of size 10. Each index holds a set of values 0-10.
So, it seems to me that a good way to do this is to have something keeping count of all the available values from among all the sets. So, if a value is rare(lets say there is only 1 left), if I had a structure where I could look up the rarest value, and have the structure tell me which index it was located in, then it would make generating the possible combinations much easier.
How could I do this? What about one structure to keep count of elements, and a dictionary to keep track of list indices that contain the value?
edit: I guess I should put in that a specific problem I am looking to solve here, is how to update the set for every index of the list (or whatever other structures i end up using), so that when I use a value 3 times, it is made unavailable for every other combination.
Thank you.
Another edit
It seems that this may be a little too abstract to be asking for solutions when it's hard to understand what I am even asking for. I will come back with some code soon, please check back in 1.5-2 hours if you are interested.
how to update the set for every index of the list (or whatever other structures i end up using), so that when I use a value 3 times, it is made unavailable for every other combination.
I assume you want to sample the values truly randomly, right? What if you put 3 of each value into a list, shuffle it with random.shuffle, and then just keep popping values from the end of the list when you're building your combination? If I'm understanding your problem right, here's example code:
from random import shuffle
valid_values = [i for i in range(10)] # the valid values are 0 through 9 in my example, update accordingly for yours
vals = 3*valid_values # I have 3 of each valid value
shuffle(vals) # randomly shuffle them
while len(vals) != 0:
combination = (vals.pop(), vals.pop(), vals.pop()) # combinations are 3 values?
print(combination)
EDIT: Updated code based on the added information that you have sets of values (but this still assumes you can use more than one value from a given set):
from random import shuffle
my_sets_of_vals = [......] # list of sets
valid_values = list()
for i in range(my_sets_of_vals):
for val in my_sets_of_vals[i]:
valid_values.append((i,val)) # this can probably be done in list comprehension but I forgot the syntax
vals = 3*valid_values # I have 3 of each valid value
shuffle(vals) # randomly shuffle them
while len(vals) != 0:
combination = (vals.pop()[1], vals.pop()[1], vals.pop()[1]) # combinations are 3 values?
print(combination)
Based on the edit you could make an object for each value. It could hold the number of times you have used the element and the element itself. When you find you have used an element three times, remove it from the list

Categories