Intersection of lists having comparing 3 elements at a time - python

I have tried creating two separate lists by the name of 'sample' and 'game'. These contain outcomes of 3 games, eg, (1,1,0) (0,1,0) shown as [1,1,0,0,1,0] in both of the lists. I am trying to find intersection between both the lists through my last loop which should compare 3 elements of one list with 3 elements of another list and then return the match by appending it to list 'intersection'.
Eg, sample has [1,1,0,0,1,0] and game has [1,0,1,1,1,0,1,1,0]. The intersection of both should give me [1,1,0] that is the first 3 elements of 'sample' and 3 elements from index 3 of 'game'.
However, I am facing an error of index out of range.
Also, (1,1,0) in one list might get compared with the same (1,1,0) in other list twice, if that other list has (1,1,0) 2 times, which should not happen in intersection.
import random
P1 = 1/2 # win 1st game
P2 = 2/3 # win game immediately after a win
P3 = 1/3 # win game immediately after a loss
A = [0,1] # 0 for losing a game and 1 for winning a game
N = 100
sample_points = []; G1=[]; G2=[]; G3=[]
for i in range(N):
Game1 = random.choice([0,1])
Game2 = random.choice([0,1])
Game3 = random.choice([0,1])
G1.append(Game1)
G2.append(Game2)
G3.append(Game3)
sample_points.extend([Game1, Game2, Game3])
sample = []; game=[];intersection=[]
i = 0
# creating two separate lists
while i < len(sample_points):
if sample_points[i] + sample_points[i+1] + sample_points[i+2] == 2:
n1 = sample_points[i] ; n2 = sample_points[i+1] ; n3 = sample_points[i+2]
sample.append(n1);sample.append(n2);sample.append(n3)
if sample_points[i] == 1:
q1 = sample_points[i] ; q2 = sample_points[i+1] ; q3 = sample_points[i+2]
game.append(q1);game.append(q2);game.append(q3)
i = i+3
i=0
j=0
while j < len(sample):
for i in range(len(game)):
for j in range(len(sample)):
if game[i] == sample[j] and game[i+1] == sample[j+1] and game[i+2] == sample[j+2]:
intersection.append(sample[j]);intersection.append(sample[j+1]);intersection.append(sample[j+2])
j = j+3
i=i+3

Let's look at this block of code
while j < len(sample):
for i in range(len(game)):
for j in range(len(sample)):
if game[i] == sample[j] and game[i+1] == sample[j+1] and game[i+2] == sample[j+2]:
intersection.append(sample[j]);intersection.append(sample[j+1]);intersection.append(sample[j+2])
j = j+3
i=i+3
Notice the you let i and j to run until the very end of the vector and yet you consider indices like i+1 and i+2.
I would use range to indicate the increment by 3 and also we can compare two lists rather than using multiple and statement. I have also tried to use extend. You might like to replace it with something like
for i in range(0, len(game)-3, 3):
for j in range(0, len(sample)-3, 3):
if game[i:i+3] == sample[j:j+3]:
intersection.extend(sample[j:j+3])
print(intersection)
Also, you mentioned that you want to avoid duplicate, you might want to use set to check for duplicate for the two separate lists and then convert them back to a list.

Related

How to get the correct number of distinct combination locks with a margin or error of +-2?

