#!/usr/bin/env python3 """ Na trzech serwerach zbierano listy adresów IP, z których następował nietypowy ruch przychodzący. Napisz program, który je przefiltruje i wypisze: 1. adresy zarejestrowane na poszczególnych serwerach, ale bez powtórzeń – np. adres 124.194.117.151 pojawia się na serwerze nr 1 dwukrotnie, nie chcemy tego, 2. adresy powtarzające się w każdym z zestawów i ile razy, 3. adresy, które są w logach wszystkich serwerów jednocześnie, 4. całą pulę zebranych adresów – czyli te, które pojawiły się gdziekolwiek, 5. adresy, które pojawiły się wyłącznie na jednym z serwerów i nigdzie indziej (wypisz to dla każdego z trzech serwerów). Kod może wydawać się znacznie bardziej obszerny niż jest w istocie - w niektórych miejscach starałem się pokazać różne sposoby rozwiązania i je skomentować. Poniższe adresy IP zostały wygenerowane losowo, proszę ich nie traktować jako IP potencjalnych złoczyńców. :-) """ ip_serwer1 = ["124.194.117.151", "102.26.179.4", "124.194.117.151", "228.50.34.103", "72.64.203.50", "156.71.102.245", "67.33.135.75", "240.58.59.186", "75.245.64.35", "42.107.64.123"] ip_serwer2 = ["87.60.211.200", "124.194.117.151", "39.50.47.1", "35.104.29.70", "75.245.64.35", "233.152.75.71", "196.40.247.11", "110.84.193.208", "60.69.233.205", "113.164.109.228"] ip_serwer3 = ["69.24.218.219", "30.186.239.61", "215.131.155.192", "234.125.132.67", "75.245.64.35", "75.245.64.35", "167.13.89.15", "9.238.14.91", "127.97.147.85", "67.33.135.75", "129.21.227.118"] #Przekształcamy listy w zbiory. W zbiorze każdy element może występować tylko raz, #więc duplikaty automatycznie znikają! zbior1=set(ip_serwer1) zbior2=set(ip_serwer2) zbior3=set(ip_serwer3) ###################################################################################### #Wypisujemy adresy bez powtórzeń (punkt 1.) print("IP z serwera1:") print(zbior1) print("IP z serwera2:") print(zbior2) print("IP z serwera3:") print(zbior3) print("-"*80) ###################################################################################### #Wypisujemy powtarzające się adresy (punkt 2.) print("Powtórzenia:") def powtorzenia(lista,zbior): """ Można to zrobić na różne sposoby, np. wykorzystując metodę .count() ze wskazówki. Na moim komputerze dla ok. 40000 ip zajmuje to ok. 5 sekund. """ duplikaty = [] for ip in zbior: #iterujemy już po zbiorze, bo nie ma duplikatów c = lista.count(ip) #.count liczy wystąpienia w liście if c > 1 : duplikaty.append((ip,c)) return duplikaty """ Jeżeli ktoś chciałby zrobić to za pomocą listy składanej, to w zasadzie mógłby. Niestety, wtedy operacja liczenia .count(ip) jest wykonywana dwa razy. Na moim komputerze dla ok. 40000 ip zajmuje to ok. 10 sekund. """ #return [(ip,lista.count(ip)) for ip in zbior if lista.count(ip) > 1] """ Powyższego problemu można uniknąć przez użycie zagnieżdżonej listy sklejanej. Zastanawiałbym się jednak czy warto ze względu na czytelność kodu... Na moim komputerze dla ok. 40000 ip zajmuje to ponownie ok. 5 sekund. """ #return [duplikat for ip in zbior for duplikat in [(ip,lista.count(ip))] if duplikat[1] > 1] """ Można też porzucić pomysł z .count() i zrobić to podobnie do tego jak robiliśmy liczenie znaków w pliku. Przeszukiwanie słowników jest drastycznie szybsze. Na moim komputerze dla ok. 40000 ip zajmuje to zaledwie 0.0025 sekundy! """ #duplikaty = {} #for ip in lista: duplikaty[ip] = duplikaty.get(ip,0) + 1 #return [(ip,count) for (ip,count) in duplikaty.items() if count > 1] #0.0025s !!! """ W zasadzie w przypadku tak typowego zagadnienia moglibyśmy nie odkrywać koła na nowo i posłużyć się modułem collections, która - tak jak w podejściu powyżej, da nam do dyspozycji słownik z elementami i liczbą ich wystąpień. Na moim komputerze dla ok. 40000 ip zajmuje to ok. 0.0020 sekundy, jeszcze trochę krócej. """ #import collections # bardziej elegancko byłoby to wrzucić na początku pliku #duplikaty = [(ip,count) for (ip,count) in collections.Counter(lista).items() if count > 1] #return duplikaty print("Serwer1:",powtorzenia(ip_serwer1,zbior1)) print("Serwer2:",powtorzenia(ip_serwer2,zbior2)) print("Serwer3:",powtorzenia(ip_serwer3,zbior3)) print("-"*80) ###################################################################################### """ Pozostałe punkty są trywialne jeśli używamy rachunku na zbiorach. """ print("Część wspólna: ", zbior1 & zbior2 & zbior3) print("-"*80) print("Suma: ", zbior1 | zbior2 | zbior3) print("-"*80) print("Tylko 1: ", zbior1 - zbior2 - zbior3) print("-"*80) print("Tylko 2: ", zbior2 - zbior1 - zbior3) print("-"*80) print("Tylko 3: ", zbior3 - zbior1 - zbior2) print("-"*80)