What does partition do in this function? - python

I came across this question asking what the function 'foo' would accomplish. However, I couldn't understand what the 'partition' in this function is doing? There is no other information provided. I know about partition from a string manipulation point of view but here it is acting on a list.
def foo(nums, k):
pos = partition(nums, 0, len(nums)-1)
if k > pos+1:
return foo(nums[pos+1:], k-pos-1)
elif k < pos+1:
return foo(nums[:pos], k)
else:
return nums[pos]

It depends on the definition of partition. However, foo is most likely trying to find the k-th smallest item in nums.
The partition function I have in mind splits the list in half and returns the midpoint. Depending on k, the foo function decides whether to use the left or right sub-array, and then adjusts the value of k accordingly.
Here's a fleshed out example, including some commented out debugging lines that you can enable. I have used the implementation of partition() from Introduction to Algorithms by Cormen et al.
import random
def partition(A, p, r):
""" This uses 1-based indexing. """
#print(f" partition: {A} {p} {r}")
x = A[r-1]
i = p - 1
for j in range(p, r):
if A[j-1] <= x:
i = i + 1
(A[i-1], A[j-1]) = (A[j-1], A[i-1])
(A[i], A[r-1]) = (A[r-1], A[i])
return i + 1
def partition2(A, p, r):
""" Zero-based indexing version """
return partition(A, p+1, r+1) - 1
def foo(nums, k):
#print(f" foo: {nums} {k}")
pos = partition2(nums, 0, len(nums) - 1)
#print(f" pos: {pos}")
if k > pos + 1:
return foo(nums[pos+1:], k-pos-1)
if k < pos + 1:
return foo(nums[:pos], k)
else:
return nums[pos]
# generate some random samples
random.seed(0)
for sample in range(5):
# random value [1, 5]
k = random.randint(1, 5)
# list of 5 random values in range [0, 20]
l = [ random.randint(0, 20) for i in range(6) ]
# result of foo
res = foo(l, k)
# inform
print(f"For list {l} and k = {k}, we got {res}")
Here are the results of running this program:
For list [1, 8, 12, 16, 15, 13] and k = 4, we got 13
For list [4, 11, 18, 6, 16, 15] and k = 3, we got 11
For list [4, 3, 19, 8, 17, 19] and k = 3, we got 8
For list [9, 3, 2, 10, 15, 17] and k = 2, we got 3
For list [6, 13, 10, 19, 20, 11] and k = 1, we got 6

Related

min sum of consecutive values with Divide and Conquer

Given an array of random integers
N = [1,...,n]
I need to find min sum of two consecutive values using divide and conquer.
What is not working here but my IQ?
def minSum(array):
if len(array) < 2:
return array[0]+array[1]
if (len(a)%2) != 0:
mid = int(len(array)/2)
leftArray = array[:mid]
rightArray = array[mid+1:]
return min(minSum(leftArray),minSum(rightArray),crossSum(array,mid))
else:
mid = int(len(array)/2)
leftArray = array[:mid]
rightArray = array[mid:]
return min(minSum(leftArray), minSum(rightArray), array[mid]+array[mid+1])
def crossSum(array,mid):
return min(array[mid-1]+array[mid],array[mid]+array[mid+1])
The main problem seems to be that the first condition is wrong: If len(array) < 2, then the following line is bound to raise an IndexError. Also, a is not defined. I assume that that's the name of the array in the outer scope, thus this does not raise an exception but just silently uses the wrong array. Apart from that, the function seems to more-or-less work (did not test it thoroughly, though.
However, you do not really need to check whether the array has odd or even length, you can just use the same code for both cases, making the crossSum function unneccesary. Also, it is kind of confusing that the function for returning the min sum is called maxSum. If you really want a divide-and-conquer approach, try this:
def minSum(array):
if len(array) < 2:
return 10**100
elif len(array) == 2:
return array[0]+array[1]
else:
# len >= 3 -> both halves guaranteed non-empty
mid = len(array) // 2
leftArray = array[:mid]
rightArray = array[mid:]
return min(minSum(leftArray),
minSum(rightArray),
leftArray[-1] + rightArray[0])
import random
lst = [random.randint(1, 10) for _ in range(20)]
r = minSum(lst)
print(lst)
print(r)
Random example output:
[1, 5, 6, 4, 1, 2, 2, 10, 7, 10, 8, 4, 9, 5, 7, 6, 5, 1, 4, 9]
3
However, a simple loop would be much better suited for the problem:
def minSum(array):
return min(array[i-1] + array[i] for i in range(1, len(array)))