I am trying to solve the usaco problem combination lock where you are given a two lock combinations. The locks have a margin of error of +- 2 so if you had a combination lock of 1-3-5, the combination 3-1-7 would still solve it.
You are also given a dial. For example, the dial starts at 1 and ends at the given number. So if the dial was 50, it would start at 1 and end at 50. Since the beginning of the dial is adjacent to the end of the dial, the combination 49-1-3 would also solve the combination lock of 1-3-5.
In this program, you have to output the number of distinct solutions to the two lock combinations. For the record, the combination 3-2-1 and 1-2-3 are considered distinct, but the combination 2-2-2 and 2-2-2 is not.
I have tried creating two functions, one to check whether three numbers match the constraints of the first combination lock and another to check whether three numbers match the constraints of the second combination lock.
a,b,c = 1,2,3
d,e,f = 5,6,7
dial = 50
def check(i,j,k):
i = (i+dial) % dial
j = (j+dial) % dial
k = (k+dial) % dial
if abs(a-i) <= 2 and abs(b-j) <= 2 and abs(c-k) <= 2:
return True
return False
def check1(i,j,k):
i = (i+dial) % dial
j = (j+dial) % dial
k = (k+dial) % dial
if abs(d-i) <= 2 and abs(e-j) <= 2 and abs(f-k) <= 2:
return True
return False
res = []
count = 0
for i in range(1,dial+1):
for j in range(1,dial+1):
for k in range(1,dial+1):
if check(i,j,k):
count += 1
res.append([i,j,k])
if check1(i,j,k):
count += 1
res.append([i,j,k])
print(sorted(res))
print(count)
The dial is 50 and the first combination is 1-2-3 and the second combination is 5-6-7.
The program should output 249 as the count, but it instead outputs 225. I am not really sure why this is happening. I have added the array for display purposes only. Any help would be greatly appreciated!
You're going to a lot of trouble to solve this by brute force.
First of all, your two check routines have identical functionality: just call the same routine for both combinations, giving the correct combination as a second set of parameters.
The critical logic problem is handling the dial wrap-around: you miss picking up the adjacent numbers. Run 49 through your check against a correct value of 1:
# using a=1, i=49
i = (1+50)%50 # i = 1
...
if abs(1-49) <= 2 ... # abs(1-49) is 48. You need it to show up as 2.
Instead, you can check each end of the dial:
a_diff = abs(i-a)
if a_diff <=2 or a_diff >= (dial-2) ...
Another way is to start by making a list of acceptable values:
a_vals = [(a-oops) % dial] for oops in range(-2, 3)]
... but note that you have to change the 0 value to dial. For instance, for a value of 1, you want a list of [49, 50, 1, 2, 3]
With this done, you can check like this:
if i in a_vals and j in b_vals and k in c_vals:
...
If you want to upgrade to the itertools package, you can simply generate all desired combinations:
combo = set(itertools.product(a_list, b_list_c_list) )
Do that for both given combinations and take the union of the two sets. The length of the union is the desired answer.
I see the follow-up isn't obvious -- at least, it's not appearing in the comments.
You have 5*5*5 solutions for each combination; start with 250 as your total.
Compute the sizes of the overlap sets: the numbers in each triple that can serve for each combination. For your given problem, those are [3],[4],[5]
The product of those set sizes is the quantity of overlap: 1*1*1 in this case.
The overlapping solutions got double-counted, so simply subtract the extra from 250, giving the answer of 249.
For example, given 1-2-3 and 49-6-6, you would get sets
{49, 50, 1}
{4}
{4, 5}
The sizes are 3, 1, 2; the product of those numbers is 6, so your answer is 250-6 = 244
Final note: If you're careful with your modular arithmetic, you can directly compute the set sizes without building the sets, making the program very short.
Here is one approach to a semi-brute-force solution:
import itertools
#The following code assumes 0-based combinations,
#represented as tuples of numbers in the range 0 to dial - 1.
#A simple wrapper function can be used to make the
#code apply to 1-based combos.
#The following function finds all combos which open lock with a given combo:
def combos(combo,tol,dial):
valids = []
for p in itertools.product(range(-tol,1+tol),repeat = 3):
valids.append(tuple((x+i)%dial for x,i in zip(combo,p)))
return valids
#The following finds all combos for a given iterable of target combos:
def all_combos(targets,tol,dial):
return set(combo for target in targets for combo in combos(target,tol,dial))
For example, len(all_combos([(0,1,2),(4,5,6)],2,50)) evaluate to 249.
The correct code for what you are trying to do is the following:
dial = 50
a = 1
b = 2
c = 3
d = 5
e = 6
f = 7
def check(i,j,k):
if (abs(a-i) <= 2 or (dial-abs(a-i)) <= 2) and \
(abs(b-j) <= 2 or (dial-abs(b-j)) <= 2) and \
(abs(c-k) <= 2 or (dial-abs(c-k)) <= 2):
return True
return False
def check1(i,j,k):
if (abs(d-i) <= 2 or (dial-abs(d-i)) <= 2) and \
(abs(e-j) <= 2 or (dial-abs(e-j)) <= 2) and \
(abs(f-k) <= 2 or (dial-abs(f-k)) <= 2):
return True
return False
res = []
count = 0
for i in range(1,dial+1):
for j in range(1,dial+1):
for k in range(1,dial+1):
if check(i,j,k):
count += 1
res.append([i,j,k])
elif check1(i,j,k):
count += 1
res.append([i,j,k])
print(sorted(res))
print(count)
And the result is 249, the total combinations are 2*(5**3) = 250, but we have the duplicates: [3, 4, 5]

How to keep track of various types of streaks

