Algorithm for calculating the duplicates in a list - python-3.x

The code below prints out the numbers which are duplicated in A.
From my understanding, the for loop goes through each element in the list and turns it into a negative number, though i can not figure out why it does not turn the numbers it prints (which are at at position 0,4,5) negative.
A = [1,2,3,1,3,6,6]
def printRepeating(arr, size):
print("The repeating elements are: ")
for i,x in enumerate(arr):
if arr[abs(arr[i])] >= 0:
arr[abs(arr[i])] = -arr[abs(arr[i])]
print(arr)
else:
print (abs(arr[i]), end = " ")
printRepeating(A,len(A))

The algorithm assumes:
all the elements of the array start as positive numbers, and
all the elements of the array are less than the length of the array.
In your example, since the length of the array is 7, all the elements in the array must be between 1 and 6.
What the algorithm does is change array[k] to negative to indicate that k has been seen. For example, since 1 is the first number seen, array[1] is changed to a negative number. The next time 1 is seen, array[1] is already negative, so 1 must be a duplicate.

If you just want to print the repeated values in the list then why not try this:
A = [1, 2, 3, 1, 3, 6, 6]
def get_repeated_elements(lst):
return list(set((i for i in lst if lst.count(i) > 1)))
print(get_repeated_elements(A))
This function converts the passed array into a generator of duplicated values
and then converts this into a set to filter out duplicates in the generator and then converts this into a list for returning to the caller. This is a far shorter function than the one given.

The algorithm assumes that all entries are strictly positive and smaller than the length of the list. Then, what it does is essentially using the sign of the i-th element to store if it already saw number i. In your example:
A=[1,2,3,1,3,6,6] Take 1
A[1] is positive, i.e. we have not seen it. Make it negative to mark it
A=[1,-2,3,1,3,6,6] Take -2 (stands for 2)
A[2] is positive, i.e. we have not seen it. Make it negative to mark it
A=[1,-2,-3,1,3,6,6] Take -3
A[3] is positive, i.e. we have not seen it. Make it negative to mark it
A=[1,-2,-3,-1,3,6,6] Take -1
A[1] is negative, i.e. we have already seen it. Report it.
A=[1,-2,-3,-1,3,6,6] Take 3
A[3] is negative, i.e. we have already seen it. Report it.
...

The below code can be used to find repeated elements in the list and also unique elements in the list.
from collections import Counter
A = [1,2,3,1,3,6,6]
B = Counter(A)
The below line prints repeated elements.
[k for k, v in B.items() if v > 1]
Output : [1, 3, 6]
The below line prints unique elements.
[k for k, v in B.items() if v == 1]
Output : [2]

Related

How to pass an element in list as an index to a list in python?

Given a sorted list [1,2,3,4,5,6,7,8] and a shuffled version of that list
a=[1,2,5,3,7,8,6,4]
I want to find how many steps each element has moved from its position relative to the sorted list (steps towards index 0 being positive, steps away from index 0 being negative) and store those step values in the respective index in a different list.
For the given input, the expected output is:
b=[0,0,-1,-4,2,-1,2,2]
Considering the value 3 (which is at index 2 in the sorted list), in the shuffled array it is at index 3, so it has taken -1 step, and hence b[2] = -1. Similarly for the value 8 (which is at index 7 in the sorted list), in the shuffled array it is at index 5, so it has taken +2 steps, and hence b[7] = 2.
This is what I have tried:
b=[0,0,0,0,0,0,0,0]
a=[1,2,5,3,7,8,6,4]
for x in range(len(a)):
b[a[x]]=a[x]-(x+1)
I am getting an error with this code:
IndexError: list assignment index out of range
Please help me understand this. Why can't I pass an integer from a list as an index to another list?
You can get the result you want with a simple comparison of the expected value at an array position with the index of that value in the a array. By iterating over the number of elements in a, we can generate the output array:
a = [1,2,5,3,7,8,6,4]
b = []
for x in range(len(a)):
b.append(x - a.index(x+1))
print(b)
Output
[0, 0, -1, -4, 2, -1, 2, 2]
Demo on rextester
The problem as you very well know is that there is an index error.
a=[1,2,5,3,7,8,6,4]
The above array has size = 8.
Now you create b:
b=[0,0,0,0,0,0,0,0]
The size of the list b is also 8.
In your for loop you are doing:
b[a[x]]
Pause...
If x is 5, then a[x] will be 8. However, there is no such thing as index 8 in list b, which only has up to index 7.
This is the problem.
You should change the code in your for-loop to:
b[a[x] - 1]=a[x]-(x+1)
I would suggest you change your loop to this:
for x, n in enumerate(a):
b[n-1] = n-(x+1)
Index starts from 0 to n-1
,so in b=[0,0,0,0,0,0,0,0] (having 8 elements) index will start from 0 to 7 and list 'a=[1,2,5,3,7,8,6,4]' contains numbers from 1-8 hence 8 is out of index.
corrected code:
b=[0,0,0,0,0,0,0,0]
a=[1,2,5,3,7,8,6,4]
for x in range(len(a)):
b[a[x] - 1]=a[x]-(x + 1)
Avoiding any indexing, nor unnecessary offsets, we can store the elements in a dictionary (the element as the key and its move as the value)
a=[1,2,5,3,7,8,6,4]
d = {v: v-e for e,v in enumerate(a, 1)}
b = [v for _, v in sorted(d.items())]
print(b)
produces
[0, 0, -1, -4, 2, -1, 2, 2]