Given an array of integers, return indices of the two numbers such that they add up to a specific target [duplicate]

I'm new to Python and have just started to try out LeetCode to build my chops. On this classic question my code misses a test case.
The problem is as follows:
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
I miss on test case [3,2,4] with the target number of 6, which should return the indices of [1,2], but hit on test case [1,5,7] with the target number of 6 (which of course returns indices [0,1]), so it appears that something is wrong in my while loop, but I'm not quite sure what.
class Solution:
def twoSum(self, nums, target):
x = 0
y = len(nums) - 1
while x < y:
if nums[x] + nums[y] == target:
return (x, y)
if nums[x] + nums[y] < target:
x += 1
else:
y -= 1
self.x = x
self.y = y
self.array = array
return None
test_case = Solution()
array = [1, 5, 7]
print(test_case.twoSum(array, 6))
Output returns null on test case [3,2,4] with target 6, so indices 1 and 2 aren't even being summarized, could I be assigning y wrong?
A brute force solution is to double nest a loop over the list where the inner loop only looks at index greater than what the outer loop is currently on.
class Solution:
def twoSum(self, nums, target):
for i, a in enumerate(nums, start=0):
for j, b in enumerate(nums[i+1:], start=0):
if a+b==target:
return [i, j+i+1]
test_case = Solution()
array = [3, 2, 4]
print(test_case.twoSum(array, 6))
array = [1, 5, 7]
print(test_case.twoSum(array, 6))
array = [2, 7, 11, 15]
print(test_case.twoSum(array, 9))
Output:
[1, 2]
[0, 1]
[0, 1]
Bit different approach. We will build a dictionary of values as we need them, which is keyed by the values we are looking for.If we look for a value we track the index of that value when it first appears. As soon as you find the values that satisfy the problem you are done. The time on this is also O(N)
class Solution:
def twoSum(self, nums, target):
look_for = {}
for n,x in enumerate(nums):
try:
return look_for[x], n
except KeyError:
look_for.setdefault(target - x,n)
test_case = Solution()
array = [1, 5, 7]
array2 = [3,2,4]
given_nums=[2,7,11,15]
print(test_case.twoSum(array, 6))
print(test_case.twoSum(array2, 6))
print(test_case.twoSum(given_nums,9))
output:
(0, 1)
(1, 2)
(0, 1)
class Solution:
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
ls=[]
l2=[]
for i in nums:
ls.append(target-i)
for i in range(len(ls)):
if ls[i] in nums :
if i!= nums.index(ls[i]):
l2.append([i,nums.index(ls[i])])
return l2[0]
x= Solution()
x.twoSum([-1,-2,-3,-4,-5],-8)
output
[2, 4]
import itertools
class Solution:
def twoSum(self, nums, target):
subsets = []
for L in range(0, len(nums)+1):
for subset in itertools.combinations(nums, L):
if len(subset)!=0:
subsets.append(subset)
print(subsets) #returns all the posible combinations as tuples, note not permutations!
#sums all the tuples
sums = [sum(tup) for tup in subsets]
indexes = []
#Checks sum of all the posible combinations
if target in sums:
i = sums.index(target)
matching_combination = subsets[i] #gets the option
for number in matching_combination:
indexes.append(nums.index(number))
return indexes
else:
return None
test_case = Solution()
array = [1,2,3]
print(test_case.twoSum(array, 4))
I was trying your example for my own learning. I am happy with what I found. I used the itertools to make all the combination of the numbers for me. Then I used list manipulation to sum all the possible combination of numbers in your input array, then I just check in one shot if the target is inside the sum array or not. If not then return None, return the indexes otherwise. Please note that this approach will return all the three indexes as well, if they add up to the target. Sorry it took so long :)
This one is more comprehensive cohesive efficient one even so shorter lines of code.
nums = [6, 7, 11, 15, 3, 6, 5, 3,99,5,4,7,2]
target = 27
n = 0
for i in range(len(nums)):
n+=1
if n == len(nums):
n == len(nums)
else:
if nums[i]+nums[n] == target:
# to find the target position
print([nums.index(nums[i]),nums.index(nums[n])])
# to get the actual numbers to add print([nums[i],nums[n]])

