Simplify counting and appending to a list - python-3.x

Okay there must be a much more cleaner way to do this. I am still an amateur programmer but I feel like they have something that can shorten this. So basically I have this data set of numbers and I am counting the occurrences of 1, 2, 3, 4, 5, 6, 7, 8, 9 as the first digit and then appending the count to a list. This definitely seems long that I have to do it in this way
countList = []
for elm in pop_num:
s = str(elm)
if (s[0] == '1'):
count1 += 1
if (s[0] == '2'):
count2 += 1
if (s[0] == '3'):
count3 += 1
if (s[0] == '4'):
count4 += 1
if (s[0] == '5'):
count5 += 1
if (s[0] == '6'):
count6 += 1
if (s[0] == '7'):
count7 += 1
if (s[0] == '8'):
count8 += 1
if (s[0] == '9'):
count9 += 1
countList.append(count1)
countList.append(count2)
countList.append(count3)
countList.append(count4)
countList.append(count5)
countList.append(count6)
countList.append(count7)
countList.append(count8)
countList.append(count9)

You can do this in two lines using collections.Counter (basically a special dict designed for counting things) and list comprehensions (a more concise syntax for writing simple loops).
Here's how I'd do it.
import collections
counts = collections.Counter(str(x)[0] for x in pop_num)
countList = [counts[str(i)] for i in range(1,10)]
Edit: Here's how to get equivalent functionality without using collections.
counts = {}
for x in pop_num:
k = str(x)[0]
counts.setdefault(k, 0)
counts[k] += 1
countList = [counts[str(i)] for i in range(1,10)]

Related

Found a recursive function summing elements in a list quite difficult to follow and I wonder if anyone could explain it to me well

