2012-04-13 8 views
7

मान लें मेरे पास आई पी की एक सूची पर्वतमाला (पिछले अवधि केवल) या ओवरलैप है कि हो सकता है नहीं:अजगर में श्रेणियों में आईपी एकीकरण

('1.1.1.1-7', '2.2.2.2-10', '3.3.3.3-3.3.3.3', '1.1.1.4-25', '2.2.2.4-6') 

मैं किसी भी अतिव्यापी पर्वतमाला की पहचान करने और मजबूत करने के लिए एक तरह से तलाश कर रहा हूँ उन्हें एकल श्रेणियों में।

('1.1.1.1-25', '2.2.2.2-10', '3.3.3.3-3') 

एल्गोरिथ्म के लिए वर्तमान सोचा सभी IP की एक सूची में सभी सीमाओं का विस्तार करने, डुप्लिकेट को निकाल देते हैं, उसके आधार है, और किसी भी लगातार आईपी मजबूत है।

कोई और अजगर-esque एल्गोरिथ्म सुझाव?

+0

छंटाई और फिर मेरे लिए एक बहुत अच्छा समाधान की तरह मजबूत बनाने लगता है जाने के लिए शायद बेहतर तरीका है। ओ (NLG (एन))। सुनिश्चित नहीं है कि यह बेहतर किया जा सकता है। –

+0

@ColinD मुझे यकीन है कि वह श्रेणियों से लेकर – agf

उत्तर

5

यहाँ मेरी संस्करण है, एक मॉड्यूल के रूप। मेरा एल्गोरिदम एक उत्तर में एक लुनिक्सबोच का उल्लेख करता है, और रेंज स्ट्रिंग से पूर्णांक और बैक में रूपांतरण अच्छी तरह से मॉड्यूलरकृत है।

import socket, struct 

def ip2long(ip): 
    packed = socket.inet_aton(ip) 
    return struct.unpack("!L", packed)[0] 

def long2ip(n): 
    unpacked = struct.pack('!L', n) 
    return socket.inet_ntoa(unpacked) 

def expandrange(rng): 
    # expand '1.1.1.1-7' to ['1.1.1.1', '1.1.1.7'] 
    start, end = [ip.split('.') for ip in rng.split('-')] 
    return map('.'.join, (start, start[:len(start) - len(end)] + end)) 

def compressrange((start, end)): 
    # compress ['1.1.1.1', '1.1.1.7'] to '1.1.1.1-7' 
    start, end = start.split('.'), end.split('.') 
    return '-'.join(map('.'.join, 
      (start, end[next((i for i in range(4) if start[i] != end[i]), 3):]))) 

def strings_to_ints(ranges): 
    # turn range strings into list of lists of ints 
    return [map(ip2long, rng) for rng in map(expandrange, ranges)] 

def ints_to_strings(ranges): 
    # turn lists of lists of ints into range strings 
    return [compressrange(map(long2ip, rng)) for rng in ranges] 

def consolodate(ranges): 
    # join overlapping ranges in a sorted iterable 
    iranges = iter(ranges) 
    startmin, startmax = next(iranges) 
    for endmin, endmax in iranges: 
     # leave out the '+ 1' if you want to join overlapping ranges 
     # but not consecutive ranges. 
     if endmin <= (startmax + 1): 
      startmax = max(startmax, endmax) 
     else: 
      yield startmin, startmax 
      startmin, startmax = endmin, endmax 
    yield startmin, startmax 

def convert_consolodate(ranges): 
    # convert a list of possibly overlapping ip range strings 
    # to a sorted, consolodated list of non-overlapping ip range strings 
    return list(ints_to_strings(consolodate(sorted(strings_to_ints(ranges))))) 

if __name__ == '__main__': 
    ranges = ('1.1.1.1-7', 
       '2.2.2.2-10', 
       '3.3.3.3-3.3.3.3', 
       '1.1.1.4-25', 
       '2.2.2.4-6') 
    print convert_consolodate(ranges) 
    # prints ['1.1.1.1-25', '2.2.2.2-10', '3.3.3.3-3'] 
+0

मुझे लगता है कि 'श्रेणियां = (' 1.1.1।1-7 ',' 1.1.1.8-10 ')' को '[1.1.1.1-10'] 'में विलय किया जाना चाहिए। अब आउटपुट '['1.1.1.1-7', '1.1.1.8-10']' – ovgolovin

