python - checking if an array consisting of N integers is a permutation - python

I am analyzing the routine which checks if an array of N integers is a permutation (sequence containing each element from 1 to N).
I am new to python. I can't grasp how this routine gets the correct answer. Could anybody explain the logic behind the loop? especially the use of the counter[element-1].
Is the counter a built-in function working on every element of A? does the counter[element-1] reference position/value of elements of A by default because the loop is defined on an array?
A=[4,1,3,2]
def solution(A):
counter = [0]*len(A)
limit = len(A)
for element in A:
if not 1 <= element <= limit:
return 0
else:
if counter[element-1] != 0:
return 0
else:
counter[element-1] = 1
return 1
Update:
I modified the code to see the values used within the loop, for example
def solution(A):
counter = [0]*len(A)
limit = len(A)
for element in A:
if not 1 <= element <= limit:
print element
print 'outside'
return 0
else:
if counter[element-1] != 0:
print 'element %d' % element
print [element-1]
print counter[element-1]
return 0
else:
counter[element-1] = 1
print 'element %d' % element
print [element-1]
print counter[element-1]
return 1
gives me
element 4
[3]
1
element 1
[0]
1
element 3
[2]
1
element 2
[1]
1
1
I still don't get the logic. For example fot the first element, why [3] gives 1?

The idea behind the code is twofold. A permutation of the list [1, 2, ..., N] has two properties. It has only elements between 1 and N and each element just appear one time in the list.
I will try explain it to you part by part this idea in the code.
def solution(A):
counter = [0]*len(A)
limit = len(A)
Assume as an example, a list [1, 3, 2].
counter is initialized as a list of zeros of size len(A) = 3. Each 0 correspond to one of the elements of the list
for element in A:
if not 1 <= element <= limit:
return 0
This part condition is the most easy one. If the element is not in this range, the list cannot be a permutation of [1, 2,...N]. For instance, [1, 3, 2] is a permutation of [1, 2, 3] but [1, 6, 2] is not.
else:
if counter[element-1] != 0:
return 0
else:
counter[element-1] = 1
This next part is related with the uniqueness of each term. The if checks if a number = element has already passed through this loop. The second else make sure that this number is marked, so if a repeated number is found in the next iterations, the if will be true and return 0.
For instance, for the list [1, 2, 2]. The first 2 would not trigger the if, while the second 2 would trigger it, returning 0. On the other hand, [1, 3, 2], would never trigger the if.
If all the number pass this conditions, the two properties were true and the list is a permutation.

