簡體   English   中英

在 integer 序列中查找缺失元素的有效方法

[英]Efficient way to find missing elements in an integer sequence

假設我們在連續整數序列中缺少兩個項目,並且缺少的元素位於第一個和最后一個元素之間。 我確實編寫了完成任務的代碼。 但是,如果可能的話,我想使用更少的循環來提高效率。 任何幫助將不勝感激。 另外,當我們必須找到更多丟失的項目(比如接近 n/4)而不是 2 時,情況又如何呢?我認為我的代碼應該是高效的,因為我更早地從循環中跳出?

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()

如果輸入序列已排序,則可以在此處使用集合。 從輸入列表中獲取開始和結束值:

def missing_elements(L):
    start, end = L[0], L[-1]
    return sorted(set(range(start, end + 1)).difference(L))

這假設 Python 3; 對於 Python 2,使用xrange()避免先構建列表。

sorted()調用是可選的; 如果沒有它, set()返回缺失值的set() ,有了它,您將得到一個排序列表。

演示:

>>> L = [10,11,13,14,15,16,17,18,20]
>>> missing_elements(L)
[12, 19]

另一種方法是檢測后續數字之間的差距; 使用舊的itertools庫滑動窗口配方

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)

這是一個純 O(n) 操作,如果您知道丟失項目的數量,您可以確保它只生成那些然后停止:

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))

這也將處理更大的差距; 如果您在 11 和 12 處丟失了 2 個項目,它仍然可以工作:

>>> missing_elements([10, 13, 14, 15], 2)
[11, 12]

而上面的示例只需要迭代[10, 13]來解決這個問題。

假設 L 是一個沒有重復的整數列表,你可以推斷出 start 和 index 之間的列表部分是完全連續的當且僅當L[index] == L[start] + (index - start)並且類似with index 和 end 是完全連續的當且僅當L[index] == L[end] - (end - index) 這與將列表遞歸地拆分為兩個相結合,給出了一個次線性解決方案。

# 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]

使用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])

輸出:

[12, 19]

使用scipy庫:

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)))

計時:

$ python -mtimeit -s "$(cat with_scipy.py)" 

7 8

100000000 loops, best of 3: 0.0181 usec per loop

其他選項是:

>>> from sets import Set
>>> a = Set(range(1,11))
>>> b = Set([1,2,3,4,5,6,9,10])
>>> a-b
Set([8, 7])

時間是:

Set([8, 7])
100000000 loops, best of 3: 0.0178 usec per loop
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]

如果列表已排序,我們可以查找任何差距。 然后在當前(+1)和下一個值(不包括)之間生成一個范圍對象,並將其擴展到差異列表。


 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))

答案: [4, 6, 8, 9, 10, 12, 13, 14, 15, 16, 17, 18, 19]

適用於已sortedunsorted ,也適用於duplicates

這是一個單行:

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]

我使用列表,切片以將副本偏移一個,並使用 enumerate 獲取丟失項目的索引。

對於長列表,這不是很好,因為它不是 O(log(n)),但我認為與使用用於小輸入的set相比,它應該非常有效。 itertools 中的izip可能會使其更快。

我的看法是不使用循環和設置操作:

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])

只需遍歷列表並查找不連續的數字:

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

如果兩個連續數字之間的差大於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]

注意:Python 3。在 Python 2 中使用itertools.izip

連續丟失多個值的改進版本:

>>> 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]
>>> 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]
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:])

首先我們應該對列表進行排序,然后我們檢查每個元素,除了最后一個,如果下一個值在列表中。 注意列表中不要有重復項!

l.sort()

[l[i]+1 for i in range(len(l)-1) if l[i]+1 not in l]

我偶然發現了一種不同的效率 - 給定一個唯一序列號列表,可能非常稀疏,產生下一個可用序列號,而無需在內存中創建整個集合。 (想想一個物品經常來來去去的庫存,但有些是長期存在的。)

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)

(輸入是一個整數的字符串表示列表,yield 是一個整數,所以不是完全通用的代碼。如果我們超出范圍,longtail 提供外推。)

還有一個類似問題的答案,它建議使用位數組來有效地處理大量整數。

我的代碼的某些版本使用了 itertools 中的函數,但我最終放棄了這種方法。

一點數學,我們得到一個簡單的解決方案。 以下解決方案適用於從 m 到 n 的整數。 適用於已排序和未排序的正數和負數。

#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

使用此代碼,您可以找到序列中除最后一個數字之外的任何缺失值。 它只需要將您的數據輸入到列名為“數字”的 Excel 文件中。

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))

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM