ตัวอย่างเช่นกำหนดรายการ['one', 'two', 'one']
อัลกอริทึมควรกลับTrue
ในขณะที่ได้รับควรกลับ['one', 'two', 'three']
False
ตัวอย่างเช่นกำหนดรายการ['one', 'two', 'one']
อัลกอริทึมควรกลับTrue
ในขณะที่ได้รับควรกลับ['one', 'two', 'three']
False
คำตอบ:
ใช้set()
เพื่อลบรายการที่ซ้ำกันหากค่าทั้งหมดสามารถแฮชได้ :
>>> your_list = ['one', 'two', 'one']
>>> len(your_list) != len(set(your_list))
True
แนะนำสำหรับรายการสั้น ๆเท่านั้น:
any(thelist.count(x) > 1 for x in thelist)
ไม่ได้ใช้ในรายการยาว - มันสามารถใช้สัดส่วนเวลากับตารางของจำนวนรายการในรายการ!
สำหรับรายการที่ยาวขึ้นด้วยรายการที่ลบได้ (สตริง, ตัวเลข, และ c):
def anydup(thelist):
seen = set()
for x in thelist:
if x in seen: return True
seen.add(x)
return False
หากรายการของคุณไม่แฮช (รายการย่อย, dicts, ฯลฯ ) รายการนั้นจะดูอ่อนกว่าแม้ว่าจะเป็นไปได้ที่จะได้รับ O (N logN) หากเปรียบเทียบกันเป็นอย่างน้อย แต่คุณจำเป็นต้องรู้หรือทดสอบคุณสมบัติของรายการ (แฮชหรือไม่เปรียบเทียบหรือไม่) เพื่อให้ได้ประสิทธิภาพที่ดีที่สุดที่คุณสามารถทำได้ - O (N) สำหรับแฮชเบิ้ล, O (N log N) สำหรับการเปรียบเทียบที่ไม่แฮช มันลงไปที่ O (N กำลังสอง) และไม่มีสิ่งใดที่ทำได้เกี่ยวกับมัน :-(
all
มาก พจน์ที่มีค่าทั้งหมดเป็น True ซึ่งคุณพูดถึงก็คือการล้อเลียนที่ไร้สาระของไร้สาระซึ่งไร้set
ค่า แต่อย่างใด Big-O ไม่ใช่ทุกอย่างในการเขียนโปรแกรม
นี่เก่า แต่คำตอบที่นี่ทำให้ฉันแตกต่างออกไปเล็กน้อย หากคุณพร้อมสำหรับการทำความเข้าใจที่ไม่เหมาะสมคุณสามารถทำให้เกิดการลัดวงจรด้วยวิธีนี้
xs = [1, 2, 1]
s = set()
any(x in s or s.add(x) for x in xs)
# You can use a similar approach to actually retrieve the duplicates.
s = set()
duplicates = set(x for x in xs if x in s or s.add(x))
หากคุณมีความรักรูปแบบโปรแกรมการทำงานที่นี่เป็นฟังก์ชันที่มีประโยชน์ตนเองเอกสารและผ่านการทดสอบโดยใช้รหัสdoctest
def decompose(a_list):
"""Turns a list into a set of all elements and a set of duplicated elements.
Returns a pair of sets. The first one contains elements
that are found at least once in the list. The second one
contains elements that appear more than once.
>>> decompose([1,2,3,5,3,2,6])
(set([1, 2, 3, 5, 6]), set([2, 3]))
"""
return reduce(
lambda (u, d), o : (u.union([o]), d.union(u.intersection([o]))),
a_list,
(set(), set()))
if __name__ == "__main__":
import doctest
doctest.testmod()
จากตรงนั้นคุณสามารถทดสอบความเป็นหนึ่งเดียวได้โดยตรวจสอบว่าองค์ประกอบที่สองของคู่ที่คืนค่าว่างเปล่าหรือไม่:
def is_set(l):
"""Test if there is no duplicate element in l.
>>> is_set([1,2,3])
True
>>> is_set([1,2,1])
False
>>> is_set([])
True
"""
return not decompose(l)[1]
โปรดทราบว่าสิ่งนี้ไม่ได้มีประสิทธิภาพเนื่องจากคุณกำลังสร้างการแบ่งแยกอย่างชัดเจน แต่ตามแนวทางการลดคุณสามารถทำสิ่งที่เทียบเท่า (แต่มีประสิทธิภาพน้อยกว่าเล็กน้อย) เพื่อตอบ 5:
def is_set(l):
try:
def func(s, o):
if o in s:
raise Exception
return s.union([o])
reduce(func, l, set())
return True
except:
return False
ฉันคิดว่ามันจะมีประโยชน์ในการเปรียบเทียบการกำหนดเวลาของโซลูชันที่แตกต่างกันที่นำเสนอที่นี่ สำหรับสิ่งนี้ฉันใช้ห้องสมุดของตัวเองsimple_benchmark
:
ดังนั้นสำหรับกรณีนี้ทางออกจากDenis Otkidachนั้นเร็วที่สุด
วิธีการบางอย่างแสดงเส้นโค้งที่ชันกว่านี้คือวิธีการที่ปรับกำลังสองตามจำนวนองค์ประกอบ (วิธีแก้ปัญหาแรกของ Alex Martellis, wjandrea และโซลูชันของ Xavier Decorets ทั้งคู่) สิ่งสำคัญที่ต้องพูดถึงก็คือสารละลายแพนด้าจาก Keiku มีปัจจัยคงที่ที่ใหญ่มาก แต่สำหรับรายการที่มีขนาดใหญ่กว่านั้นก็เกือบจะทันกับโซลูชั่นอื่น ๆ
และในกรณีที่ซ้ำกันอยู่ที่ตำแหน่งแรก สิ่งนี้มีประโยชน์ในการดูว่าโซลูชันใดใช้การลัดวงจร:
ที่นี่มีหลายวิธีไม่ลัดวงจร: Kaiku, Frank, Xavier_Decoret (ทางออกแรก), Turn, Alex Martelli (ทางออกแรก) และแนวทางที่นำเสนอโดย Denis Otkidach (ซึ่งเร็วที่สุดในกรณีที่ไม่ซ้ำกัน)
ฉันรวมฟังก์ชั่นจากห้องสมุดของตัวเองที่นี่: iteration_utilities.all_distinct
ซึ่งสามารถแข่งขันกับโซลูชันที่เร็วที่สุดในกรณีที่ไม่มีการซ้ำซ้อนและดำเนินการในเวลาคงที่สำหรับกรณีที่ซ้ำกันเริ่มต้น (แม้ว่าจะไม่เร็วที่สุด)
รหัสสำหรับการวัดประสิทธิภาพ:
from collections import Counter
from functools import reduce
import pandas as pd
from simple_benchmark import BenchmarkBuilder
from iteration_utilities import all_distinct
b = BenchmarkBuilder()
@b.add_function()
def Keiku(l):
return pd.Series(l).duplicated().sum() > 0
@b.add_function()
def Frank(num_list):
unique = []
dupes = []
for i in num_list:
if i not in unique:
unique.append(i)
else:
dupes.append(i)
if len(dupes) != 0:
return False
else:
return True
@b.add_function()
def wjandrea(iterable):
seen = []
for x in iterable:
if x in seen:
return True
seen.append(x)
return False
@b.add_function()
def user(iterable):
clean_elements_set = set()
clean_elements_set_add = clean_elements_set.add
for possible_duplicate_element in iterable:
if possible_duplicate_element in clean_elements_set:
return True
else:
clean_elements_set_add( possible_duplicate_element )
return False
@b.add_function()
def Turn(l):
return Counter(l).most_common()[0][1] > 1
def getDupes(l):
seen = set()
seen_add = seen.add
for x in l:
if x in seen or seen_add(x):
yield x
@b.add_function()
def F1Rumors(l):
try:
if next(getDupes(l)): return True # Found a dupe
except StopIteration:
pass
return False
def decompose(a_list):
return reduce(
lambda u, o : (u[0].union([o]), u[1].union(u[0].intersection([o]))),
a_list,
(set(), set()))
@b.add_function()
def Xavier_Decoret_1(l):
return not decompose(l)[1]
@b.add_function()
def Xavier_Decoret_2(l):
try:
def func(s, o):
if o in s:
raise Exception
return s.union([o])
reduce(func, l, set())
return True
except:
return False
@b.add_function()
def pyrospade(xs):
s = set()
return any(x in s or s.add(x) for x in xs)
@b.add_function()
def Alex_Martelli_1(thelist):
return any(thelist.count(x) > 1 for x in thelist)
@b.add_function()
def Alex_Martelli_2(thelist):
seen = set()
for x in thelist:
if x in seen: return True
seen.add(x)
return False
@b.add_function()
def Denis_Otkidach(your_list):
return len(your_list) != len(set(your_list))
@b.add_function()
def MSeifert04(l):
return not all_distinct(l)
และสำหรับข้อโต้แย้ง:
# No duplicate run
@b.add_arguments('list size')
def arguments():
for exp in range(2, 14):
size = 2**exp
yield size, list(range(size))
# Duplicate at beginning run
@b.add_arguments('list size')
def arguments():
for exp in range(2, 14):
size = 2**exp
yield size, [0, *list(range(size)]
# Running and plotting
r = b.run()
r.plot()
ฉันเพิ่งตอบคำถามที่เกี่ยวข้องเพื่อสร้างรายการที่ซ้ำกันทั้งหมดในรายการโดยใช้ตัวสร้าง มันมีข้อได้เปรียบที่ถ้าใช้เพียงเพื่อสร้าง 'ถ้ามีซ้ำ' จากนั้นคุณเพียงแค่ต้องได้รับรายการแรกและส่วนที่เหลือสามารถละเว้นซึ่งเป็นทางลัดที่ดีที่สุด
นี่เป็นวิธีที่น่าสนใจที่ฉันได้ดัดแปลงจากmoooeeeep :
def getDupes(l):
seen = set()
seen_add = seen.add
for x in l:
if x in seen or seen_add(x):
yield x
list(getDupes(etc))
ดังนั้นรายชื่อเต็มของหลอกจะเป็น หากต้องการทดสอบ "ถ้า" มีการล่อจับคู่ควรห่อดังนี้:
def hasDupes(l):
try:
if getDupes(l).next(): return True # Found a dupe
except StopIteration:
pass
return False
สิ่งนี้จะปรับขนาดได้ดีและให้เวลาการทำงานที่สม่ำเสมอทุกที่ที่มีค่าล่ออยู่ในรายการ - ฉันทดสอบด้วยรายการที่มีความยาวไม่เกิน 1 เมตร หากคุณรู้บางอย่างเกี่ยวกับข้อมูลโดยเฉพาะอย่างยิ่งคู่หูนั้นมีแนวโน้มที่จะปรากฏขึ้นในช่วงครึ่งปีแรกหรือสิ่งอื่น ๆ ที่ให้คุณเบี่ยงเบนความต้องการของคุณเช่นต้องการได้รับการดักจับที่เกิดขึ้นจริง ที่อาจมีประสิทธิภาพสูงกว่า สองที่ฉันแนะนำคือ ...
วิธีการตาม dict ง่ายอ่านง่ายมาก:
def getDupes(c):
d = {}
for i in c:
if i in d:
if d[i]:
yield i
d[i] = False
else:
d[i] = True
ใช้ประโยชน์จาก itertools (เป็นหลัก ifilter / izip / tee) ในรายการที่เรียงลำดับมีประสิทธิภาพมากถ้าคุณได้รับการหลอกทั้งหมด แต่ไม่เร็วพอที่จะได้รับเพียงครั้งแรก:
def getDupes(c):
a, b = itertools.tee(sorted(c))
next(b, None)
r = None
for k, g in itertools.ifilter(lambda x: x[0]==x[1], itertools.izip(a, b)):
if k != r:
yield k
r = k
เหล่านี้เป็นนักแสดงชั้นนำจากวิธีที่ฉันลองสำหรับรายการเพลงเต็มโดยมีล่อแรกเกิดขึ้นที่ใดก็ได้ในรายการองค์ประกอบ 1 เมตรตั้งแต่เริ่มต้นจนถึงกลาง มันน่าแปลกใจที่ค่าใช้จ่ายขั้นตอนการจัดเรียงเพิ่มเล็กน้อย ไมล์สะสมของคุณอาจแตกต่างกันไป แต่นี่คือผลลัพธ์ที่กำหนดเวลาของฉัน:
Finding FIRST duplicate, single dupe places "n" elements in to 1m element array
Test set len change : 50 - . . . . . -- 0.002
Test in dict : 50 - . . . . . -- 0.002
Test in set : 50 - . . . . . -- 0.002
Test sort/adjacent : 50 - . . . . . -- 0.023
Test sort/groupby : 50 - . . . . . -- 0.026
Test sort/zip : 50 - . . . . . -- 1.102
Test sort/izip : 50 - . . . . . -- 0.035
Test sort/tee/izip : 50 - . . . . . -- 0.024
Test moooeeeep : 50 - . . . . . -- 0.001 *
Test iter*/sorted : 50 - . . . . . -- 0.027
Test set len change : 5000 - . . . . . -- 0.017
Test in dict : 5000 - . . . . . -- 0.003 *
Test in set : 5000 - . . . . . -- 0.004
Test sort/adjacent : 5000 - . . . . . -- 0.031
Test sort/groupby : 5000 - . . . . . -- 0.035
Test sort/zip : 5000 - . . . . . -- 1.080
Test sort/izip : 5000 - . . . . . -- 0.043
Test sort/tee/izip : 5000 - . . . . . -- 0.031
Test moooeeeep : 5000 - . . . . . -- 0.003 *
Test iter*/sorted : 5000 - . . . . . -- 0.031
Test set len change : 50000 - . . . . . -- 0.035
Test in dict : 50000 - . . . . . -- 0.023
Test in set : 50000 - . . . . . -- 0.023
Test sort/adjacent : 50000 - . . . . . -- 0.036
Test sort/groupby : 50000 - . . . . . -- 0.134
Test sort/zip : 50000 - . . . . . -- 1.121
Test sort/izip : 50000 - . . . . . -- 0.054
Test sort/tee/izip : 50000 - . . . . . -- 0.045
Test moooeeeep : 50000 - . . . . . -- 0.019 *
Test iter*/sorted : 50000 - . . . . . -- 0.055
Test set len change : 500000 - . . . . . -- 0.249
Test in dict : 500000 - . . . . . -- 0.145
Test in set : 500000 - . . . . . -- 0.165
Test sort/adjacent : 500000 - . . . . . -- 0.139
Test sort/groupby : 500000 - . . . . . -- 1.138
Test sort/zip : 500000 - . . . . . -- 1.159
Test sort/izip : 500000 - . . . . . -- 0.126
Test sort/tee/izip : 500000 - . . . . . -- 0.120 *
Test moooeeeep : 500000 - . . . . . -- 0.131
Test iter*/sorted : 500000 - . . . . . -- 0.157
.next()
โทรในบล็อกรหัสที่สองของคุณไม่ทำงานบน Python 3.x ฉันคิดว่าnext(getDupes(l))
ควรทำงานกับเวอร์ชัน Python ดังนั้นจึงควรเปลี่ยน
ifilter
และìzip
สามารถถูกแทนที่ได้ง่ายๆโดยการติดตั้งในตัวfilter
และzip
ใน Python 3.x
อีกวิธีในการทำสิ่งนี้อย่างกระชับคือCounterเคาน์เตอร์
หากต้องการกำหนดว่ามีรายการซ้ำในรายการเดิมหรือไม่:
from collections import Counter
def has_dupes(l):
# second element of the tuple has number of repetitions
return Counter(l).most_common()[0][1] > 1
หรือเพื่อรับรายการที่มีรายการซ้ำ:
def get_dupes(l):
return [k for k, v in Counter(l).items() if v > 1]
my_list = ['one', 'two', 'one']
duplicates = []
for value in my_list:
if my_list.count(value) > 1:
if value not in duplicates:
duplicates.append(value)
print(duplicates) //["one"]
ฉันพบว่าสิ่งนี้ทำผลงานได้ดีที่สุดเพราะมันเป็นการลัดวงจรการทำงานเมื่อพบซ้ำครั้งแรกจากนั้นอัลกอริทึมนี้มีความซับซ้อนของเวลาและพื้นที่ O (n) โดยที่ n คือความยาวของรายการ:
def has_duplicated_elements(iterable):
""" Given an `iterable`, return True if there are duplicated entries. """
clean_elements_set = set()
clean_elements_set_add = clean_elements_set.add
for possible_duplicate_element in iterable:
if possible_duplicate_element in clean_elements_set:
return True
else:
clean_elements_set_add( possible_duplicate_element )
return False
ฉันไม่รู้จริงๆว่าฉากทำอะไรอยู่เบื้องหลังฉันก็เลยอยากให้มันเรียบง่าย
def dupes(num_list):
unique = []
dupes = []
for i in num_list:
if i not in unique:
unique.append(i)
else:
dupes.append(i)
if len(dupes) != 0:
return False
else:
return True
วิธีการแก้ปัญหาที่ง่ายขึ้นมีดังนี้ เพียงตรวจสอบจริง / เท็จด้วย.duplicated()
วิธีการแพนด้าแล้วนำผลรวม โปรดดู เอกสารประกอบ pandas.Series.duplicated - pandas 0.24.1
import pandas as pd
def has_duplicated(l):
return pd.Series(l).duplicated().sum() > 0
print(has_duplicated(['one', 'two', 'one']))
# True
print(has_duplicated(['one', 'two', 'three']))
# False
หากรายการมีรายการที่ไม่สามารถล้างได้คุณสามารถใช้โซลูชันของ Alex Martelliแต่มีรายการแทนชุดแม้ว่ามันจะช้าลงสำหรับอินพุตที่มีขนาดใหญ่กว่า: O (N ^ 2)
def has_duplicates(iterable):
seen = []
for x in iterable:
if x in seen:
return True
seen.append(x)
return False
ฉันใช้วิธี pyrospade เพื่อความเรียบง่ายและปรับเปลี่ยนเล็กน้อยในรายการสั้น ๆ ที่ทำจากรีจิสทรี Windows ที่ไม่ตรงตามตัวพิมพ์ใหญ่ - เล็ก
หากสตริงค่า raw PATH ถูกแบ่งออกเป็นแต่ละพา ธ พา ธ 'null' ทั้งหมด (สตริงว่างหรือ whitespace-only) สามารถลบออกได้โดยใช้:
PATH_nonulls = [s for s in PATH if s.strip()]
def HasDupes(aseq) :
s = set()
return any(((x.lower() in s) or s.add(x.lower())) for x in aseq)
def GetDupes(aseq) :
s = set()
return set(x for x in aseq if ((x.lower() in s) or s.add(x.lower())))
def DelDupes(aseq) :
seen = set()
return [x for x in aseq if (x.lower() not in seen) and (not seen.add(x.lower()))]
PATH ต้นฉบับมีทั้งรายการ 'null' และรายการซ้ำเพื่อการทดสอบ:
[list] Root paths in HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment:PATH[list] Root paths in HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
1 C:\Python37\
2
3
4 C:\Python37\Scripts\
5 c:\python37\
6 C:\Program Files\ImageMagick-7.0.8-Q8
7 C:\Program Files (x86)\poppler\bin
8 D:\DATA\Sounds
9 C:\Program Files (x86)\GnuWin32\bin
10 C:\Program Files (x86)\Intel\iCLS Client\
11 C:\Program Files\Intel\iCLS Client\
12 D:\DATA\CCMD\FF
13 D:\DATA\CCMD
14 D:\DATA\UTIL
15 C:\
16 D:\DATA\UHELP
17 %SystemRoot%\system32
18
19
20 D:\DATA\CCMD\FF%SystemRoot%
21 D:\DATA\Sounds
22 %SystemRoot%\System32\Wbem
23 D:\DATA\CCMD\FF
24
25
26 c:\
27 %SYSTEMROOT%\System32\WindowsPowerShell\v1.0\
28
เส้นทาง Null ถูกลบ แต่ยังมีข้อมูลซ้ำเช่น (1, 3) และ (13, 20):
[list] Null paths removed from HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment:PATH
1 C:\Python37\
2 C:\Python37\Scripts\
3 c:\python37\
4 C:\Program Files\ImageMagick-7.0.8-Q8
5 C:\Program Files (x86)\poppler\bin
6 D:\DATA\Sounds
7 C:\Program Files (x86)\GnuWin32\bin
8 C:\Program Files (x86)\Intel\iCLS Client\
9 C:\Program Files\Intel\iCLS Client\
10 D:\DATA\CCMD\FF
11 D:\DATA\CCMD
12 D:\DATA\UTIL
13 C:\
14 D:\DATA\UHELP
15 %SystemRoot%\system32
16 D:\DATA\CCMD\FF%SystemRoot%
17 D:\DATA\Sounds
18 %SystemRoot%\System32\Wbem
19 D:\DATA\CCMD\FF
20 c:\
21 %SYSTEMROOT%\System32\WindowsPowerShell\v1.0\
และในที่สุดพวกมันก็ถูกลบออก:
[list] Massaged path list from in HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment:PATH
1 C:\Python37\
2 C:\Python37\Scripts\
3 C:\Program Files\ImageMagick-7.0.8-Q8
4 C:\Program Files (x86)\poppler\bin
5 D:\DATA\Sounds
6 C:\Program Files (x86)\GnuWin32\bin
7 C:\Program Files (x86)\Intel\iCLS Client\
8 C:\Program Files\Intel\iCLS Client\
9 D:\DATA\CCMD\FF
10 D:\DATA\CCMD
11 D:\DATA\UTIL
12 C:\
13 D:\DATA\UHELP
14 %SystemRoot%\system32
15 D:\DATA\CCMD\FF%SystemRoot%
16 %SystemRoot%\System32\Wbem
17 %SYSTEMROOT%\System32\WindowsPowerShell\v1.0\
def check_duplicates(my_list):
seen = {}
for item in my_list:
if seen.get(item):
return True
seen[item] = True
return False