Quite a cunning algorithm actually.
The input is a sequence of length N.
Each element of input is presumed to be an integer (if not, either comparison or indexing will throw an exception).
counter is an array of flags - of length N, too.
No integers outside of [1,N] range are allowed
No duplicates are allowed (see how it's done)
Can you now prove that the only way for both conditions to stay true is for the sequence to be a permutation?

Related

Find missing elements in a list created from a sequence of consecutive integers with duplicates in O(n)

This is a Find All Numbers Disappeared in an Array problem from LeetCode:
Given an array of integers where 1 ≤ a[i] ≤ n (n = size of array),
some elements appear twice and others appear once.
Find all the elements of [1, n] inclusive that do not appear in this array.
Could you do it without extra space and in O(n) runtime? You may
assume the returned list does not count as extra space.
Example:
Input:
[4,3,2,7,8,2,3,1]
Output:
[5,6]
My code is below - I think its O(N) but interviewer disagrees
def findDisappearedNumbers(self, nums: List[int]) -> List[int]:
results_list=[]
for i in range(1,len(nums)+1):
if i not in nums:
results_list.append(i)
return results_list
You can implement an algorithm where you loop through each element of the list and set each element at index i to a negative integer if the list contains the element i as one of the values,. You can then add each index i which is positive to your list of missing items. It doesn't take any additional space and uses at the most 3 for loops(not nested), which makes the complexity O(3*n), which is basically O(n). This site explains it much better and also provides the source code.
edit- I have added the code in case someone wants it:
#The input list and the output list
input = [4, 5, 3, 3, 1, 7, 10, 4, 5, 3]
missing_elements = []
#Loop through each element i and set input[i - 1] to -input[i - 1]. abs() is necessary for
#this or it shows an error
for i in input:
if(input[abs(i) - 1] > 0):
input[abs(i) - 1] = -input[abs(i) - 1]
#Loop through the list again and append each positive value to output list
for i in range(0, len(input)):
if input[i] > 0:
missing_elements.append(i + 1)
For me using loops is not the best way to do it because loops increase the complexity of the given problem. You can try doing it with sets.
def findMissingNums(input_arr):
max_num = max(input_arr) # get max number from input list/array
input_set = set(input_arr) # convert input array into a set
set_num = set(range(1,max(input_arr)+1)) #create a set of all num from 1 to n (n is the max from the input array)
missing_nums = list(set_num - input_set) # take difference of both sets and convert to list/array
return missing_nums
input_arr = [4,3,2,7,8,2,3,1] # 1 <= input_arr[i] <= n
print(findMissingNums(input_arr)) # outputs [5 , 6]```
Use hash table, or dictionary in Python:
def findDisappearedNumbers(self, nums):
hash_table={}
for i in range(1,len(nums)+1):
hash_table[i] = False
for num in nums:
hash_table[num] = True
for i in range(1,len(nums)+1):
if not hash_table[i]:
print("missing..",i)
Try the following :
a=input() #[4,3,2,7,8,2,3,1]
b=[x for x in range(1,len(a)+1)]
c,d=set(a),set(b)
print(list(d-c))

Index out of Range despite check

t = [1, 2, 3]
def cumsum(t):
t2 = []
total = 0
i = 0
while i < len(t):
total += t[i]
t2[i].append(total)
i += 1
return t2
cumsum(t)
This code takes the sum of the first two list integers and appends it to another list.
I feel like this should logically work and I don't understand why it is producing an index error if i < len(t) when len(t)= 3. So ideally t2 =[1, 3, 6]
while the iterator is less than len(t) (which is 3) add the list item to the total variable then append the total to the new list then iterate.
Because you are using index i to access t2 list that is empty. To append an element to a list you should use <list>.append(<element>), that is t2.append(total) in your case.

Cannot fix "list index out of range"

I'm trying to write a simple code in python to find the first missing positive integer. My algorithm is to create an array full of zeros with the size of maximum positive integer in the input array+1 (for example if the maximum number is 7, the size of 0's array would be 8). Then I trace the input array and whenever I find a positive number I change the index value+1 in the second array to 1. This is my code:
def minPositive(a):
max_a = max(a)
b = [0]*(max_a+1) # This is the second array initialized to zero
for i in range(len(a)):
if a[i] > 0:
b[a[i]+1]= 1
for j in range(len(b)):
if j != 0:
if b[j] == 0:
return j
But when I code this I face "List index out of range". I traced my program several times but I cannot find the error.
Python indexes from 0, so a list of length n has no nth element. Likewise, a list with n+1 elements has no n+1th element.
One option is for every positive value in a (rather than the value plus 1), the index corresponding to that value in b will be set to 1. You could rewrite your function like this (simplified a bit):
def minPositive(a):
b = [1 if n in a and n > 0 else 0 for n in range(max(a) + 1)]
return b
Or you could just make your list b one element longer.

Solving the "firstDuplicate" question in Python