Python Two Sum - Brute Force Approach

I'm new to Python and have just started to try out LeetCode to build my chops. On this classic question my code misses a test case.
The problem is as follows:
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
I miss on test case [3,2,4] with the target number of 6, which should return the indices of [1,2], but hit on test case [1,5,7] with the target number of 6 (which of course returns indices [0,1]), so it appears that something is wrong in my while loop, but I'm not quite sure what.
class Solution:
def twoSum(self, nums, target):
x = 0
y = len(nums) - 1
while x < y:
if nums[x] + nums[y] == target:
return (x, y)
if nums[x] + nums[y] < target:
x += 1
else:
y -= 1
self.x = x
self.y = y
self.array = array
return None
test_case = Solution()
array = [1, 5, 7]
print(test_case.twoSum(array, 6))
Output returns null on test case [3,2,4] with target 6, so indices 1 and 2 aren't even being summarized, could I be assigning y wrong?
A brute force solution is to double nest a loop over the list where the inner loop only looks at index greater than what the outer loop is currently on.
class Solution:
def twoSum(self, nums, target):
for i, a in enumerate(nums, start=0):
for j, b in enumerate(nums[i+1:], start=0):
if a+b==target:
return [i, j+i+1]
test_case = Solution()
array = [3, 2, 4]
print(test_case.twoSum(array, 6))
array = [1, 5, 7]
print(test_case.twoSum(array, 6))
array = [2, 7, 11, 15]
print(test_case.twoSum(array, 9))
Output:
[1, 2]
[0, 1]
[0, 1]
Bit different approach. We will build a dictionary of values as we need them, which is keyed by the values we are looking for.If we look for a value we track the index of that value when it first appears. As soon as you find the values that satisfy the problem you are done. The time on this is also O(N)
class Solution:
def twoSum(self, nums, target):
look_for = {}
for n,x in enumerate(nums):
try:
return look_for[x], n
except KeyError:
look_for.setdefault(target - x,n)
test_case = Solution()
array = [1, 5, 7]
array2 = [3,2,4]
given_nums=[2,7,11,15]
print(test_case.twoSum(array, 6))
print(test_case.twoSum(array2, 6))
print(test_case.twoSum(given_nums,9))
output:
(0, 1)
(1, 2)
(0, 1)
class Solution:
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
ls=[]
l2=[]
for i in nums:
ls.append(target-i)
for i in range(len(ls)):
if ls[i] in nums :
if i!= nums.index(ls[i]):
l2.append([i,nums.index(ls[i])])
return l2[0]
x= Solution()
x.twoSum([-1,-2,-3,-4,-5],-8)
output
[2, 4]
import itertools
class Solution:
def twoSum(self, nums, target):
subsets = []
for L in range(0, len(nums)+1):
for subset in itertools.combinations(nums, L):
if len(subset)!=0:
subsets.append(subset)
print(subsets) #returns all the posible combinations as tuples, note not permutations!
#sums all the tuples
sums = [sum(tup) for tup in subsets]
indexes = []
#Checks sum of all the posible combinations
if target in sums:
i = sums.index(target)
matching_combination = subsets[i] #gets the option
for number in matching_combination:
indexes.append(nums.index(number))
return indexes
else:
return None
test_case = Solution()
array = [1,2,3]
print(test_case.twoSum(array, 4))
I was trying your example for my own learning. I am happy with what I found. I used the itertools to make all the combination of the numbers for me. Then I used list manipulation to sum all the possible combination of numbers in your input array, then I just check in one shot if the target is inside the sum array or not. If not then return None, return the indexes otherwise. Please note that this approach will return all the three indexes as well, if they add up to the target. Sorry it took so long :)
This one is more comprehensive cohesive efficient one even so shorter lines of code.
nums = [6, 7, 11, 15, 3, 6, 5, 3,99,5,4,7,2]
target = 27
n = 0
for i in range(len(nums)):
n+=1
if n == len(nums):
n == len(nums)
else:
if nums[i]+nums[n] == target:
# to find the target position
print([nums.index(nums[i]),nums.index(nums[n])])
# to get the actual numbers to add print([nums[i],nums[n]])

Choose at random from combinations

