How to find back to back same items in Python? - python

I basically want to count how many times "H" printed back to back 3 times in this random sequence. How can i add a code which detects its occurence, is it possible with index method?
import random
prob= ["H","T"]
streakcount=0
x =[]
for excount in range(1000):
y = random.choice(prob)
x.append(y)
print(x)

Yeah, you can index the previous element in the 'x' array and check if it is also an 'H'.
So, first add an IF statement after y is assigned a random value and check if that value is 'H'. If it is, then you want to index the previous element in the array 'x' to see if it is also an 'H'. Finally, we need an accumulator to store the number of times this happens.
That's the gist of it. The only potential hiccup we want to avoid occurs at the very beginning of the 'for' loop when excount is 0 or 1. At this time, if an 'H' is randomly chosen and we try to index the 'x' array at the index, excount-2, we'll end up indexing the list from the end (with a negative number) or indexing a list at an index that does not yet exist.
This could occur because we're subtracting 1 and 2 from excount and then indexing the 'x' array, so we just want to double-check that excount is >= 2 before we start checking to see if we've seen three H's in a row.
import random
prob= ["H","T"]
streakcount=0
x =[]
for excount in range(1000):
y = random.choice(prob)
if y == 'H' and excount >= 2:
# check to see if previous element was also an 'H'
if x[excount-1] == 'H' and x[excount-2] == 'H':
streakcount += 1 # 3 H's in a row! increment counter
x.append(y)
print(x)

I think you can just convert a list to string and then use count to get count.
import random
prob= ["H","T"]
streakcount=0
x =[]
for _ in range(1000):
y = random.choice(prob)
x.append(y)
strlist = ' '.join(map(str, x))
print(strlist.count("H H H"))

The deque class from the collections module is useful for this use-case. The deque is constructed so as to support a list with a maximum size of 3. As new values are appended to the deque the oldest value is lost. Therefore we can use the deque's count() function to get the number of values matching the criterion of interest
from random import choice
from collections import deque
ht = ['H', 'T']
streakcount = 0
d = deque([], 3)
for _ in range(1_000):
d.append(choice(ht))
if d.count('H') == 3:
streakcount += 1
print(f'{streakcount=}')

import random
prob = ["H", "T"]
streakcount = 0
x = []
for excount in range(1000):
y = random.choice(prob)
x.append(y)
for i in range(len(x)):
if i > 0 and i + 1 <= len(x):
if x[i - 1] == 'H' and x[i] == 'H' and x[i + 1] == 'H':
streakcount += 1
print(streakcount)

Related

How would I count all pairs in a list when they are all the same?