def sum_recursive(L):
if len(L) == 1:
return L[0]
idx = len(L) // 2
return sum_recursive(L[:idx]) + sum_recursive(L[idx:])
I understand that the first condition is the base case for the function but I do not understand the return sum_recursive(L[:idx]) + sum_recursive(L[idx:]). Why does sum_recursive(L[:idx]) run first and sum_recursive(L[idx:] after len(L) != 1: . Also, looks like there is a sum of a list and a single index in sum_recursive(L[:idx]) + sum_recursive(L[idx:]) and that does not make sense to me. Thanks
Let's see if I can explain with an example:
sum_recursive([1,2,3,4]):
len(L) > 1, so:
idx = 4//2 = 2
return #splits list in half and sums each half
( sum_recursive([1,2])
len(L) > 1 so:
idx = 2//2 = 1
return #splits list in half again and sums each half
( sum_recursive([1])
len(l) = 1 so:
return 1
+
sum_recursive([2])
len(L) = 1 so
return 2
) = 3
+
sum_recursive([3,4])
len(L) = 2 so:
idx = 2//2 = 1
return #splits list in half abain and sums each half
( sum_recursive([3])
len(l) = 1 so:
return 3
+
sum_recursive([4])
len(L) = 1 so
return 4
) = 7
) = 10

Buggy merge sort implementation

My merge sort implementation is buggy since i am not getting two sorted list before calling merge.I am not sure what is wrong with it.
def mergeSort(arr):
if len(arr) == 1 : return arr
mid = len(arr) // 2
left_half = arr[:mid]
right_half = arr[mid:]
mergeSort(left_half)
mergeSort(right_half)
return merge(left_half,right_half)
def merge(list1,list2):
res = []
i = 0
j = 0
while i < len(list1) and j < len(list2):
if list1[i] < list2[j]:
res.append(list1[i])
i += 1
elif list1[i] > list2[j]:
res.append(list2[j])
j += 1
#Add remaining to res if any
while i < len(list1):
res.append(list1[i])
i += 1
while j < len(list2):
res.append(list2[j])
j += 1
return res
A = [5,1,2,15]
print(mergeSort(A))
My understanding of merge sort is that the space complexity is O(n) since n items in memory (in the final merge).Is quick sort preferred over merge sort just because quick sort is in-place?
I not python expert, but you should write
left_half = arr[:mid]
right_half = arr[mid:]
left_half = mergeSort(left_half)
right_half = mergeSort(right_half)
Because your mergeSort return copy of sorted array.
You have 3 mistakes in your code.
The first is that you don't handle the empty list. You need a <= instead of an == in your second line.
The second is that by simply calling mergeSort(left_half) you suppose that it will sort left_half “by reference”, which it doesn't (same with right_half).
The third is that you aren't doing anything in the case list1[i] == list2[j]. Actually you don't need that elif, you simply need an else. It doesn't matter whether you append list1[i] or list2[j] if they are equal, but you must append one of the two.
Your code should rather be:
def mergeSort(arr):
if len(arr) <= 1 : return arr
mid = len(arr) // 2
left_half = mergeSort(arr[:mid])
right_half = mergeSort(arr[mid:])
return merge(left_half, right_half)
def merge(list1, list2):
res = []
i = 0
j = 0
while i < len(list1) and j < len(list2):
if list1[i] < list2[j]:
res.append(list1[i])
i += 1
else:
res.append(list2[j])
j += 1
#Add remaining to res if any
...
As for your questions about space complexity and comparison with quicksort, there are already answers here on StackOverflow (here and here).

How to count the amount of times an object in a list appears N times in a row?

So, what I'm doing here is I'm generating 1000 coin tosses, and putting these tosses in a list. What I'm then trying to do is find how many times heads was tossed 3 times in a row, or tails was tossed 3 times in a row. My attempt:
toss_count = 0
trips_count = 0
coin_tosses = []
while toss_count < 1000:
toss = random.randint(1, 2)
if toss == 1:
coin_tosses.append("heads")
toss_count += 1
elif toss == 2:
coin_tosses.append("tails")
toss_count += 1
so up until here the code is doing what I want. My problem occurs with what I'm trying to do next though:
for trips in coin_tosses:
if "'heads', 'heads', 'heads'" in coin_tosses:
trips_count += 1
print(trips_count)
Something like this clearly does not work, but I don't know how else to achieve what I'm trying to achieve here.
you can use the following code that slice array every item in it that takes three items every item coin_tosses[counter:counter+3]
toss_count = 0
trips_count = 0
coin_tosses = []
while toss_count < 1000:
toss = random.randint(1, 2)
if toss == 1:
coin_tosses.append("heads")
toss_count += 1
elif toss == 2:
coin_tosses.append("tails")
toss_count += 1
print(coin_tosses)
counter = 0
for trips in coin_tosses:
if ['heads', 'heads', 'heads'] == coin_tosses[counter:counter+3]:
trips_count += 1
counter +=1
print(trips_count)
I'd iterate through, checking whether the value is the same as the previous two. I think this should work. It counts the total number of triples, regardless of whether they're heads or tails, which your code seems to suggest is what you're after. Can easily add a check if you need them seperately.
trips_count = 0
previous_value = None
value_before_that = None
for current_value in coin_tosses:
if (current_value == previous_value) and (current_value == value_before_that):
trips_count += 1
value_before_that = previous_value
previous_value = current_value
You can try this:
import random
toss_count = 0
trips_count = 0
coin_tosses = []
while toss_count < 1000:
toss = random.randint(1, 2)
if toss == 1:
coin_tosses.append("heads")
toss_count += 1
elif toss == 2:
coin_tosses.append("tails")
toss_count += 1
for i in range(len(coin_tosses)-2):
if coin_tosses[i] == coin_tosses[i+1] == coin_tosses[i+2]:
trips_count += 1
print(trips_count)

Is it possible to merge these two functions as one calls on another

Would like to know if it is possible to merge these two functions together it seems like it could be a singular function through recursion but i'm not sure. I don't really do a lot of programming but this works as intended i just thought it would be nice to have it as a single function. Just not sure how to program recursion any advice would be great thanks
def sieve(A, n):
for i in A:
if i % n == 0 and i > n:
A.remove(i)
return A
def primelist(N):
A = [i for i in range(2, N + 1)]
for i in A:
A = (sieve(A, i))
print(A)
Decided on a new approach and solved:
def primelist(N):
k = 0
A = [i for i in range(2, N + 1)]
while k < len(A):
for i in A:
if i % A[k] == 0 and i > A[k]:
A.remove(i)
k += 1
return(A)
Decided on a new approach and solved:
We can do better -- your solution, and that of #ikuamike, have the same issue. These lines in particular are inefficient:
for i in A:
if i % A[k] == 0 and i > A[k]:
First, when possible, we should do an easier test before a harder test, so the if should really be:
for i in A:
if i > A[k] and i % A[k] == 0:
to do the comparison (subtraction) test ahead of the modulus (division) test. (Why do all those divisions when you don't need too?)
The next issue is that all the numbers from A[0] to A[k] don't need to be tested as they're eliminated by the comparison, so why not leave them out in the first place:
for i in A[k + 1:]:
if i % A[k] == 0:
Revised code:
def primelist(N):
k = 0
A = [i for i in range(2, N + 1)]
while k < len(A):
for i in A[k + 1:]:
if i % A[k] == 0:
A.remove(i)
k += 1
return A
With N set to 10,000, you can measure the time improvement.
def primelist(N):
A = [i for i in range(2, N + 1)]
new_a = A
for i in A:
for n in new_a:
if n % i == 0 and n > i:
new_a.remove(n)
print(new_a)
This should work well for you, I just replaced the for loop in seive onto the primelist function.

Python - direct a functions output to a set

I am new to python and was going through this following code which takes in an integer and outputs the positions of 1's in its binary value.
def L(encoded_set):
print('{', end = '')
i = 0
if encoded_set:
while encoded_set % 2 == 0:
encoded_set //= 2
i += 1
print(i, end = '')
encoded_set //= 2
i += 1
while encoded_set:
if encoded_set % 2:
print(',', i,end = '')
encoded_set //= 2
i += 1
print('}')
For ex: Binary of 54 is 110110 so the code will output: {1, 2, 4, 5}
Now, i need to direct this output to a set so that i can work with the individual set elements. Something like X[0] = 1, X[1] = 2, X[2] = 4
and X[3] = 5. I'm not sure how to do this.
I would go for bit-shifting of value
ones = []
pos = 0
while val:
if val & 1:
ones.append(pos)
pos += 1
val >>= 1
Your solution numbers from first non-0 most-significant bit - which may be meaningless unless the position of MSB is known (yes, you know it, but you'll have to add extra code for that). My solution counts position relative to LSB.
As a more elegant way you can convert your number to binary with bin function and use enumerate and a list comprehension to return the proper positionsm,and return the list of indices:
>>> def pos(num) :
... return [i for i,j in enumerate(bin(num)[2:],1) if j=='1']
...
>>> pos(54)
[1, 2, 4, 5]
NOTE that the result of bin has a 0b on leading of the number so you need to loop over the slice of your binary number (bin(num)[2:]).

Resources