I am writing a script that watches an online coin flip game, and keeps a tally of the results. I would like to find a simpler way of finding out how many times the streak ended after three of the same results, four of the same result etc.
if result = heads:
headsCount += 1
headsStreak +=1
tailsCount = 0
tailsStreak = 0
headsCount is the total amount of heads results witnessed in a session, and the streak is just so I can display how many heads have appeared in a row. This is update by:
if headsCount >= headsStreak:
headsStreak = headsCount
My problem - I wish to keep track of how many times the streak ends at one, ends at two, ends at three etc...
A silly way I have for now:
if headsStreak = 1:
oneHeadsStreak +=1
if headsStreak = 2
twoHeadsStreal +=1
But it is very tedious. So is there an easier way to create the variables... for example:
for i in range (1, 20):
(i)streak = 0
and then something like
for i in range (1, 20):
if headsStreak = i:
(i)streak += 1
Thank you in advance!
You could use a list to keep track of the streak counter. You will have to think about which index is which streak length (e.g. index 0 is for streak length 1, index 1 for length 2 etc.).
Initialize all list elements to zero:
l = [0 for i in range(20)]
Then, whenever a streak ends, increment the list element at the corresponding index: l[3] += 1 for a 4-streak.
Using a defaultdict, You just need three variables,
from collections import defaultdict
current_streak = 0
current_side = "heads"
streak_changes = defaultdict(int)
Then store the values in a dictionary when the streak changes
if side == current_side:
current_streak += 1
else:
current_side = side
streak_changes[current_streak] += 1
current_streak = 1

Referencing a conditional random element of an array and replacing it

This is my second question post on StackOverflow relating to coding in Python/Numpy.
I feel like there is definitely some sort of function which does the pseudocode:
np.random.choice([a[i-1,j],a[i+1,j],a[i,j-1],a[i,j+1]])==0 = 9
Essentially, I would like the random function to select a cell adjacent to mine (up, down, left, right) with the value 0, and replace said cell with a 9
Unforunately, I know why the code I typed is illegal. The first half of the statement returns a True/False boolean as I have used a comparison/checking operator. I can't set this into a value 9.
If I split the code-load into two codes and used an if statement with the random.choice (looking at an adjacent element that equalled zero), then following this, I would need some sort of function or definition to recall which cell (up down left or right) did the random generator originally select, to which I can then set it to 9.
Kind Regards,
EDIT: I may as well attach a sample code, so you can simply just run this (I am including my error)
a = np.empty((6,6,))
a[:] = 0
a[2,3]=a[3,3]=a[2,4] = 1
for (i,j), value in np.ndenumerate(a):
if a[i,j]==1:
np.random.choice([a[i-1,j],a[i+1,j],a[i,j-1],a[i,j+1]])==0 = 9
You could select from a range of directions (up, down, left, right) that map to specific coordinate movements in the 2D array, like this:
# generate a dataset
a = np.zeros((6,6))
a[2,3]=a[3,3]=a[2,4] = 1
# map directions to coordinate movements
nesw_map = {'left': [-1, 0], 'top': [0, 1], 'right': [1,0], 'bottom': [0,-1]}
directions = nesw_map.keys()
# select only those places where a == 1
for col_ind, row_ind in zip(*np.where(a == 1)): # more efficient than iterating over the entire array
x = np.random.choice(directions)
elm_coords = col_ind + nesw_map[x][0], row_ind + nesw_map[x][1]
if a[elm_coords] == 0:
a[elm_coords] = 9
Note that this does not do any type of bounds checking (so if a 1 appears at the edge, you might select an item "off the grid" which will result in an error).
This is the most "basic" way of getting what you need (Adding a try/except statement provides error checking, so you can prevent any unwanted errors):
import random,numpy
a = numpy.empty((6,6,))
a[:] = 0
a[2,3]=a[3,3]=a[5,5] = 1
for (i,j), value in numpy.ndenumerate(a):
var = 0
if a[i,j]==1:
while var==0:
x=random.randrange(0,4) #Generate a random number
try:
if x==0 and a[i-1,j]==0:
a[i-1,j] =9 #Do this if x = 0
elif x==1 and a[i+1,j]==0:
a[i+1,j] =9 #Do this if x = 1
elif x==2 and a[i,j-1]==0:
a[i,j-1] =9 #Do this if x = 2
elif x==3 and a[i,j+1]==0:
a[i,j+1] =9 #Do this if x = 3
var=1
except:
var=0
print a

Finding an index in range of values between 0-100 in Python