I am trying to count all pairs of numbers in a list. A pair is just two numbers that are the same. My current code looks like this.
def pairs(lst):
lst.sort()
count = 0
for x in range(len(lst)):
if x+1 < len(lst):
if lst[x] == lst[x+1]:
count +=1
return count
pairs([1, 1, 1, 1, 1])
What do I need to change to be able to have it count each pair of 1's?
The reason that the function gives the wrong value is that it is taking each item in the list and checking if the next value matches it. This will double count all non-endpoint values. Also looping with conditional statements is inefficient. It may be better to think of the problem as the sum of modulo 2 of the count of each distinct item in the list.
Try this:
Include incomplete pairs
import math
def count_pairs(a_list):
counter=0
for x in set(a_list):
counter += math.ceil(lst.count(x)/2)
print(counter)
Include only complete pairs
import math
def count_pairs(a_list):
counter=0
for x in set(a_list):
counter += math.floor(lst.count(x)/2)
print(counter)
Example:
lst=[1,1,1,1,1,2,2,2,2,2,3,3,3,4,4,5,6,5]
count_pairs(lst)
Output 1
11
Output 2
7
You can try this approach:
list = [1,1,1,1,1,1,2,2,3,3,4]
list.sort
# remove last element if len(list) is odd
if ( len(list) % 2 != 0 ) :
list.pop()
c = 0
# create an `iter` object to simplify comparisons
it = iter(list)
for x1 in it:
x2 = next(it)
if ( x1 == x2 ):
c += 1
print(c)
It wasn't clear to me if you only want "1", if this is the case, introduce a check for x1 or x2 greater than 1 and break the loop.
Code
def count_pairs(lst):
' Using generator with Walrus operator '
return sum(cnt*(cnt-1)//2 for element in set(lst) if (cnt:=lst.count(element)))
Test
print(count_pairs([1, 1, 1, 1, 1])) # Output: 10
print(count_pairs([1,1,1,1,1,2,2,2,2,2,3,3,3,4,4,5,6,5])) # Output: 25
Explanation
The number of pairs of a number in the list is found by:
count the frequency of the number in the list
counting its combinations taking 2 at a time (i.e. for frequency k, combinations = k*(k-1)//2
We sum the pairs count for each unique number in list (i.e. set(lst))
For clarity, the oneliner solution can be expanded to the following.
def count_pairs(lst):
cnt = 0
for element in set(lst):
frequency = lst.count(element) # frequency of element
cnt += frequency * (frequency - 1) //2 # accumulate count of pairs of element
return cnt

traversing through a list using recursion

So I am new to recursion and I am trying to make a program where you can enter a list and python tests each integer (lets say 9 for example) and sees if the integer following it is doubled. So if I entered a list of 2 4 8 16 32, would return 4, and -5 -10 0 6 12 9 36, would return 2 because -5 followed by -10 is one and 6 followed by 12 is the second. This is the code I have so far. I feel like I am very close. but just a few thing stand in my way. Any help would be great!
L = []
def countDouble(L):
x = input(f'Enter a list of numbers separated by a space: ')
y = (x.split(' '))
print(y[1])
print(y[0])
count = 0
y[0] += y[0]
# unsure of how to multiple y[0] by 2
if y[0]*2 == y[1]:
count += 1
else:
count += 0
#how would I traverse through the rest of the entered list using recursion?
print(count)
countDouble(L)
If you want/need to solve it using recursion, the following will do the trick:
def count_sequential_doubles(li, count=0):
return count_sequential_doubles(li[1:], count + int(li[0] * 2 == li[1])) if len(li) > 1 else count
I would suggest this recursive way:
def countDouble(L):
count = 0
if len(L) == 1:
return count
else:
if int(L[0])*2 == int(L[1]):
count += 1
return count + countDouble(L[1:])
x = input(f'Enter a list of numbers separated by a space: ')
y = (x.split(' '))
count = countDouble(y)
print(count)
I urge you to read the entire answer, but in case you are not interested in tips, notes and the process of finding the solution, here are two solutions:
solution using recursion (not recommended):
x = input()
y = x.split(' ')
count = 0
def countDouble(i):
if(i+1 == len(y)):
return 'recursion ends here when'
if(int(y[i])*2==int(y[i+1])):
count += 1
countDouble(i+1)
countDouble(0)
print(count)
this solution just imitates a while loop:
solution using a while loop (recommended):
x = input()
y = x.split(' ')
count = 0
i = 0
while(i < len(y) - 1):
if(int(y[i]) * 2 == int(y[i+1])):
count += 1
i += 1
print(count)
Before I continue, here are a few tips and notes: (some of them will only make sense after)
I assume the 14 in your example is a typo
I didn't put the code in a function because it's not needed, but you can change it easily.
In your code, you are passing L as a parameter to the countDouble() function, but you don't use it. if you don't need a parameter don't pass it.
when splitting the input, the values of the list are still strings. so you have to invert them to integers (for instance, you can do that with the int() 'function') before comparing their values - otherwise multiplying by 2 will just repeat the string. for example: '13'*2 is the string '1313'
I don't know why you why you added y[0] to itself in line 9, but based on the code that comes after this would yield incorrect results, you don't need to change the elements in order to get their value multiplied by 2.
notice that in the else block, nothing has changed. adding 0 to the count doesn't change it. so you can remove the else block entirely
While it's possible to solve the problem in recursion, there's something else designed for these kind of problems: loops.
The problem is essentially repeating a simple check for every element of a list.
This is how I would arrive to a solution
so we want to run the following 'code':
if(y[0]*2 == y[1]):
count += 1
if(y[1]*2 == y[2]):
count += 1
if(y[2]*2 == y[3]):
count += 1
...
of course the computer doesn't understand what "..." means, but it gives us an idea to the pattern in the code. now we can do the following:
divide the extended 'code' into similar sections.
identify the variables in the pattern - the values that change between sections
find the starting values of all variables
find a pattern in the changes of each variable
find a breaking point, a condition on one of the variables that tells us we have reached the last repeating section.
here are the steps in this specific problem:
the sections are the if statements
the variables are the indexes of the elements in y we compare
the first index starts at 0 and the second at 1
both indexes increase by one after each if-statement
when the second index is bigger then the last index of y then we already checked all the elements and we can stop
so all is left is to set the needed variables, have a while loop with the breaking condition we found, and in the while loop have the general case of the repeating sections and then the changing of the variables.
so:
x = input(f'Enter a list of numbers separated by a space: ')
y = (x.split(' '))
count = 0
# setting the starting values of the variables
index1 = 0
index2 = 1
# creating a loop with the breaking condition
while(index2 < len(y)):
# the general case of the repeated code:
if(int(y[index1]) * 2 == int(y[index2])):
count += 1
# changing the variables for the next loop
index1 += 1
index2 += 1
print(count)
We see that the index2 is just index1 + 1 at all time. so we can replace it like that:
x = input(f'Enter a list of numbers separated by a space: ')
y = (x.split(' '))
count = 0
index1 = 0
while(index1 + 1 < len(y)):
if(int(y[index1]) * 2 == int(y[index1 + 1])):
count += 1
index1 += 1
print(count)
Note: You can use a for loop similarly to the while loop
So in summary, you can use recursion to solve the problem, but the recursion would just be imitating the process of a loop:
in each call, the breaking condition will be checked, the repeated code would run and the variables/parameters would change.
Hope you find this answer useful :)
Final edit: OP edited his example so my other code didnt apply
Some good questions people are asking, but in the spirit of helping, here's a recursive function that returns the count of all doubles.
def get_doubles_count_with_recursion(a_list, count, previous=None):
while a_list:
try:
first = previous if previous else a_list.pop(0)
next_item = a_list.pop(0)
except IndexError:
return count
if next_item / 2 == first:
count += 1
return get_doubles_count_with_recursion(a_list, count, next_item)
return count
a_list = [1, 3, 5, 10, 11, 14, 28, 56, 88, 116, 232, 464, 500]
doubles = get_doubles_count_with_recursion(a_list, 0)
print(doubles == 5)
Probably could clean it up a bit, but it's a lot easier to read than the other guy's ;)
If I'm reading your question right, you want a count of all pairs where the 2nd item is double the first. (and the 14 in the first list is a typo). In which case a simple function like this should do the job:
#a = [2,4,8,16,32]
a = [-5, -10, 0, 16, 32]
count = 0
for i, x in enumerate(a):
# Stop before the list overflows
if i < len(a) - 1:
# If the next element is double the current one, increment the counter
if a[i+1] == x * 2:
count = count + 1
else:
break
print(count)