+0

@ovgolovin मैंने उसे सचमुच लिया। वे "लगातार" श्रेणियां हैं, न कि "ओवरलैपिंग" श्रेणियां। – agf

+0

@ovgolovin वास्तव में, आप सही हैं, वह लगभग निश्चित रूप से लगातार श्रेणियों में शामिल होना चाहता है। मैंने अपने कोड को सूचियों के बजाय पूर्णांक पर काम करने के लिए बदल दिया है ताकि यह आसानी से लगातार श्रेणियों को संभाल सके। – agf

1

एक बार मैं एक ही समस्या का सामना करना पड़ा। केवल अंतर यह था कि मुझे सूची में लाइन सेगमेंट को कुशलता से रखना था। यह एक मोंटे कार्लो सिमुलेशन के लिए था। और नए यादृच्छिक रूप से जेनरेट किए गए लाइन सेगमेंट को मौजूदा सॉर्ट और विलय लाइन सेगमेंट में जोड़ा जाना था।

मैंने lunixbochs द्वारा आईपी को पूर्णांक में बदलने के लिए उत्तर का उपयोग करके आपकी समस्या के लिए एल्गोरिदम अनुकूलित किया।

यह समाधान पहले से विलय वाली श्रेणियों की मौजूदा सूची में एक नई आईपी रेंज जोड़ने की अनुमति देता है (जबकि अन्य समाधान सूची-से-रेंज-टू-मर्ज सॉर्ट किए जाने पर भरोसा करते हैं और पहले से ही विलय करने के लिए एक नई श्रेणी जोड़ने की अनुमति नहीं देते हैं रेंज सूची)। यह add_range समारोह में bisect मॉड्यूल का उपयोग कर जगह है जहाँ नया आईपी रेंज सम्मिलित करने के लिए खोजने के लिए और फिर अनावश्यक आईपी अंतराल को हटाने और समायोजित सीमाओं के साथ नई रेंज डालने ताकि नई रेंज हटाए गए सभी सीमाओं को गले लगाती है के द्वारा किया जाता है।

import socket 
import struct 
import bisect 


def ip2long(ip): 
    '''IP to integer''' 
    packed = socket.inet_aton(ip) 
    return struct.unpack("!L", packed)[0] 


def long2ip(n): 
    '''integer to IP''' 
    unpacked = struct.pack('!L', n) 
    return socket.inet_ntoa(unpacked) 


def get_ips(s): 
    '''Convert string IP interval to tuple with integer representations of boundary IPs 
'1.1.1.1-7' -> (a,b)''' 
    s1,s2 = s.split('-') 
    if s2.isdigit(): 
     s2 = s1[:-1] + s2 
    return (ip2long(s1),ip2long(s2)) 


def add_range(iv,R): 
    '''add new Range to already merged ranges inplace''' 
    left,right = get_ips(R) 
    #left,right are left and right boundaries of the Range respectively 

    #If this is the very first Range just add it to the list 
    if not iv: 
     iv.append((left,right)) 
     return 

    #Searching the first interval with left_boundary < left range side 
    p = bisect.bisect_right(iv, (left,right)) #place after the needed interval 
    p -= 1 #calculating the number of interval basing on the position where the insertion is needed 


    #Interval: |----X----| (delete)  
    #Range: <--<--|----------| (extend) 
    #Detect if the left Range side is inside the found interval 
    if p >=0: #if p==-1 then there was no interval found 
     if iv[p][1]>= right: 
      #Detect if the Range is completely inside the interval 
      return #drop the Range; I think it will be a very common case 

     if iv[p][1] >= left-1: 
      left = iv[p][0] #extending the left Range interval 
      del iv[p] #deleting the interval from the interval list 
      p -= 1 #correcting index to keep the invariant 


    #Intervals: |----X----| |---X---| (delete)  
    #Range: |-----------------------------|   
    #Deleting all the intervals which are inside the Range interval 
    while True: 
     p += 1 
     if p >= len(iv) or iv[p][0] >= right or iv[p][1] > right: 
      'Stopping searching for the intervals which is inside the Range interval' 
      #there are no more intervals or 
      #the interval is to the right of the right Range side 
      # it's the next case (right Range side is inside the interval) 
      break 
     del iv[p] #delete the now redundant interval from the interval list 
     p -= 1 #correcting index to keep the invariant 


    #Interval: |--------X--------| (delete)  
    #Range: |-----------|-->--> (extend)  
    #Working the case when the right Range side is inside the interval 
    if p < len(iv) and iv[p][0] <= right-1: 
     #there is no condition for right interval side since 
     #this case would have already been worked in the previous block 
     right = iv[p][1] #extending the right Range side 
     del iv[p] #delete the now redundant interval from the interval list 
     #No p -= 1, so that p is no pointing to the beginning of the next interval 
     #which is the position of insertion 


    #Inserting the new interval to the list 
    iv.insert(p, (left,right)) 


