Làm thế nào chúng ta có thể giả định rằng các hoạt động cơ bản trên số mất thời gian không đổi?


74

Thông thường trong các thuật toán, chúng tôi không quan tâm đến việc so sánh, cộng hoặc trừ các số - chúng tôi giả sử chúng chạy trong thời gian . Ví dụ: chúng tôi giả sử điều này khi chúng tôi nói rằng sắp xếp dựa trên so sánh là , nhưng khi số lượng quá lớn để khớp với các thanh ghi, chúng tôi thường biểu diễn chúng dưới dạng các mảng để các thao tác cơ bản yêu cầu tính toán thêm cho mỗi phần tử.O ( n log n )O(1)O(nlogn)

Có bằng chứng nào cho thấy việc so sánh hai số (hoặc các hàm số học nguyên thủy khác) có thể được thực hiện trong không? Nếu không tại sao chúng ta lại nói rằng sắp xếp dựa trên so sánh là ?O ( n log n )O(1)O(nlogn)


Tôi gặp phải vấn đề này khi tôi đã trả lời một câu hỏi SO và tôi nhận ra rằng thuật toán của tôi không phải là vì sớm hay muộn tôi phải đối phó với tuổi lớn int, cũng có thể nó không phải là giả đa thức thuật toán thời gian, nó là .PO(n)P


3
Nếu bạn định tính độ phức tạp của việc so sánh các số, bạn cũng nên viết giới hạn độ phức tạp của mình theo kích thước bit của đầu vào. Vì vậy, với -bit, kích thước bit của đầu vào là và việc sắp xếp có thể được thực hiện trong thời gian . w n = N w O ( N w log N ) = O ( n log n )N wn=NwO(NwlogN)=O(nlogn)
Sasho Nikolov

2
về cơ bản có hai "cảnh giới" hoặc "chế độ" của nghiên cứu phức tạp. nói chung các thao tác được giả sử cho các hoạt động "độ rộng cố định", đó là một xấp xỉ hợp lý cho hầu hết các ngôn ngữ máy tính có biểu diễn số độ rộng cố định bao gồm cả dấu phẩy động, ví dụ 2-4 byte (xem ví dụ tiêu chuẩn IEEE). sau đó có "số học chính xác tùy ý" trong đó các số có kích thước tùy ý và có nghiên cứu chính xác / cẩn thận hơn về sự phức tạp của các hoạt động. bối cảnh trước là nhiều hơn trong phân tích ứng dụng và sau đó là nhiều hơn trong phân tích lý thuyết / trừu tượng. O(1)
vzn

Câu trả lời:


75

Đối với những người như tôi nghiên cứu các thuật toán để kiếm sống, mô hình tính toán tiêu chuẩn của thế kỷ 21 là RAM nguyên . Mô hình nhằm phản ánh hành vi của máy tính thực chính xác hơn mô hình máy Turing. Máy tính trong thế giới thực xử lý số nguyên nhiều bit trong thời gian không đổi bằng phần cứng song song; không phải là số nguyên tùy ý , nhưng (vì kích thước từ tăng dần theo thời gian) cũng không phải là số nguyên kích thước cố định .

Mô hình phụ thuộc vào một tham số duy nhất , được gọi là kích thước từ . Mỗi địa chỉ bộ nhớ chứa một số nguyên -bit hoặc từ . Trong mô hình này, kích thước đầu vào là số lượng từ trong đầu vào và thời gian chạy của thuật toán là số lượng thao tác trên các từ . Các phép toán số học tiêu chuẩn (cộng, trừ, nhân, chia số nguyên, phần dư, so sánh) và các phép toán boolean (bitwise và, hoặc, xor, shift, xoay) trên các từ đòi hỏi thời gian theo định nghĩa .w n O ( 1 )wwnO(1)

Chính thức, kích thước từ KHÔNG phải là hằng sốw cho mục đích phân tích thuật toán trong mô hình này. Để làm cho mô hình phù hợp với trực giác, chúng tôi yêu cầu , vì nếu không, chúng tôi thậm chí không thể lưu trữ số nguyên trong một từ. Tuy nhiên, đối với hầu hết các thuật toán phi số, thời gian chạy thực sự độc lập , bởi vì các thuật toán đó không quan tâm đến biểu diễn nhị phân cơ bản của đầu vào của chúng. Sáp nhập và heapsort đều chạy trong thời gian ; median-of-3-quicksort chạy trong thời gian trong trường hợp xấu nhất. Một ngoại lệ đáng chú ý là sắp xếp cơ số nhị phân, chạy trong thời gian .wlog2nnwO(nlogn)O(n2)O(nw)