Index out of range error when creating a random list with no consecutive repetitions

I am trying to generate a list of 100 elements which consist of the numbers 1 to 4 randomly distributed, but without consecutive repetitions. I do not want to determine whether the numbers 1 to 4 occur the same number of times, I want it to be completely random except for having no consecutive repetitions. I wrote some code that seems to be doing that until it stops and says
list index out of range, however I cannot figure out why this error is happening.
from random import randint
guesses = []
for x in range (0, 99):
guess = randint(1,4)
guesses.append(guess)
if x> 0 and guesses[x] == guesses[x-1]:
guesses.remove(guess)
print(guesses)
It should look something like this:
123421342312321423124213...23142314213
Your problem is that the number keeps increasing even when you remove a number instead of decreasing. I would recommend using a while loop instead. Also, you should only add the number to your list if needed instead of adding it then removing it.
from random import randint
guesses = [randint(1,4)]
x = 1
while x < 100:
guess = randint(1,4)
if guess != guesses[x-1]:
guesses.append(guess)
x += 1
print(guesses)
here is a solution using numpy
from time import time
import numpy as np
def solve_random_non_consecutive(minValue,maxValue,size):
# initial guess
a = np.random.randint(minValue,maxValue,size)
# indexes where a[i] == a[i-1]
x = np.where(np.diff(a) == 0)[0]
# as long as we have consecutive duplicates
while len(x) > 0:
# rerandomize all indexes
a[x] = np.random.randint(minValue,maxValue,len(x))
# find all duplicates
x = np.where(np.diff(a) == 0)[0]
return a
s = time()
print(solve_random_non_consecutive(1,5,1000000))
print("Took %0.2fs to solve"%(time()-s)) # took ~ 0.17 seconds to generate 1MIL
# any of the solutions using iteration took ~ 10 seconds to generate 1 mil
some caveats are that since its repopulating the data randomly the amount of time may vary from run to run
Your code would work if instead of removing the element that matches the one before, you replace it until it doesn't:
from random import randint
guesses = [randint(1,4)]
for x in range (1, 100):
guess = randint(1,4)
guesses.append(guess)
while guesses[x] == guesses[x-1]:
guesses[x] = randint(1,4)
Two alternate ideas:
You could create a set of your choices:
{1, 2, 3, 4}
And then on each iteration ask for a random.choice from the set - the last item. choice needs something indexible so you need to convert to a list each time, but there might be some ways to make that more efficient if this is a bottleneck:
from random import choice
choices = {1, 2, 3, 4}
l = [choice(list(choices))] # start with one random choice
for i in range(99):
l.append(choice(list(choices - {l[-1]})))
This seems to be pretty uniform:
from collections import Counter
counts = Counter(l)
counts
Counter({3: 26, 2: 25, 1: 26, 4: 23})
Use Iterators
You can do this all with iterators that evaluate lazily, then just take an islice() of the length you want:
from random import randint
from itertools import tee, islice
#generator to makes random ints between start and stop
def randIt(start, stop):
while True:
yield randint(1,4)
rands, prevs = tee(randIt(1, 4))
next(prevs)
# non_dupes is a generator that makes non-repeating rands
non_dupes = (r for r, i in zip(rands, prevs) if r!=i)
# use itertools islice or a loop to get the number you want
# or just call `next(non_dupes)` for one:
list(islice(non_dupes, 0, 100))
I had a similar problem this week, my solution was that I had to adjust my counter(x it looks like for you) every time I removed an index because the array gets shorter so things start to shift around.
The problem is once you remove an element, x becomes larger than the size of your array
so guesses[x] is out of bounds because x >= guesses.size()
You're only generating 99 elements. Range(0,99) goes from 0 to 98 including 0 to total 99 elements.
Also, the part of your code which removes duplicate guesses needs to set x back to x - 1. This way the "counter" for each element you want to create is not 1 ahead of how many elements you actually have.
Additionally, when you remove this element, that method will remove the first instance of a object equal to the variable guess, not necessarily the one you just added. You should use .pop() View the example in python I screenshotted.
for x in range (0, 100):
guess = randint(1,4)
guesses.append(guess)
if x> 0 and guesses[x] == guesses[x-1]:
guesses.pop()
x = x - 1
When you remove an element from the guesses array, its length will decrement
use this code
from random import randint
guesses = []
x = 0
while x < 100:
guess = randint(1,4)
guesses.append(guess)
if x > 0 and guesses[x] == guesses[x-1]:
guesses.pop()
else:
x += 1
print(guesses)