I can make a list of all combinations using list(itertools.combinations(range(n), m)) but this will typically be very large.
Given n and m, how can I choose a combination uniformly at random without first constructing a massive list??
In the itertools module there is a recipe for returning a random combination from an iterable. Below are two versions of the code, one for Python 2.x and one for Python 3.x - in both cases you are using a generator which means that you are not creating a large iterable in memory.
Assumes Python 2.x
def random_combination(iterable, r):
"Random selection from itertools.combinations(iterable, r)"
pool = tuple(iterable)
n = len(pool)
indices = sorted(random.sample(xrange(n), r))
return tuple(pool[i] for i in indices)
In your case then it would be simple to do:
>>> import random
>>> def random_combination(iterable, r):
"Random selection from itertools.combinations(iterable, r)"
pool = tuple(iterable)
n = len(pool)
indices = sorted(random.sample(xrange(n), r))
return tuple(pool[i] for i in indices)
>>> n = 10
>>> m = 3
>>> print(random_combination(range(n), m))
(3, 5, 9) # Returns a random tuple with length 3 from the iterable range(10)
In the case of Python 3.x
In the case of Python 3.x you replace the xrange call with range but the use-case is still the same.
def random_combination(iterable, r):
"Random selection from itertools.combinations(iterable, r)"
pool = tuple(iterable)
n = len(pool)
indices = sorted(random.sample(range(n), r))
return tuple(pool[i] for i in indices)
From http://docs.python.org/2/library/itertools.html#recipes
def random_combination(iterable, r):
"Random selection from itertools.combinations(iterable, r)"
pool = tuple(iterable)
n = len(pool)
indices = sorted(random.sample(xrange(n), r))
return tuple(pool[i] for i in indices)
A generator would be more memory efficient for iteration:
def random_combination(iterable,r):
i = 0
pool = tuple(iterable)
n = len(pool)
rng = range(n)
while i < r:
i += 1
yield [pool[j] for j in random.sample(rng, r)]
I've modified Jthorpe's generator in order to work when the number of combination you need greater than the length of iterator you pass:
def random_combination(iterable,r):
i = 0
pool = tuple(iterable)
n = len(pool)
rng = range(n)
while i < n**2/2:
i += 1
yield [pool[j] for j in random.sample(rng, r)]
And so, this code maybe help you:
I created a empty list = "lista" to append results of np.random.permutation
"i" = iterator to control loop while
"r" = number of times to loop (in my case 1milion)
Into the while I did permutation of "20" numbers and get(output) a numpy array like: [20,3,5,8,19,7,5,...n20]
Observation:: there isn't permutation repeated...
Take this output is very
lista = []
i = 0
r = 1000000
while i < r:
lista.append(np.random.permutation(20))
i += 1
print(lista)
# thanks to my friend WIniston for asking about it
Results:::
[array([11, 15, 12, 18, 5, 0, 9, 8, 14, 13, 19, 10, 7, 16, 3, 1, 17,4, 6, 2]),
array([ 5, 15, 12, 4, 17, 16, 14, 7, 19, 1, 2, 10, 3, 0, 18, 6, 9, 11, 13, 8]),
array([16, 5, 12, 19, 18, 17, 7, 1, 10, 4, 11, 3, 0, 14, 15, 9, 6, 2, 13, 8]), ...
The problem with the existing answers is they sample with replacement or wastefully discard duplicate samples. This is fine if you only want to draw one sample but not for drawing many samples (like I wanted to do!).
You can uniformly sample from the space of all combinations very efficiently by making use of Python's inbuilt methods.
from random import sample
def sample_from_all_combinations(all_items, num_samples = 10):
"""Randomly sample from the combination space"""
num_items = len(all_items)
num_combinations = 2 ** num_items
num_samples = min(num_combinations, num_samples)
samples = sample(range(1, num_combinations), k=num_samples)
for combination_num in samples:
items_subset = [
all_items[item_idx]
for item_idx, item_sampled in enumerate(
format(combination_num, f"0{num_items}b")
)
if item_sampled == "1"
]
yield items_subset
NOTE the above will fail with an OverflowError when len(all_items) >= 64 because the Python ints >= 2 ** 64 are too large to convert to C ssize_t. You can solve this by modifying the sample method from random to the following: (Don't forget to update the import to from RandomLarge import sample)
class RandomLarge(random.Random):
"""
Clone of random inbuilt methods but modified to work with large numbers
>= 2^64
"""
def sample(self, population, k):
"""
Chooses k unique random elements from a population sequence or set.
Modified random sample method to work with large ranges (>= 2^64)
"""
if isinstance(population, random._Set):
population = tuple(population)
if not isinstance(population, random._Sequence):
raise TypeError(
"Population must be a sequence or set. For dicts, use list(d)."
)
randbelow = self._randbelow
# NOTE this is the only line modified from the original function
# n = len(population)
n = population.stop - population.start
if not 0 <= k <= n:
raise ValueError("Sample larger than population or is negative")
result = [None] * k
setsize = 21 # size of a small set minus size of an empty list
if k > 5:
setsize += 4 ** random._ceil(
random._log(k * 3, 4)
) # table size for big sets
if n <= setsize:
# An n-length list is smaller than a k-length set
pool = list(population)
for i in range(k): # invariant: non-selected at [0,n-i)
j = randbelow(n - i)
result[i] = pool[j]
pool[j] = pool[n - i - 1] # move non-selected item into vacancy
else:
selected = set()
selected_add = selected.add
for i in range(k):
j = randbelow(n)
while j in selected:
j = randbelow(n)
selected_add(j)
result[i] = population[j]
return result
To choose a random subset of multiple combinations
You can keep picking random samples and discarding the ones that were already picked.
def random_subset_of_combos2(iterable, r, k):
"""Returns at most `n` random samples of
`r` length (combinations of) subsequences of elements in `iterable`.
"""
def random_combination(iterable, r):
"Random selection from itertools.combinations(iterable, r)"
pool = tuple(iterable)
n = len(pool)
indices = sorted(random.sample(range(n), r))
return tuple(pool[i] for i in indices)
results = set()
while True:
new_combo = random_combination(iterable, r)
if new_combo not in results:
results.add(new_combo)
if len(results) >= min(k, len(iterable)):
break
return results
This seems faster in most cases.
Alternatively, you can assign a index number to each combination, and create a list indexing the targeted number of random samples.
def random_subset_of_combos(iterable, r, k):
"""Returns at most `n` random samples of
`r` length (combinations of) subsequences of elements in `iterable`.
"""
max_combinations = math.comb(len(iterable), min(r, len(iterable)))
k = min(k, max_combinations) # Limit sample size to ...
indexes = set(random.sample(range(max_combinations), k))
max_idx = max(indexes)
results = []
for i, combo in zip(it.count(), it.combinations(iterable, r)):
if i in indexes:
results.append(combo)
elif i > max_idx:
break
return results
Technically, this answer may not be what the OP asked for, but search engines (and at least three SO members) have considered this to be the same question.

Efficient way to find missing elements in an integer sequence

Suppose we have two items missing in a sequence of consecutive integers and the missing elements lie between the first and last elements. I did write a code that does accomplish the task. However, I wanted to make it efficient using less loops if possible. Any help will be appreciated. Also what about the condition when we have to find more missing items (say close to n/4) instead of 2. I think then my code should be efficient right because I am breaking out from the loop earlier?
def missing_elements(L,start,end,missing_num):
complete_list = range(start,end+1)
count = 0
input_index = 0
for item in complete_list:
if item != L[input_index]:
print item
count += 1
else :
input_index += 1
if count > missing_num:
break
def main():
L = [10,11,13,14,15,16,17,18,20]
start = 10
end = 20
missing_elements(L,start,end,2)
if __name__ == "__main__":
main()
If the input sequence is sorted, you could use sets here. Take the start and end values from the input list:
def missing_elements(L):
start, end = L[0], L[-1]
return sorted(set(range(start, end + 1)).difference(L))
This assumes Python 3; for Python 2, use xrange() to avoid building a list first.
The sorted() call is optional; without it a set() is returned of the missing values, with it you get a sorted list.
Demo:
>>> L = [10,11,13,14,15,16,17,18,20]
>>> missing_elements(L)
[12, 19]
Another approach is by detecting gaps between subsequent numbers; using an older itertools library sliding window recipe:
from itertools import islice, chain
def window(seq, n=2):
"Returns a sliding window (of width n) over data from the iterable"
" s -> (s0,s1,...s[n-1]), (s1,s2,...,sn), ... "
it = iter(seq)
result = tuple(islice(it, n))
if len(result) == n:
yield result
for elem in it:
result = result[1:] + (elem,)
yield result
def missing_elements(L):
missing = chain.from_iterable(range(x + 1, y) for x, y in window(L) if (y - x) > 1)
return list(missing)
This is a pure O(n) operation, and if you know the number of missing items, you can make sure it only produces those and then stops:
def missing_elements(L, count):
missing = chain.from_iterable(range(x + 1, y) for x, y in window(L) if (y - x) > 1)
return list(islice(missing, 0, count))
This will handle larger gaps too; if you are missing 2 items at 11 and 12, it'll still work:
>>> missing_elements([10, 13, 14, 15], 2)
[11, 12]
and the above sample only had to iterate over [10, 13] to figure this out.
Assuming that L is a list of integers with no duplicates, you can infer that the part of the list between start and index is completely consecutive if and only if L[index] == L[start] + (index - start) and similarly with index and end is completely consecutive if and only if L[index] == L[end] - (end - index). This combined with splitting the list into two recursively gives a sublinear solution.
# python 3.3 and up, in older versions, replace "yield from" with yield loop
def missing_elements(L, start, end):
if end - start <= 1:
if L[end] - L[start] > 1:
yield from range(L[start] + 1, L[end])
return
index = start + (end - start) // 2
# is the lower half consecutive?
consecutive_low = L[index] == L[start] + (index - start)
if not consecutive_low:
yield from missing_elements(L, start, index)
# is the upper part consecutive?
consecutive_high = L[index] == L[end] - (end - index)
if not consecutive_high:
yield from missing_elements(L, index, end)
def main():
L = [10,11,13,14,15,16,17,18,20]
print(list(missing_elements(L,0,len(L)-1)))
L = range(10, 21)
print(list(missing_elements(L,0,len(L)-1)))
main()
missingItems = [x for x in complete_list if not x in L]
a=[1,2,3,7,5,11,20]
b=[]
def miss(a,b):
for x in range (a[0],a[-1]):
if x not in a:
b.append(x)
return b
print (miss(a,b))
ANS:[4, 6, 8, 9, 10, 12, 13, 14, 15, 16, 17, 18, 19]
works for sorted,unsorted , with duplicates too
Using collections.Counter:
from collections import Counter
dic = Counter([10, 11, 13, 14, 15, 16, 17, 18, 20])
print([i for i in range(10, 20) if dic[i] == 0])
Output:
[12, 19]
arr = [1, 2, 5, 6, 10, 12]
diff = []
"""zip will return array of tuples (1, 2) (2, 5) (5, 6) (6, 10) (10, 12) """
for a, b in zip(arr , arr[1:]):
if a + 1 != b:
diff.extend(range(a+1, b))
print(diff)
[3, 4, 7, 8, 9, 11]
If the list is sorted we can lookup for any gap. Then generate a range object between current (+1) and next value (not inclusive) and extend it to the list of differences.
Using scipy lib:
import math
from scipy.optimize import fsolve
def mullist(a):
mul = 1
for i in a:
mul = mul*i
return mul
a = [1,2,3,4,5,6,9,10]
s = sum(a)
so = sum(range(1,11))
mulo = mullist(range(1,11))
mul = mullist(a)
over = mulo/mul
delta = so -s
# y = so - s -x
# xy = mulo/mul
def func(x):
return (so -s -x)*x-over
print int(round(fsolve(func, 0))), int(round(delta - fsolve(func, 0)))
Timing it:
$ python -mtimeit -s "$(cat with_scipy.py)"
7 8
100000000 loops, best of 3: 0.0181 usec per loop
Other option is:
>>> from sets import Set
>>> a = Set(range(1,11))
>>> b = Set([1,2,3,4,5,6,9,10])
>>> a-b
Set([8, 7])
And the timing is:
Set([8, 7])
100000000 loops, best of 3: 0.0178 usec per loop
My take was to use no loops and set operations:
def find_missing(in_list):
complete_set = set(range(in_list[0], in_list[-1] + 1))
return complete_set - set(in_list)
def main():
sample = [10, 11, 13, 14, 15, 16, 17, 18, 20]
print find_missing(sample)
if __name__ == "__main__":
main()
# => set([19, 12])
Simply walk the list and look for non-consecutive numbers:
prev = L[0]
for this in L[1:]:
if this > prev+1:
for item in range(prev+1, this): # this handles gaps of 1 or more
print item
prev = this
Here's a one-liner:
In [10]: l = [10,11,13,14,15,16,17,18,20]
In [11]: [i for i, (n1, n2) in enumerate(zip(l[:-1], l[1:])) if n1 + 1 != n2]
Out[11]: [1, 7]
I use the list, slicing to offset the copies by one, and use enumerate to get the indices of the missing item.
For long lists, this isn't great because it's not O(log(n)), but I think it should be pretty efficient versus using a set for small inputs. izip from itertools would probably make it quicker still.
>>> l = [10,11,13,14,15,16,17,18,20]
>>> [l[i]+1 for i, j in enumerate(l) if (l+[0])[i+1] - l[i] > 1]
[12, 19]
We found a missing value if the difference between two consecutive numbers is greater than 1:
>>> L = [10,11,13,14,15,16,17,18,20]
>>> [x + 1 for x, y in zip(L[:-1], L[1:]) if y - x > 1]
[12, 19]
Note: Python 3. In Python 2 use itertools.izip.
Improved version for more than one value missing in a row:
>>> import itertools as it
>>> L = [10,11,14,15,16,17,18,20] # 12, 13 and 19 missing
>>> [x + diff for x, y in zip(it.islice(L, None, len(L) - 1),
it.islice(L, 1, None))
for diff in range(1, y - x) if diff]
[12, 13, 19]
def missing_elements(inlist):
if len(inlist) <= 1:
return []
else:
if inlist[1]-inlist[0] > 1:
return [inlist[0]+1] + missing_elements([inlist[0]+1] + inlist[1:])
else:
return missing_elements(inlist[1:])
First we should sort the list and then we check for each element, except the last one, if the next value is in the list. Be carefull not to have duplicates in the list!
l.sort()
[l[i]+1 for i in range(len(l)-1) if l[i]+1 not in l]
I stumbled on this looking for a different kind of efficiency -- given a list of unique serial numbers, possibly very sparse, yield the next available serial number, without creating the entire set in memory. (Think of an inventory where items come and go frequently, but some are long-lived.)
def get_serial(string_ids, longtail=False):
int_list = map(int, string_ids)
int_list.sort()
n = len(int_list)
for i in range(0, n-1):
nextserial = int_list[i]+1
while nextserial < int_list[i+1]:
yield nextserial
nextserial+=1
while longtail:
nextserial+=1
yield nextserial
[...]
def main():
[...]
serialgenerator = get_serial(list1, longtail=True)
while somecondition:
newserial = next(serialgenerator)
(Input is a list of string representations of integers, yield is an integer, so not completely generic code. longtail provides extrapolation if we run out of range.)
There's also an answer to a similar question which suggests using a bitarray for efficiently handling a large sequence of integers.
Some versions of my code used functions from itertools but I ended up abandoning that approach.
A bit of mathematics and we get a simple solution. The below solution works for integers from m to n.
Works for both sorted and unsorted postive and negative numbers.
#numbers = [-1,-2,0,1,2,3,5]
numbers = [-2,0,1,2,5,-1,3]
sum_of_nums = 0
max = numbers[0]
min = numbers[0]
for i in numbers:
if i > max:
max = i
if i < min:
min = i
sum_of_nums += i
# Total : sum of numbers from m to n
total = ((max - min + 1) * (max + min)) / 2
# Subtract total with sum of numbers which will give the missing value
print total - sum_of_nums
With this code you can find any missing values in a sequence, except the last number. It in only required to input your data into excel file with column name "numbers".
import pandas as pd
import numpy as np
data = pd.read_excel("numbers.xlsx")
data_sort=data.sort_values('numbers',ascending=True)
index=list(range(len(data_sort)))
data_sort['index']=index
data_sort['index']=data_sort['index']+1
missing=[]
for i in range (len(data_sort)-1):
if data_sort['numbers'].iloc[i+1]-data_sort['numbers'].iloc[i]>1:
gap=data_sort['numbers'].iloc[i+1]-data_sort['numbers'].iloc[i]
numerator=1
for j in range (1,gap):
mis_value=data_sort['numbers'].iloc[i+1]-numerator
missing.append(mis_value)
numerator=numerator+1
print(np.sort(missing))

Categories

Resources