This is a two part question, I have to make a selection of 2 indexes via a random range of any number of integers in a list. Can't return both if they're both in the same range as well
Selection1 = random.randint(0,100)
Selection2 = random.randint(0,100)
For the sake of this argument, say:
Selection1 = 10
Selection2 = 17
And the list would be like so [25, 50, 75, 100]
Both would return the index of 0 because they fall between 0-25
So both would fall into the first index range, the problem is i'm having some issues trying to fit it into this range (IE: 0-25) which will return this first index (return list[0])
What is the syntax for this type of logic in python?
I'm sure I can figure out how to return different indexes if they fall in the same range, probably just loop reset to the loop but if I can get some advice on that it wouldn't hurt.
I'll give the code i'm working with right now as a guideline. Mostly at the bottom is where i'm struggling.
Code Here
def roulette_selection(decimal_list, chromosome_fitness, population):
percentages = []
for i in range(population):
result = decimal_list[i]/chromosome_fitness
result = result * 100
percentages.append(result)
print(percentages)
range_in_fitness = []
current_percent = 0
for i in range(population):
current_percent = percentages[i] + current_percent
range_in_fitness.append(current_percent)
parent1 = random.randint(0, 100)
parent2 = random.randint(0, 100)
for i in range(population):
if parent1 >= range_in_fitness[i] and parent1<=range_in_fitness[i+1]:
print(parent1, parent2)
print(range_in_fitness)
If your list of ranges is sorted, or it is acceptable to sort it, and is contiguous (no gaps), you can use Python's bisect module to do this in an efficient manner. Example:
>>> l = [25, 50, 75, 100]
>>> import bisect
>>> bisect.bisect(l, 10)
0
>>> bisect.bisect(l, 17)
0
>>> bisect.bisect(l, 55)
2
>>> bisect.bisect(l, 25)
1
Bisect returns the index of where the input number should fall into the list to maintain sort order. Note that this is a little confusing to think about at first; In the case of 55 above, it returns 2 because it should be inserted at index 2 as it falls between the current values at indices 1 and 2. If you give it a number exactly on a range boundary, it will 'fall to the right', as evidenced by the bisect(l,25) example.
The linked documentation includes a set of recipes for searching through sorted lists using bisect.
Given an input val and a list of range delimiters delims, here are two approaches:
# Both methods require range_delims to be sorted
range_delims = [25,50,75,100]
# Simple way
def find_range1(val, delims):
for i,d in enumerate(delims):
if val < d: return i
print find_range1(10, range_delims) # 0
print find_range1(17, range_delims) # 0
print find_range1(32, range_delims) # 1
print find_range1(64, range_delims) # 2
print find_range1(96, range_delims) # 3
print find_range1(101, range_delims) # None
# More explicit, possibly unnecessarily so
import math
def find_range2(val, delims):
lbl = [float('-inf')] + delims
ubl = delims + [float('inf')]
for (i,(lb,ub)) in enumerate(zip(lbl, ubl)):
if lb <= val < ub: return i
print find_range2(10, range_delims) # 0
print find_range2(17, range_delims) # 0
print find_range2(32, range_delims) # 1
print find_range2(64, range_delims) # 2
print find_range2(96, range_delims) # 3
print find_range2(101, range_delims) # 4
The first just compares val to the elements of delims and when it finds that val is less than the element, returns the index of that element.
The second is a little more verbose, generating both upper and lower bounds, and ensuring that val is between them. For interior elements of delims the bounds are list elements, for the 2 exterior elements of delims, the bounds are the element and either + or - infinity.
Note: Both approaches require the input list of delimiters to be sorted. There are ways to deal with different delimiter list formats, but it looks like you have a sorted list of delimiters (or could sort it without issue).

No errors, just doesn't print or do anything

I am pretty a beginner and I'm looking for help. I am supposed to write a simple programm which reads numbers from a file (they are ordered in two columns like this:
3 788506
255 879405
3 687899
255 697879 etc)
and always pairwise subtracts the number near 255 from the number near 3. The differences should be appended to a list. I also have to check whether the pair is rigt (e.g. that it's always 3 and 255 one after the other and not two 255s). So far I think I'm ready, but it doesn't do anything. I spent hours looking for my mistake, but I just cannot see what went wrong. I would appreciate any help.
filepath = "C:/liz/RT1-1.dat"
f = open (filepath, 'rU')
reac3 = []
reac255 = []
right_list = []
wrong_list = []
very_wrong_list =[]
li = [i.strip().split() for i in f.readlines()]
for element in li:
if int(element[0]) == 3: reac3.append(element[-1])
elif int(element[0]) == 255: reac255.append(element[-1])
k = 0
for i in range (0, len(li)+1, 2): #0,2,4,6,8 etc
if li[i][0] == 3 and li[i+1][0] == 255:
difference = int(reac255[k]) - int(reac3[k])
print int(difference)
k+=1
if difference > 300 and difference < 1200: right_list.append(difference)
else: wrong_list.append(difference)
else: very_wrong_list.append(li[i])
print right_list
i.strip().split() will return 2 strings .. therefore your comparison li[i][0] == 3 & li[i+1][0] == 5 should fail as li[i][0] & li[i+1][0] are still strings.
Also notice, that since len(li) should be even, then xrange(0, len(li) + 1, 2) will eventually make i = len(li) which should be out of the list boundaries.

Categories