Python for loop to iterate two times

Below is example code:
List1 = [['a'], ['b'], ['c']]
range_value = len(List1) * 2
for x in range(0, range_value):
if x == 0 or x == 1:
for y in List1[1]:
print y
if x == 2 or x == 3:
for y in List1[2]
if x == 4 or x == 5:
for y in List1[2]
This is manual steps defineing if statement in case I have big values like 100 or 1000. Its difficult, Please help me with any logic for this. first two values should use List[1] and next two values List[2] and so on.
Use a third loop:
for x in List1:
for _ in [0,1]:
for y in x:
...
This iterates over each element of List1 twice, as in the original code, without explicit index wrangling.
I think you're looking for:
for y in List1[x//2]
It's a bit hard to tell, since you started at index 1 instead of index 0, but apparently want to use every element of the list. You may need [x//2 + 1].
Another possible enhancement is to double-step your outer loop:
for x in range(0, range_value, 2):

Create a list not that random

My question is about creating a semi random list of letters in Python. I want that 20% of the time, the letter is the same than 2 letters before.
I do not want only random because if I do so it will not happen 20% of the time that n-2 is the same letter.
I was thinking about creating a first list with the letter I want and then create a new list that will take the letter from the first list but randomly, but I do not know how to add my constraint of 20%?
Finally I need it to be exactly 20% of the time
A
B
B
C
B
A
A
C
A
Like that for example... Do you have tips for me ?
Try this:
from random import random, choice
letters = ['A', 'B', 'C']
prob = 0.2
target_length = 20
target = ""
while len(target) < target_length:
if len(target) >= 2 and random() < prob:
c = target[-2]
else:
c = choice(letters)
target += c
print(target)
Here I just decide about choosing a new letter from the set or getting the letter 2 times before from the existing sequence (string target). Of course I have to consider the case when target has less than 2 letters, otherwise it's nothing to choose 2 time before.
How about this:
import random
import string
alpha = string.ascii_uppercase
my_list = [random.choice(alpha) for _ in range(2)] # initializing; first two elements have to be random
n = 50 # select the length of the list; note that 2 elements are already inside!
for i in range(n):
if random.random() < 0.2: # 20% of the time repeat a letter
my_list.append(my_list[len(my_list)-2])
else:
my_list.append(random.choice(alpha)) # 80% of the time, get a new
print(my_list) # -> ['O', 'M', 'O', 'F', 'B', 'F', 'S', 'G', ...
# ^ ^
And to check:
j = 0
for i, item in enumerate(my_list[2:], 2):
if item == my_list[i-2]:
j += 1
print(j/len(my_list)) # I got 23%. The bigger the list get's, the closer to 20% you'll get
You could try this: Create a random list and get indices that do not fulfil the condition. From those, select random indices to fulfil the condition minus the number of indices already fulfilling it. Set those indices to the element two indices prior.
lst = [random.randint(1, 10) for _ in range(20)]
not_same = [i for i in range(2, len(lst)) if i not in same]
num_same = len(lst) - len(not_same)
make_same = random.sample(not_same, len(lst)//5 - num_same)
for i in make_same:
lst[i] = lst[i-2]
Note, however, that there is the chance of getting slightly fewer or slightly more than 20%. If e.g. your list is [1,2,3,4,1] and you set 3 to 1, you gain two "same" elements instead of one, and if the list is [1,2,3,4,3] and you set the first 3 to 1, you gain one and you lose one.

Categories