def merge_ranges(ranges): 
    '''Merge the ranges''' 
    iv = [] 
    for R in ranges: 
     add_range(iv,R) 
    return ['-'.join((long2ip(left),long2ip(right))) for left,right in iv] 

ranges = ('1.1.1.1-7', '2.2.2.2-10', '3.3.3.3-3.3.3.3', '1.1.1.4-25', '2.2.2.4-6') 
print(merge_ranges(ranges)) 

आउटपुट:

['1.1.1.1-1.1.1.25', '2.2.2.2-2.2.2.10', '3.3.3.3-3.3.3.3'] 

यह मैं कोड करने के लिए बहुत मज़ा था! कि के लिए धन्यवाद :)

+0

तक जाने से बचना चाहते हैं। मुझे आश्चर्य है कि इस एल्गोरिदम को फिर से लिखना संभव है कि यह आसानी से सूची में मौजूदा श्रेणियों को विस्तारित करता है (उन्हें 'सूची' नहीं होना चाहिए tuple's) मौजूदा श्रेणियों को हटाने और 'iv.insert (पी, (बाएं, दाएं)) के साथ अंत में नया सब-गले लगाने वाला जोड़ने के बजाय। परंपरागत 'सूची' में हटाने और सम्मिलन पर ओ (एन) से बचने के लिए मैंने अभी ['blist'] (http://stutzbachenterprises.com/blist/blist.html#blist) का उपयोग किया था। लेकिन मुझे आश्चर्य है कि कोई व्यक्ति एक अच्छा एल्गोरिदमिक समाधान के साथ आ सकता है जो * अनावश्यक * हटाने और सम्मिलन से बच जाएगा। – ovgolovin

+0

मुझे नहीं लगता कि आप प्री-सॉर्ट किए गए डेटा के बिना आवेषण/हटाना से बच सकते हैं, जो आपकी विधि की आवश्यकता को अस्वीकार कर देगा। ऐसा लगता है कि आपका एल्गोरिदम ओ (nlogn) है, मानते हैं कि आपके सम्मिलन और हटाना ओ (लॉगन) से भी बदतर नहीं है जो कि एल्गोरिदम लुनिक्सबोच के समान है और मैं इसका उपयोग करता हूं - सिवाय इसके कि आपका प्रत्येक मैन्युअल रूप से ओ (लॉग) चरण करता है, जबकि हमारा एक प्रकार पर निर्भर करता है और फिर रैखिक समय में श्रेणियों को विलीन करता है। शायद, हमारा संस्करण सीपीथॉन में तेज़ होगा क्योंकि क्रम सी – agf

+0

@agf में किया जाता है। यह एल्गोरिदम केवल मेरे द्वारा बनाए गए पुराने एल्गोरिदम से एक अनुकूलन है जो रेखा खंडों को यादृच्छिक रूप से जेनरेट किया जाता है। प्रत्येक चरण में मैंने कुछ नए लाइन खंड उत्पन्न किए और उन्हें मौजूदा लोगों में जोड़ना पड़ा, इसलिए मुझे एक प्रभावी इन-लाइन विलय एल्गोरिदम की आवश्यकता थी। – ovgolovin

0

यूनिफाई अपने ips के प्रारूप, ints की एक जोड़ी में रेंज कर देते हैं।

अब कार्य बहुत सरल है - "मजबूत" पूर्णांक रेंज। मेरा मानना ​​है कि मौजूदा कुशल एल्गोरिथ्म का एक बहुत है कि ऐसा करने के लिए, केवल मेरे अनुभवहीन कोशिश नीचे देखते हैं:

>>> orig_ranges = [(1,5), (7,12), (2,3), (13,13), (13,17)] # should result in (1,5), (7,12), (13,17) 
>>> temp_ranges = {}  
>>> for r in orig_ranges: 
     temp_ranges.setdefault(r[0], []).append('+') 
     temp_ranges.setdefault(r[1], []).append('-') 

>>> start_count = end_count = 0 
>>> start = None 
>>> for key in temp_ranges: 
     if start is None: 
      start = key 
     start_count += temp_ranges[key].count('+') 
     end_count += temp_ranges[key].count('-') 
     if start_count == end_count: 
      print start, key 
      start = None 
      start_count = end_count = 0 

1 5 
7 12 
13 17 

सामान्य विचार बगल में है: हम पर्वतमाला एक और पर एक डाल के बाद (temp_ranges dict में), हम मिल सकता है शुरुआती सीमाओं और मूल श्रेणियों के अंत की गणना करके नई रचनाकृत श्रेणियां; एक बार हमें समानता मिल गई, हमें एक संयुक्त सीमा मिली।

1

संख्या के जोड़े में अपने पर्वतमाला बदलें। ये फ़ंक्शंस व्यक्तिगत आईपी को पूर्णांक मानों से और परिवर्तित कर देंगे।

def ip2long(ip): 
    packed = socket.inet_aton(ip) 
    return struct.unpack("!L", packed)[0] 

def long2ip(n): 
    unpacked = struct.pack('!L', n) 
    return socket.inet_ntoa(unpacked) 

अब आप सॉर्ट कर सकते हैं/संख्या के रूप में प्रत्येक रेंज के किनारों विलय, तो आईपी वापस करने के लिए परिवर्तित एक अच्छा प्रतिनिधित्व मिलता है। merging time ranges के बारे में यह प्रश्न एक अच्छा एल्गोरिदम है।


  1. संख्या की एक जोड़ी में 1.1.1.1-1.1.1.2 और 1.1.1.1-2 के अपने तार पार्स।बाद के प्रारूप के लिए, आप कर सकता है:

    x = '1.1.1.1-2' 
    first, add = x.split('-') 
    second = first.rsplit('.', 1)[0] + '.' + add 
    pair = ip2long(first), ip2long(second) 
    
  2. सरल संख्या की तुलना का उपयोग कर अतिव्यापी पर्वतमाला मर्ज करें।

  3. Convert वापस स्ट्रिंग प्रतिनिधित्व करने के लिए (अभी भी बाद के प्रारूप मान लिया गया):

    first, second = pair 
    first = long2ip(first) + '-' + long2ip(second).rsplit('.', 1)[1] 
    
0

मैं इन मामले में चारों ओर झूठ बोल रही थी आप उन्हें जरूरत है, सॉकेट का उपयोग कर/struct हालांकि

def ip_str_to_int(address): 
    """Convert IP address in form X.X.X.X to an int. 

    >>> ip_str_to_int('74.125.229.64') 
    1249764672 

    """ 
    parts = address.split('.') 
    parts.reverse() 
    return sum(int(v) * 256 ** i for i, v in enumerate(parts)) 


def ip_int_to_str(address): 
    """Convert IP address int into the form X.X.X.X. 

    >>> ip_int_to_str(1249764672) 
    '74.125.229.64' 

    """ 
    parts = [(address & 255 << 8 * i) >> 8 * i for i in range(4)] 
    parts.reverse() 
    return '.'.join(str(x) for x in parts) 
+0

यह सवाल का जवाब नहीं देता है, पहले से ही सेवर इसके अन्य संस्करणों में एल संस्करण, और समस्या को हल करने के लिए भी आवश्यक नहीं है। यदि आप इसे उत्तर के रूप में पोस्ट करना चाहते हैं, तो एक प्रश्न है जहां यह उचित होगा: [आईपी स्ट्रिंग से पूर्णांक में रूपांतरण, और पाइथन में पिछड़ा] [http://stackoverflow.com/questions/5619685/conversion-from- आईपी-स्ट्रिंग करने वाली पूर्णांक और पिछड़े-इन-अजगर)। – agf

संबंधित मुद्दे