create list from list where values only increase by 1

I have the code below that gets the maximum value from a list. It then compares it to the maximum value of the remaining values in the list, and if it is more than 1 higher than the next greatest value, it replaces the original list maximum with 1 higher than the next greatest value. I would like the code to search the entire list and make sure that any value in the list is at most 1 larger than any other value in the list. I know this ins’t the best worded explanation, I hope the example lists below make what I’m trying to accomplish clearer.
for example I don’t want to get a final list like:
[0,2,0,3]
I would want the final list to be
[0,1,0,2]
input:
empt=[0,2,0,0]
Code:
nwEmpt=[i for i in empt if i !=max(empt)]
nwEmpt2=[]
for i in range(0,len(empt)):
if (empt[i]==max(empt))&(max(empt)>(max(nwEmpt)+1)):
nwEmpt2.append((max(nwEmpt)+1))
elif (empt[i]==max(empt))&(max(empt)==(max(nwEmpt)+1)):
nwEmpt2.append(max(empt))
else:
nwEmpt2.append(empt[i])
output:
nwEmpt2
[0,1,0,0]
min_value = min(empt)
empt_set = set(empt)
for i in empt:
nwEmpt.append(min_value + len(list(filter(lambda x: x < i, empt_set))))
This gives e.g. for input empt = [8, 10, 6, 4, 4] output nwEmpt = [6, 7, 5, 4, 4].
It works by mapping each element to (the minimum value) + (the number of distinct values smaller than element).

python3 functional programming: Accumulating items from a different list into an initial value

