Longest sequence of consecutive duplicates in a python list - python

As mentioned, a run is a sequence of consecutive repeated values. Implement a Python function called longest_run that takes a list of numbers and returns the length of the longest run. For example in the sequence:
2, 7, 4, 4, 2, 5, 2, 5, 10, 12, 5, 5, 5, 5, 6, 20, 1 the longest run has length 4. Then, in the main, your program should ask the user to input the list, then it should call longest_run function, and print the result.
This is what I tried but it only returns 1 and I don't understand why. I can't import any modules for this question.
def longest_run(aList):
'''(list)->int
Returns length of the longest run
Precondition: aList is a list of a len of at least 2 and elements of list are ints
'''
count=0
bucket=[]
for i in aList:
if bucket==i:
count=count+1
else:
bucket=i
count=1
return count

The biggest mistake of your code is to set bucket=[] (which is a list) and later to an integer.
Also, you need to store the longest sequence and the current sequence length (initialized to 1) and the last seen value, so more variables than you're storing.
Each time value is the same as before, increase counter. If it's different, reset counter after having checked if it's not greater than the max. In the end perform the max test again just in case the longest sequence is in the end (classic mistake)
like this:
seq = [2, 7, 4, 4, 2, 5, 2, 5, 10, 12, 5, 5, 5, 5, 6, 20, 1]
result=1
max_result=0
last_seen=seq[0]
for v in seq[1:]:
if v==last_seen:
result += 1
else:
if result > max_result:
max_result = result
last_seen = v
result = 1
# just in case the longest sequence would be at the end of your list...
if result > max_result:
max_result = result
print(max_result)
When you're finally allowed to use python batteries, use itertools.groupby and compute the max of the sequence lengths:
max(sum(1 for x in v) for _,v in itertools.groupby(seq))

You get 1 since when your loop is in it's final iteration:
bucket = 20 and i = 1 which means bucket != i so the loop enters the else clause and assigns count = 1 exits and the function returns count which is 1.
Suggestions:
1) When you encounter a bug like this try running through the code/logic manually - it helps.
2) For this question specifically - whenever a run ends you forget the last run length, think about how you can "remember" the longest run.

So you're trying to find the longest run of the same number in your list? Because it's kinda confusing, what you were trying to code.
You should keep two versions of count: maxCount (the one which you're gonna return) and actualCount (the one you're gonna increment), iterate through the list and compare number with the next one. If it's the same actualCount += 1 if not, actualCount = 0 at the end of every iteration, compare maxCount and actualCount and if actualCount is bigger than maxCount = actualCount.
def longest_run(aList):
maxCount = 1
actualCount = 1
for i in range(len(aList)-1):
if aList[i] == aList[i+1]:
actualCount += 1
else:
actualCount = 1
if actualCount > maxCount:
maxCount = actualCount
return(maxCount)

You can use the following method:(Number of repetitions of each number)
mylist = [2, 7, 4, 4, 2, 5, 2, 5, 10, 12, 5, 5, 5, 5, 6, 20, 1]
my_dict = {i:mylist.count(i) for i in mylist}
mylist = list(dict.fromkeys(mylist))
R_list=[]
for i in mylist:
print("%s repeated %s" %(i,my_dict[i]))
R_list = R_list+[my_dict[i]]
print(R_list)
print(max(R_list))

Related

replacing list items in reverse order and skipping every other item python

I am making a program to check whether a card number is potentially valid using the Luhn algorithm.
num = "79927398713" #example num
digits = [int(x) for x in num]
reverse = digits[1:][::2][::-1] #step 1: start from rightmost digit, skip first, skip every other
count = 0
digitsum = 0
print(reverse) #output here is: [1, 8, 3, 2, 9]
for x in (reverse):
reverse[count] *= 2
if reverse[count] > 9:
for x in str(reverse[count]): #multiply each digit in step 1 by 2, if > 9, add digits to make single-digit number
digitsum += int(x)
reverse[count] = digitsum
count += 1
digitsum = 0
count = 0
print(reverse) #output here is [2, 7, 6, 4, 9]
basically, I want to input [2, 7, 6, 4, 9] back into the corresponding places in the list digits. It would look like this (changed numbers in asterisks)
[7, **9**, 9, **4**, 7, **6**, 9, **7**, 7, **2**, 3]
the problem is, I would have to read digits backwards, skipping the first (technically last) element, and skipping every other element from there, replacing the values each time.
Am I going about this the wrong way/making it too hard on myself? Or is there a way to index backwards, skipping the first (technically last) element, and skipping every other element?
You can do this with simple indexing
Once you have the variable reverse, you can index on the left hand side:
# reversed is [2, 7, 6, 4, 9] here
digits[1::2] = reversed(reverse) # will place 9,4,6,7,2 in your example
Note, you can use this trick too for your line where you initialize reverse
reverse = digits[1::2][::-1]
I think you could even use:
reverse = digits[-1 - len(digits) % 2::-2]
which should be even more efficient
Edit
Running timeit, the last solution of digits[-1 - len(digits) % 2::-2] on an array of size 10,000 was 3.6 times faster than the original, I'd highly suggest using this

Sorting repeated elements

What I try to do is to write a function sort_repeated(L) which returns a sorted list of the repeated elements in the list L.
For example,
>>sort_repeated([1,2,3,2,1])
[1,2]
However, my code does not work properly. What did I do wrong in my code?
def f5(nums):
count = dict()
if not nums:
for num in nums:
if count[num]:
count[num] += 1
else:
count[num] = 1
return sorted([num for num in count if count[num]>1])
return []
if count[num]: will fail if the dictionary doesn't have the key already. Take a look at the various counter recipes on this site and use one instead.
Also, not nums is true if nums is an empty sequence, which means that the loop body will never be executed. Invert the condition.
Use a counter and check for values greater than 1
from collections import Counter
def sort_repeated(_list):
cntr = Counter(_list)
print sorted([x for x in cntr.keys() if cntr[x] > 1])
sort_repeated([7, 7, 1, 2, 3, 2, 1, 4, 3, 4, 6, 5])
>> [1, 2, 3, 4, 7]

What's wrong with my python selection sort?

I can't figure out why the python program produces the following output:
c:\Python Programs>selection_sort.py
[7, 4, 2, 9, 6]
[2, 4, 7, 9, 6]
[2, 6, 7, 9, 4]
[2, 6, 4, 9, 7]
Traceback (most recent call last):
File "J:\Python Programs\Python Practice\selection_sort.py", line 11, in <modu
le>
num_list[i], num_list[min_num] = num_list[min_num], num_list[i]
IndexError: list index out of range
c:\Python Programs>
I think I understand the list index out of range part, but I'm not sure about why the 6 becomes the second element when i = 1. Didn't the machine read my if statement?
Here is the code below:
num_list = [7,4,2,9,6]
len_num_list = len(num_list)
print num_list
print""#print empty string to separate the original list from the following iterations
for i in range(0,len_num_list):
min_num = min(num_list[i:]) #finds minimum number in list to the right of i
if min_num>num_list[i]:
min_num = num_list[i]
num_list[i], num_list[min_num] = num_list[min_num], num_list[i]
print num_list
First, let's note that in your snippet:
min_num = min(num_list[i:]) #finds minimum number in list to the right of i
if min_num>num_list[i]:
min_num = num_list[i]
the if will never, ever match -- since min_num is the minimum of a sub-list that starts with num_list[i], it can't possibly, ever, under any circumstance, be greater than the latter.
So, lose the last two of these three statements -- they're about as useful as checking if 2+2 != 4::-).
Next, let's note that you don't really want min_num to be a value (which is what your call to min gives you) -- you want it to be an index into the list, in order to perform the swap:
num_list[i], num_list[min_num] = num_list[min_num], num_list[i]
But trying to turn a value into an index via the index method is quite an iffy path: if the input list can have any duplicates, index will always locate the first one of them, and that might quite possibly tangle you up. I personally would choose not to go there.
Rather consider the more direct path of finding the minimum index using the corresponding value via the key= feature of min! I.e:
for i in range(0,len_num_list):
min_ind = min(range(i, len_num_list),
key=lambda j: num_list[j])
num_list[i], num_list[min_ind] = num_list[min_ind], num_list[i]
print num_list
If you're not familiar with the key= feature of many Python built-ins (min, max, sorted, ...), it's really a good thing to learn.
It sorts (or gives the min, or max, or) a certain sequence, with the comparisons done after passing each item of the sequence through the "key extraction function" you pass as key=. Here, you want "the index of the minimum" and you get that by picking the min index with a key= of the corresponding look-up of each index into the list.
I personally dislike lambda and might use key=numlist.__getitem__, but that's not very readable either -- most readable is always to use def (and I'd do the same for that swap functionality), e.g...:
def item_in_list(index): return num_list[index]
def swap(i, j): num_list[i], num_list[j] = num_list[j], num_list[i]
for i in range(0,len_num_list):
min_ind = min(range(i, len_num_list), key=item_in_list)
swap(i, min_ind)
print num_list
which I find to be the most readable and elegant approach to this task.
The problem is that min(num_list[i:]) returns a number from the list, not an index into that list. You can use the index method to get the index corresponding to min(num_list[i:]). Thus, try:
num_list = [7,4,2,9,6]
len_num_list = len(num_list)
print num_list
print""#print empty string to separate the original list from the following iterations_
for i in range(0,len_num_list):
min_num = min(num_list[i:]) #finds minimum number in list to the right of i
j = num_list.index(min_num)
if min_num>num_list[i]:
min_num = num_list[i]
num_list[i], num_list[j] = num_list[j], num_list[i]
print num_list
This produces the output:
[7, 4, 2, 9, 6]
[2, 4, 7, 9, 6]
[2, 4, 7, 9, 6]
[2, 4, 6, 9, 7]
[2, 4, 6, 7, 9]
[2, 4, 6, 7, 9]

Recursive function that takes in a list and an integer and returns the integer in the list in order

So let's say I this function:
squeeze([1,4,7,9], 8)
squeeze([1,4,7,9], 0)
I would want the function to return a new list containing:
[1,4,7,8,9]
[0,1,4,7,9]
I want to make this function using recursion, but I'm having trouble
def squeeze(x:list, num:int):
if len(x) == 1:
if num < x[0]:
return [num] + x #if the integer is less than the 1st value, put it in the front
elif x[0] < num < x[2]:
return [x[0]] + [num] + [x[2]] #put it in the list
#insert this number into the correct spot
else:
return squeeze(num, x[0:1]) + squeeze(num, x[1:]) #i don't think this is right
I'm having trouble comparing the numbers in the list and putting it in the correct spot by using recursion.
You can do like this
def squeeze(myList, num):
if myList == []:
return [num]
elif num > myList[0]:
return [myList[0]] + squeeze(myList[1:], num)
else:
return [num] + myList
print squeeze([1,4,7,9], 10)
print squeeze([1,4,7,9], 8)
print squeeze([1,4,7,9], 0)
Output
[1, 4, 7, 9, 10]
[1, 4, 7, 8, 9]
[0, 1, 4, 7, 9]
Explanation
If myList is empty, return the num as list
If the num is greater than the first element of the myList, num cannot fit here. So, we recurse further leaving the first element of the myList behind. (myList[1:] means that without 0th element). For example, myList is [4, 7, 9] and num is 8. So, we leave 4 behind and recurse with [7, 9] and 8, still 8 is greater than 7, so now we recurse with [9] this time, 8 is smaller than 9, so we return [8, 9] by the else part. And when the recursion unwinds, we get [7, 8, 9] and then finally [4, 7, 8, 9]
If the num is lesser than or equal to first element of myList, then that is where we need to place num. So, we simply place num at the beginning of myList and return it.
OK, there is already an answer upvoted and accepted BUT on the chance that this is not just a leaning/homework thing and someone actually wants to do this, there's a module for this.
check out what's available in bisect.
from bisect import insort
a = [1,4,7,9]
insort(a, 10)
print a
a = [1,4,7,9]
insort(a, 8)
print a
a = [1,4,7,9]
insort(a, 0)
print a
output is what is expected but there is also insort_left and insort_right to fine-tune your policy on ties.
Note: this does not return a new list, it modifies the old but is easily wrapped with a copy of the original first leaving you with a two line squeeze function.

Cannot iterate through two lists at once python

I am having a problem with my python code, but I am not sure what it is. I am creating a program that creates a table with all possible combinations of four digits provided the digits do not repeat, which I know is successful. Then, I create another table and attempt to add to this secondary table all of the values which use the same numbers in a different order (so I do not have, say, 1234, 4321, 3241, 3214, 1324, 2413, etc. on this table.) However, this does not seem to be working, as the second table has only one value. What have I done wrong? My code is below. Oh, and I know that the one value comes from appending the 1 at the top.
combolisttwo = list()
combolisttwo.append(1)
combolist = {(a, b, c, d) for a in {1, 2, 3, 4, 5, 6, 7, 8, 9, 0} for b in {1, 2, 3, 4, 5, 6, 7, 8, 9, 0} for c in {1, 2, 3, 4, 5, 6, 7, 8, 9, 0} for d in {1, 2, 3, 4, 5, 6, 7, 8, 9, 0} if a != b and a != c and a != d and b != c and b != d and c!=d}
for i in combolist:
x = 0
letternums = str(i)
letters = list(letternums)
for g in letters:
n = 0
hits = 0
nonhits = 0
letterstwo = str(combolisttwo[n])
if g == letterstwo[n]:
hits = hits + 1
if g != letterstwo[n]:
nonhits = nonhits + 1
if hits == 4:
break
if hits + nonhits == 4:
combolisttwo.append(i)
break
x = len(combolisttwo)
print (x)
All possible combinations of four digits provided the digits do not repeat:
import itertools as IT
combolist = list(IT.combinations(range(10), 4))
Then, I create another table and attempt to add to this secondary table all of the values which use the same numbers in a different order (so I do not have, say, 1234, 4321, 3241, 3214, 1324, 2413, etc. on this table.):
combolist2 = [item for combo in combolist
for item in IT.permutations(combo, len(combo))]
Useful references:
combinations -- for enumerating collections of elements without replacement
permutations -- for enumerating collections of elements in all possible orders
This code is pretty confused ;-) For example, you have n = 0 in your inner loop, but never set n to anything else. For another, you have x = 0, but never use x. Etc.
Using itertools is really best, but if you're trying to learn how to do these things yourself, that's fine. For a start, change your:
letters = list(letternums)
to
letters = list(letternums)
print(letters)
break
I bet you'll be surprised at what you see! The elements of your combolist are tuples, so when you do letternums = str(i) you get a string with a mix of digits, spaces, parentheses and commas. I don't think you're expecting anything but digits.
Your letterstwo is the string "1" (always, because you never change n). But it doesn't much matter, because you set hits and nonhits to 0 every time your for g in letters loop iterates. So hits and nonhits can never be bigger than 1.
Which answers your literal question ;-) combolisttwo.append(i) is never executed because
hits + nonhits == 4 is never true. That's why combolisttwo remains at its initial value ([1]).
Put some calls to print() in your code? That will help you see what's going wrong.

Categories

Resources