Cài đặt cho chúng ta mô hình RAM chi phí logarit truyền thống. Nhưng một số thuật toán RAM nguyên được thiết kế cho kích thước từ lớn hơn, như thuật toán sắp xếp số nguyên theo thời gian tuyến tính của Andersson et al. , yêu cầu .w=Θ(logn)w=Ω(log2+εn)

Đối với nhiều thuật toán phát sinh trong thực tế, kích thước từ đơn giản không phải là vấn đề và chúng ta có thể (và làm) rơi vào mô hình RAM chi phí thống nhất đơn giản hơn nhiều. Các chỉ gặp khó khăn nghiêm trọng xuất phát từ nhân lồng nhau, có thể được sử dụng để xây dựng rất nguyên lớn rất nhanh chóng. Nếu chúng tôi có thể thực hiện số học trên các số nguyên tùy ý trong thời gian không đổi, chúng tôi có thể giải quyết bất kỳ vấn đề nào trong PSPACE trong thời gian đa thức .w

Cập nhật: Tôi cũng nên đề cập rằng có các ngoại lệ đối với "mô hình chuẩn", như thuật toán nhân số nguyên của Fürer , sử dụng máy Turing đa nhiệm (hoặc tương đương, "RAM bit") và hầu hết các thuật toán hình học, được phân tích theo lý thuyết mô hình "RAM thực" lý tưởng nhưng sạch sẽ .

Vâng, đây là một con giun.


3
Tôi biết tôi chỉ nên bỏ phiếu, nhưng không thể ngăn mình nói điều này: Đây là câu trả lời tốt nhất. Bí quyết là (1) các phép toán số học là thời gian không đổi theo định nghĩa và điều đó không sao vì về lý thuyết bạn có thể chọn bất kỳ mô hình nào và (2) bạn nên có một số lý do để chọn một mô hình nhất định và câu trả lời này giải thích chúng là gì.
rgrig

Tôi đồng ý với rgig, (Ngoài ra tôi chỉ nên bỏ phiếu), nhưng một vấn đề nhỏ là kích thước đầu vào không liên quan đến số đầu vào, ví dụ: Nếu tôi có đầu vào thì số lớn nhất của tôi là và nếu tôi chọn mô hình tính toán như cách tôi thích, điều này khiến thuật toán thời gian đa thức giả trở thành , phải không? nmP

1
Nếu đầu vào của bạn bao gồm các số có nhiều hơn bit, thì để phù hợp với mô hình, bạn phải chia chúng thành các khối -bit, giống như trong cuộc sống thực. Ví dụ: nếu đầu vào của bạn bao gồm số nguyên từ đến , thì kích thước đầu vào thực sự của bạn là . Do đó, thời gian chạy giả đa thức như thời gian vẫn theo cấp số nhân trong kích thước đầu vào khi lớn. w N 0 M N log w M = ( N lg M ) / ( lg w ) O ( N M ) MwwN0MNlogwM=(NlgM)/(lgw)O(NM)M
JeffE

Có thuật toán nào được phân tích trong mô hình RAM thực không phải là thuật toán "Loại RAM theo thứ tự" bí mật không? Tôi chưa bao giờ nghĩ về nó nhiều, nhưng không thể nhanh chóng đưa ra một ví dụ không.
Louis