I'm trying to solve the following challenge from codesignal.com:
Given an array a that contains only numbers in the range from 1 to a.length, find the first duplicate number for which the second occurrence has the minimal index. In other words, if there are more than 1 duplicated numbers, return the number for which the second occurrence has a smaller index than the second occurrence of the other number does. If there are no such elements, return -1.
Example
For a = [2, 1, 3, 5, 3, 2], the output should be
firstDuplicate(a) = 3.
There are 2 duplicates: numbers 2 and 3. The second occurrence of 3 has a smaller index than the second occurrence of 2 does, so the answer is 3.
For a = [2, 4, 3, 5, 1], the output should be
firstDuplicate(a) = -1.
The execution time limit is 4 seconds.
The guaranteed constraints were:
1 ≤ a.length ≤ 10^5, and
1 ≤ a[i] ≤ a.length
So my code was:
def firstDuplicate(a):
b = a
if len(list(set(a))) == len(a):
return -1
n = 0
answer = -1
starting_distance = float("inf")
while n!=len(a):
value = a[n]
if a.count(value) > 1:
place_of_first_number = a.index(value)
a[place_of_first_number] = 'string'
place_of_second_number = a.index(value)
if place_of_second_number < starting_distance:
starting_distance = place_of_second_number
answer = value
a=b
n+=1
if n == len(a)-1:
return answer
return answer
Out of the 22 tests the site had, I passed all of them up to #21, because the test list was large and the execution time exceeded 4 seconds. What are some tips for reducing the execution time, while keeping the the code more or less the same?
As #erip has pointed out in the comments, you can iterate through the list, add items to a set, and if the item is already in a set, it is a duplicate that has the lowest index, so you can simply return the item; or return -1 if you get to the end of the loop without finding a duplicate:
def firstDuplicate(a):
seen = set()
for i in a:
if i in seen:
return i
seen.add(i)
return -1
Create a new set and find its already in the new list, if its there return the element:
def firstDuplicate(a):
dup = set()
for i in range(len(a)):
if a[i] in dup:
return a[i]
else:
dup.add(a[i])
return -1
This is just an idea, I didn't verify it but it should work. It seems there's no memory limit but just a time limit. Therefore using space to trade time is probably a practical way to do this. The computation complexity is O(n). This algorithm also depends on the condition that the number range is between 1 to len(a).
def first_duplicate(a):
len_a = len(a)
b = [len_a + 1] * len_a
for i, n in enumerate(a):
n0 = n - 1
if b[n0] == len_a + 1:
b[n0] = len_a
elif b[n0] == len_a:
b[n0] = i
min_i = len_a
min_n = -1
for n0, i in enumerate(b):
if i < min_i:
min_i = i
min_n = n0 + 1
return min_n
Update:
This solution is not as fast as the set() solution by #blhsing. However, it may not be the same if it was implemented in C - it's kinda unfair since set() is a built-in function which was implemented in C as other core functions of CPython.

Get the pairs of values from a list according to a condition without elements repeating

I have a list of integers like:
1 3 4 4 9 7 10 (the number of elements is between 1 and 200000)
and an integer variable D, it lies between 0 and 10^9.
Let it be 5 for example.
I need to count how many pairs in the list have a difference between each other not bigger than a variable D but the tricky part is that if I took the zero element with value 1 and the first element with the value 3(the difference between them meets the condition) I can't use these elements of a list again.
For example for the sequence above the answer is 3 pairs: (1,3) (4,4) (7,9)
I wrote a code which seems to be correct but I need a hint how to change the input sequence and the variable d the way it will output wrong answer
list_of_colors = [1, 3, 4, 4, 9, 7, 10]
d = 5
number_of_pairs = 0
list_of_colors.sort() # the values in the list are not always sorted
i = 0
while True:
if i >= len(list_of_colors):
break
if i != len(list_of_colors) - 1:
# if the number i in list and i+1 is the same or difference between them not greater than a variable d...
if (int(list_of_colors[i]) == int(list_of_colors[i + 1])) or abs(int(list_of_colors[i]) - int(list_of_colors[i + 1])) <= d:
#print list_of_colors[i]," ",list_of_colors[i + 1]
number_of_pairs += 1 # increasing the number of the acceptable pairs
i += 2 # jump over two elements, we already counted them
continue
i += 1
print number_of_pairs
I need another algorithm to compare it with the results of my algorithm on the various range of the input sequence and the variable d
Suggest your ideas please
I have a greedy solution for this problem:
Sort the input sequence.
Parse the sorted sequence as follows:
For ith element in the sequence,
if |a[i+1]-a[i]| <= D,
then pair up the elements. Proceed to process i+2th element.
else
proceed to process i+1th element.
My solution here is to first "clean" the list what means I made the number of elements even. Then I've converted the list into a list of tuples (pairs).
My result for this example is 3 pairs in order to your condition.
list_of_colors = [1, 3, 4, 4, 9, 7, 10]
d = 5
number_of_pairs = 0
list_of_colors.sort() # the values in the list are not always sorted
# remove the last element if the number of elements is odd
if len(list_of_colors) % 2 != 0:
list_of_colors = list_of_colors[:-1]
# create a list of tuples
list_of_colors = [tuple(list_of_colors[i:i+2]) for i in range(0, len(list_of_colors), 2)]
for i in list_of_colors:
if (int(i[0]) == int(i[1])) or abs(int(i[0])) - int(i[1]) <= d:
number_of_pairs += 1
print number_of_pairs

Categories

Resources