I have some code that performs the following operation, however I was wondering if there was a more efficient and understandable way to do this. I am thinking that there might be something in itertools or such that might be designed to perform this type of operation.
So I have a list of integers the represents changes in the number of items from one period to the next.
x = [0, 1, 2, 1, 3, 1, 1]
Then I need a function to create a second list that accumulates the total number of items from one period to the next. This is like an accumulate function, but with elements from another list instead of from the same list.
So I can start off with an initial value y = 3.
The first value in the list y = [3]. The I would take the second
element in x and add it to the list, so that means 3+1 = 4. Note that I take the second element because we already know the first element of y. So the updated value of y is [3, 4]. Then the next iteration is 4+2 = 6. And so forth.
The code that I have looks like this:
def func():
x = [0, 1, 2, 1, 3, 1, 1]
y = [3]
for k,v in enumerate(x):
y.append(y[i] + x[i])
return y
Any ideas?
If I understand you correctly, you do what what itertools.accumulate does, but you want to add an initial value too. You can do that pretty easily in a couple ways.
The easiest might be to simply write a list comprehension around the accumulate call, adding the initial value to each output item:
y = [3 + val for val in itertools.accumulate(x)]
Another option would be to prefix the x list with the initial value, then skip it when accumulate includes it as the first value in the output:
acc = itertools.accumulate([3] + x)
next(acc) # discard the extra 3 at the start of the output.
y = list(acc)
Two things I think that need to be fixed:
1st the condition for the for loop. I'm not sure where you are getting the k,v from, maybe you got an example using zip (which allows you to iterate through 2 lists at once), but in any case, you want to iterate through lists x and y using their index, one approach is:
for i in range(len(x)):
2nd, using the first append as an example, since you are adding the 2nd element (index 1) of x to the 1st element (index 0) of y, you want to use a staggered approach with your indices. This will also lead to revising the for loop condition above (I'm trying to go through this step by step) since the first element of x (0) will not be getting used:
for i in range(1, len(x)):
That change will keep you from getting an index out of range error. Next for the staggered add:
for i in range(1, len(x)):
y.append(y[i-1] + x[i])
return y
So going back to the first append example. The for loop starts at index 1 where x = 1, and y has no value. To create a value for y[1] you append the sum of y at index 0 to x at index 1 giving you 4. The loop continues until you've exhausted the values in x, returning accumulated values in list y.

When I call the function it shows nothing?

A palindromic number reads the same both ways. The largest palindrome made from the product of two 2-digit numbers is 9009 = 91 × 99.
Find the largest palindrome made from the product of two 3-digit numbers.
This is my script in python 3.5:
def plin1():
for i in range(1000,1):
for j in range(1000,1):
if str(i*j)==str(i*j)[::-1]:
break
return i*j
print(i,'*',j,'=',i*j)
But when I call this function, nothing is printed. What's wrong?
range(start, stop[, step]) -> range object
range() requires the first argument to be less than the second when step is positive, and step defaults to 1 when range() is called with 2 arguments. Hence the range object yields an empty sequence and the body of the for loop is never entered:
>>> list(range(1000, 1))
[]
However, by specifying a negative value for step, you can get the sequence that you require e.g.:
>>> list(range(10, 1))
[]
>>> list(range(10, 1, -1))
[10, 9, 8, 7, 6, 5, 4, 3, 2]
So in your case you would do this:
for i in range(999, 99, -1):
for j in range(999, 99, -1):
# etc...
which would then iterate over all 3 digit numbers starting at the largest.
Also the use of return is incorrect and it will cause the function to terminate without printing anything. Here is a corrected version:
def plin1():
for i in range(999, 99, -1):
for j in range(999, 99, -1):
if str(i*j) == str(i*j)[::-1]:
return i*j
print(i,'*',j,'=',i*j)
Now the function returns the value only when it finds a palindrome. If there were no palindromes, the function would return None.
Notice, however, that this does not produce the largest palindrome that is the product of two 3 digit numbers. This function returns 580085 which is 995 * 583. But the largest one is 993 * 913 = 906609. You need to figure out which is the largest palindrome across all products, not just the first palindrome found. Here is a generator expression that will produce the required result:
max((i*j,i,j) for i in range(100, 1000) for j in range(100, 1000)
if str(i*j) == str(i*j)[::-1])
Which produces the tuple (906609, 993, 913).
Probably there are optimisations that could be made to reduce the number of calculations.
range with two arguments i, j generates the range from i to j. If i > j then there is no range to generate, and when iterating over that range nothing happens.
Range also takes a "step" parameter, allowing you to set the increment of the range. Perhaps you meant to use that. It would look like this:
for i in range(1, 1000, 1):
# loop body
Note that specifying a step of 1 is redundant, as 1 is the default value for that parameter.
If you want to generate a "do the right thing" range to generate the range from the lesser of (i, j) to the greater, you might write a function like the following:
def dwim_range(i,j):
return range(*sorted((i,j)))
Here you're sorting the tuple (i, j) and then unpacking it with the * operator so that range gets the two items of the tuple, in sorted order.

How to get list of indices for elements whose value is the maximum in that list

Suppose I have a list l=[3,4,4,2,1,4,6]
I would like to obtain a subset of this list containing the indices of elements whose value is max(l).
In this case, list of indices will be [1,2,5].
I am using this approach to solve a problem where, a list of numbers are provided, for example
l=[1,2,3,4,3,2,2,3,4,5,6,7,5,4,3,2,2,3,4,3,4,5,6,7]
I need to identify the max occurence of an element, however in case more than 1 element appears the same number of times,
I need to choose the element which is greater in magnitude,
suppose I apply a counter on l and get {1:5,2:5,3:4...}, I have to choose '2' instead of '1'.
Please suggest how to solve this
Edit-
The problem begins like this,
1) a list is provided as an input
l=[1 4 4 4 5 3]
2)I run a Counter on this to obtain the counts of each unique element
3)I need to obtain the key whose value is maximum
4)Suppose the Counter object contains multiple entries whose value is maximum,
as in Counter{1:4,2:4,3:4,5:1}
I have to choose 3 as the key whose value is 4.
5)So far, I have been able to get the Counter object, I have seperated key/value lists using k=counter.keys();v=counter.values()
6)I want to get the indices whose values are max in v
If I run v.index(max(v)), I get the first index whose value matches max value, but I want to obtain the list of indices whose value is max, so that I can obtain corresponding list of keys and obtain max key in that list.
With long lists, using NumPy or any other linear algebra would be helpful, otherwise you can simply use either
l.index(max(l))
or
max(range(len(l)),key=l)
These however return only one of the many argmax's.
So for your problem, you can choose to reverse the array, since you want the maximum that appears later as :
len(l)-l[::-1].index(max(l))-1
If I understood correctly, the following should do what you want.
from collections import Counter
def get_largest_most_freq(lst):
c = Counter(lst)
# get the largest frequency
freq = max(c.values())
# get list of all the values that occur _max times
items = [k for k, v in c.items() if v == freq]
# return largest most frequent item
return max(items)
def get_indexes_of_most_freq(lst):
_max = get_largest_most_freq(lst)
# get list of all indexes that have a value matching _max
return [i for i, v in enumerate(lst) if v == _max]
>>> lst = [3,4,4,2,1,4,6]
>>> get_largest_most_freq(lst)
4
>>> get_indexes_of_most_freq(lst)
[1, 2, 5]
>>> lst = [1,2,3,4,3,2,2,3,4,5,6,7,5,4,3,2,2,3,4,3,4,5,6,7]
>>> get_largest_most_freq(lst)
3
>>> get_indexes_of_most_freq(lst)
[2, 4, 7, 14, 17, 19]

Resources