1
@Louis: Có, rất nhiều: sơ đồ Voronoi, đường dẫn ngắn nhất Euclide, cắt đệ quy, cây phân vùng đơn giản, .... Nhưng ví dụ tốt nhất là loại bỏ Gaussian, chạy trong thời gian trên mô hình RAM thực (hoặc RAM số nguyên chi phí đơn vị, nhưng cần thời gian trên RAM số nguyên.O ( n 4 )O(n3)O(n4)
JeffE

24

Nó chỉ phụ thuộc vào bối cảnh. Khi xử lý độ phức tạp của cấp độ bit của thuật toán, chúng tôi không nói rằng việc thêm hai số bit là , chúng tôi nói đó là . Tương tự cho phép nhân, v.v.O ( 1 ) O ( n )nO(1)O(n)


Từ bài viết được tham chiếu của bạn: "có thể được đo theo hai cách khác nhau: một theo số nguyên được kiểm tra hoặc nhân, và một về số lượng chữ số nhị phân (bit) trong các số nguyên đó", nhưng điều này không đúng, chúng tôi không đúng phải luôn luôn đo theo kích thước của đầu vào.

1
@SaeedAmiri: nó chỉ phụ thuộc vào mã hóa được sử dụng. Ví dụ, trong bài viết, nếu đầu vào là một số nguyên được chỉ định bằng cách sử dụng mã hóa đơn nguyên, phân chia thử nghiệm sẽ chỉ yêu cầu . Đây là đa thức trong kích thước của đầu vào! Điều này có nghĩa là bao thanh toán bằng cách phân chia thử nghiệm là trong ? Không, thuật toán là giả đa thức . Sử dụng mã hóa nhị phân phổ biến, bạn sẽ nhận được một thuật toán theo cấp số nhân, một lần nữa theo kích thước của đầu vào. Như đã nêu, điều này xảy ra bởi vì số bit trong đầu vào đã trở nên nhỏ hơn theo cấp số nhân thay đổi mã hóa của nó. nθ(n1/2)Pn
Massimo Cafaro

Nhân tiện, thuật toán giả đa thức thực sự có thể hữu ích, nếu thứ tự độ lớn của các tham số của chúng trong các trường hợp thực tế là khá thấp. Ví dụ nổi tiếng nhất có lẽ là thuật toán giả đa thức để giải bài toán ba lô.
Massimo Cafaro

Trước tiên tôi nên đề cập rằng trang wiki được tham chiếu của bạn không tốt vì nó không có bất kỳ tài liệu tham khảo tốt nào. Ngoài ra tôi không biết tại sao bạn nghĩ rằng tôi đang nói về thuật toán thời gian giả đa thức, có thể là do kích thước đầu vào thông thường là mông trong trường hợp này? Nhưng tôi không nói về họ, tôi chủ yếu nói về các vấn đề trong ngay cả khi giả định kích thước đầu vào, như sắp xếp, vì chúng ta không thể gian lận và nói rằng vấn đề NPC là ở Tôi nghĩ chúng ta không nên nói sắp xếp là ngoại trừ chúng tôi có bằng chứng chính thức để bỏ qua so sánh. PPO(nlogn)

Tôi đang thảo luận về các thuật toán giả đa thức để tập trung sự chú ý của bạn vào kích thước của đầu vào, để cho bạn thấy rằng nó có thể gây hiểu nhầm. Đây là một ví dụ khác. Bạn được cung cấp một số tự nhiên làm đầu vào, giả sử và thuật toán chạy một vòng lặp trong đó nó thực hiện các thao tác thời gian cho lần lặp. Độ phức tạp của thuật toán vòng lặp đơn giản này được đo là một hàm của kích thước đầu vào, là . Vì là kích thước đầu vào, thuật toán theo cấp số nhân trong kích thước đầu vào! Nghĩ về điều này. Bây giờ bạn có thể hiểu ý của tôi với "Nó chỉ phụ thuộc vào ngữ cảnh". nO(1)nO(n)=O(2lgn)lgn
Massimo Cafaro

16

Để trả lời câu hỏi như đã nêu: các nhà thuật toán chỉ cần làm điều đó, khá thường xuyên, bằng cách sử dụng mô hình RAM. Để sắp xếp, trong nhiều trường hợp, mọi người thậm chí phân tích mô hình so sánh đơn giản hơn , mà tôi thảo luận thêm một chút trong câu trả lời được liên kết.

Để trả lời câu hỏi ngầm về lý do tại sao họ làm điều đó: Tôi sẽ nói rằng mô hình này có khả năng dự đoán khá tốt đối với một số loại thuật toán tổ hợp nhất định, trong đó các số đều là "nhỏ" và, trên các máy thật, phù hợp với các thanh ghi.

Để trả lời theo dõi ngầm về các thuật toán số: Không, mô hình RAM cũ đơn giản không phải là tiêu chuẩn ở đây. Ngay cả việc loại bỏ Gaussian có thể yêu cầu một số chăm sóc. Thông thường, đối với các tính toán xếp hạng, Bổ đề Schwartz sẽ nhập (ví dụ: Phần 5 ở đây ). Một ví dụ kinh điển khác là phân tích Thuật toán Ellipsoid, đòi hỏi một số sự quan tâm để phân tích.

Và cuối cùng: Mọi người đã nghĩ về việc sắp xếp chuỗi trước đó , thậm chí gần đây.

Cập nhật: Vấn đề với câu hỏi này là "chúng tôi" và "giả định" không được chỉ định chính xác. Tôi muốn nói rằng những người làm việc trong mô hình RAM không thực hiện các thuật toán số hoặc lý thuyết phức tạp (trong đó việc xác định độ phức tạp của phép chia là kết quả nổi tiếng ).


Hmmmm, có vẻ như đó là một câu trả lời thú vị ....

Có một lý do nó không hoàn toàn trả lời câu hỏi?
Louis

7

Tôi không thể tìm thấy bất kỳ nghiên cứu nào về điều này, nhưng Kozen nói trong phần giới thiệu về "Thiết kế và phân tích thuật toán" rằng mô hình "phản ánh quan sát thử nghiệm chính xác hơn [so với mô hình chi phí log] cho dữ liệu vừa phải kích thước (vì phép nhân thực sự mất một đơn vị thời gian). " Ông cũng đưa ra một tài liệu tham khảo cho bài viết này như một ví dụ về cách mô hình có thể bị lạm dụng.O(1)O(1)

Đây là hoàn toàn không phải là một đánh giá legit (không kém phần quan vì nó Python), nhưng đây là một số con số chạy python -mtimeit "$a * $b"cho $atrong và . (Tôi đã dừng ở 66 vì đó là khi cú pháp Python ngừng chấp nhận số nguyên và tôi phải chuyển một chút mã đánh giá của mình, vì vậy tôi đã không làm: p)10{1,2,...,66}$b = 2*$a

Mỗi số là giá trị trung bình của 10.000.000 vòng lặp, trong đó phải mất thời gian tốt nhất là 3 lần chạy trong mỗi vòng lặp. Tôi đã làm các thanh lỗi hoặc một cái gì đó nhưng sẽ nỗ lực hơn. : p Trong mọi trường hợp, nó trông khá ổn định đối với tôi, thậm chí là tới - hơi ngạc nhiên, vì là 43, điều này củng cố sự nghi ngờ của tôi rằng có thể đánh giá này là đặc biệt không có thật và tôi nên làm điều đó trong C. log 10 ( s y s . M a x i n t )1050log10(sys.maxint)


Có thể là kinh nghiệm làm việc trong trường hợp đặc biệt này, bạn đã đúng (nhưng tôi không chắc chắn :), nhưng ví dụ hãy xem điều này , có vẻ như đó là nhưng không phải, đó cũng là một vấn đề thực tế. Ngoài ra, bạn có thấy bất kỳ bài báo nào nói sắp xếp là không? O ( n log n log m )O(n)O(nlognlogm)

7

Bạn nói đúng, nói chung chúng ta không thể cho rằng chúng là .O(1)

Nói một cách chính xác, nếu chúng ta muốn sắp xếp một mảng với N số bằng cách sử dụng so sánh và số lớn nhất là M, thì trong trường hợp xấu nhất, mỗi so sánh có thể liên quan đến so sánh ở cấp độ bit. Và nếu thuật toán của chúng ta so sánh , thì tổng độ phức tạp của nó sẽ là .O ( N log N ) O ( N log N log M )O(logM)O(NlogN)O(NlogNlogM)

Tuy nhiên, bạn sẽ chỉ nhận thấy sự khác biệt đối với các giá trị rất lớn , không thể được lưu trữ trong một thanh ghi duy nhất, như bạn có thể thấy từ thí nghiệm của Dougal.M


O ( log n ) mO(logm) không phải , ý tôi là số lớn nhất trong tập hợp là . O(logn)m

O(logN)

nnnn

Bạn nói đúng, tôi đã sửa câu trả lời của mình.
Erel Segal-Halevi

4

Tôi sẽ nói rằng chúng ta thường giả sử các phép toán số học O (1) bởi vì chúng ta thường làm mọi thứ trong bối cảnh số nguyên 32 bit hoặc số nguyên 64 bit hoặc số dấu phẩy động IEEE 754. O (1) có lẽ là một xấp xỉ khá tốt cho loại số học đó.

Nhưng nói chung, điều đó không đúng. Nói chung, bạn cần một thuật toán để thực hiện phép cộng, phép trừ, phép nhân và phép chia. Khả năng tính toán và logic của Boolos, Burgess và Jefferies là một cách để hiểu các bằng chứng về điều đó, về mặt một vài hệ thống chính thức khác nhau, Hàm đệ quy và Máy bàn tính, ít nhất là trong bản sao Phiên bản thứ 4 của tôi.

Bạn có thể xem các thuật ngữ lambda-tính để trừ và chia với Số nhà thờ để có một lời giải thích dễ hiểu về lý do tại sao hai thao tác đó không phải là O (1). Khó hơn một chút để xem phép cộng và nhân và lũy thừa, nhưng nó ở đó nếu bạn xem xét hình thức của Chữ số Giáo hội.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.