2013-01-14 14 views
9

Masz listę numerów i chcesz odfiltrować te liczby, które zawierają unikalne cyfry, tj. Każda cyfra może wystąpić tylko raz w liczbie.Jak filtrować liczby zawierające unikalne cyfry?

pozytywne przykłady:

  • 58293,6
  • 0,1246

ujemne przykładów:

  • 9585 (5 występuje dwukrotnie)
  • 58293.666 (6 występuje trzykrotnie)
  • 0,12461 (1 występuje dwukrotnie)

Jak to zrobić? Moim pomysłem jest przekonwertowanie każdej liczby na ciąg znaków, a następnie sprawdzenie, czy rozmiar zestawu złożonego ze znaków ciągu jest równy długości łańcucha. Coś w tym stylu:

def uniques(numbers): 
    for number in numbers: 
     str_number = str(number) 
     if len(set(str_number)) == len(str_number): 
      yield number 

for i in uniques(xrange(1000, 1050)): 
    print i 

1023 
1024 
1025 
1026 
1027 
1028 
1029 
1032 
1034 
1035 
1036 
1037 
1038 
1039 
1042 
1043 
1045 
1046 
1047 
1048 
1049 

Czy istnieje sposób, aby to zrobić bez konwersji liczb całkowitych na ciągi?

+2

Nie konwertuj z powrotem. Weź inną zmienną dla usztywnionej wersji numeru. – Matthias

+0

Szczególnie jeśli twoje wejście zawiera 'float's ... –

+3

Poza tym, myślę, że twoje podejście jest proste i łatwe do zrozumienia. Poszedłbym z tym. –

Odpowiedz

8

Czy istnieje sposób to zrobić bez konwersji liczb całkowitych do struny pierwszy, a następnie konwertować je z powrotem?

Tak, można użyć divmod znaleźć podstawę 10 cyfr, ale to nie jest szybsze niż metoda ty Wysłany:

def uniques2(numbers): 
    for number in numbers: 
     seen = set() 
     quotient = number 
     while quotient > 10: 
      quotient, remainder = divmod(quotient, 10) 
      if remainder in seen: 
       break 
      else: 
       seen.add(remainder) 
     else: 
      yield number 
+0

+1. Bardzo dobrze. Właśnie tego szukałem. Na moim komputerze Twoja metoda jest nawet nieco szybsza niż moje rozwiązanie. Dobra robota. :) – pemistahl

5

Spróbuj:

def predicate(n): 
    s = repr(n) 
    return len(s) == len(set(s)) 
filtered_numbers = [ n for n in numbers if predicate(n) ] 

lub, jeśli wolisz funkcję filtra:

filtered_numbers = filter(predicate, numbers) 

czyli

filtered_numbers = filter(lambda n: len(repr(n)) == len(set(repr(n))), numbers) 
+1

+1 dla 'lambda' :) –

+1

Używając' repr' zamiast 'str', to jest to samo, mówiąc w różny sposób – Abhijit

+1

@Ahhijit, jest mała różnica między repr (1.0/7.0) i str (1.0 /7.0), na przykład. –

3

Jeśli chcesz rozwiązanie regex opartych Rozważmy następujący regex :

(?![\d.]*([\d.])[\d.]*\1)^[\d.]+$ 

Oznacza to dopasowanie sekwencji cyfr i kropek, jeśli nie ma żadnych duplikatów cyfr i nie ma duplikatu dziesiętnego.

Update (dzięki @frb): prawidłowy sposób napisać to w Pythonie jest

re.match(r"(?![\d.]*([\d.])[\d.]*\1)^[\d.]+$",str_number).group(0) 
+0

@ frb IIUC, źródło jest liczbą _ zwroconą_ na łańcuch, więc '1.1.1' nie może wystąpić. W każdym razie '1.1.1' powinno _nie_ pasować. Jeśli chcesz złożyć skargę, narzekaj, że '1.2.3'' –

+0

@frb' 1.1.1' nie pasuje: link: http://regexpal.com/?flags=g®ex=(%3F!%5B%5Cd. % 5D * (% 5Cd)% 5B% 5Cd.% 5D *% 5C1)% 5E% 5B% 5Cd.% 5D% 2B% 24 & input = 1.1.1) i nie rozumiem, dlaczego Python powinien być inny, ani nie mogę spróbuj –

+0

@ frb może potrzebuje trochę ucieczki lub '$ 1' zamiast' \ 1' dla backreferencji? Jestem fanem regexów, a nie python-guru. –

0

Korzystanie collections.Counter:

from collections import Counter 

def unique(seq): 
    return any(x > 1 for x in Counter(seq).values()) 

ten będzie działać dla dowolnej sekwencji, nie tylko dla łańcuchów.

I dopiero teraz zauważyłem, że nie chcesz konwertować na ciągi znaków ... nie wiem dlaczego, ale pozwolę, aby odpowiedź została.

+0

-1. Nie o to prosiłem. Proszę ponownie przeczytać moje pytanie. – pemistahl

Powiązane problemy