Có bất kỳ thuật toán O (1 / n) nào không?
Hoặc bất cứ điều gì khác ít hơn O (1)?
Có bất kỳ thuật toán O (1 / n) nào không?
Hoặc bất cứ điều gì khác ít hơn O (1)?
Câu trả lời:
Câu hỏi này không ngu ngốc như nó có vẻ. Ít nhất về mặt lý thuyết, một cái gì đó như O (1 / n ) là hoàn toàn hợp lý khi chúng ta lấy định nghĩa toán học của ký hiệu Big O :
Bây giờ bạn có thể dễ dàng thay thế g ( x ) cho 1 / x Rõ ràng là định nghĩa trên vẫn đúng với một số f .
Với mục đích ước tính tăng trưởng thời gian chạy không có triệu chứng, điều này ít khả thi hơn, một thuật toán có ý nghĩa không thể nhanh hơn khi đầu vào phát triển. Chắc chắn, bạn có thể xây dựng một thuật toán tùy ý để thực hiện điều này, ví dụ: thuật toán sau:
def get_faster(list):
how_long = (1 / len(list)) * 100000
sleep(how_long)
Rõ ràng, chức năng này dành ít thời gian hơn khi kích thước đầu vào tăng ít nhất cho đến khi giới hạn nào đó, được thực thi bởi phần cứng (độ chính xác của các số, thời gian tối thiểu sleep
có thể chờ, thời gian để xử lý đối số, v.v.): giới hạn này sẽ là một hằng số giới hạn dưới nên trong thực tế hàm trên vẫn có thời gian chạy O (1).
Nhưng có là trên thực tế thuật toán thực thế giới mà thời gian chạy có thể làm giảm (ít nhất là một phần) khi kích thước đầu vào tăng. Lưu ý rằng các thuật toán này sẽ không thể hiện hành vi thời gian chạy bên dưới O (1). Tuy nhiên, chúng là thú vị. Ví dụ: lấy thuật toán tìm kiếm văn bản rất đơn giản của Horspool . Ở đây, thời gian chạy dự kiến sẽ giảm khi chiều dài của mẫu tìm kiếm tăng (nhưng tăng độ dài của haystack sẽ một lần nữa tăng thời gian chạy).
Đúng.
Có chính xác một thuật toán với thời gian chạy O (1 / n), thuật toán "trống".
Đối với một thuật toán là O (1 / n) có nghĩa là nó thực thi bất đối xứng trong các bước ít hơn so với thuật toán bao gồm một lệnh đơn. Nếu nó thực hiện trong ít bước hơn một bước cho tất cả n> n0, thì nó phải bao gồm chính xác không có hướng dẫn nào cho n. Vì việc kiểm tra 'if n> n0' tốn ít nhất 1 lệnh, nên nó không bao gồm lệnh nào cho tất cả n.
Tổng hợp: Các thuật toán duy nhất là O (1 / n) là thuật toán có sản phẩm nào, bao gồm không có hướng dẫn.
sharptooth là chính xác, O (1) là hiệu suất tốt nhất có thể. Tuy nhiên, nó không ngụ ý một giải pháp nhanh, chỉ là một giải pháp thời gian cố định.
Một biến thể thú vị, và có lẽ những gì thực sự được đề xuất, là vấn đề trở nên dễ dàng hơn khi dân số tăng lên. Tôi có thể nghĩ về 1, mặc dù câu trả lời và câu trả lời:
Có ai trong một bộ có cùng ngày sinh không? Khi n vượt quá 365, trả về true. Mặc dù với ít hơn 365, đây là O (n ln n). Có lẽ không phải là một câu trả lời hay vì vấn đề không dần trở nên dễ dàng hơn mà chỉ trở thành O (1) cho n> 365.
Đó là không thể. Định nghĩa của Big-O không lớn hơn bất bình đẳng:
A(n) = O(B(n))
<=>
exists constants C and n0, C > 0, n0 > 0 such that
for all n > n0, A(n) <= C * B(n)
Vì vậy, B (n) trên thực tế là giá trị tối đa, do đó nếu nó giảm khi n tăng thì ước lượng sẽ không thay đổi.
Từ việc học trước đây của tôi về ký hiệu O lớn, ngay cả khi bạn cần 1 bước (chẳng hạn như kiểm tra một biến, thực hiện một bài tập), đó là O (1).
Lưu ý rằng O (1) giống với O (6), vì "hằng số" không thành vấn đề. Đó là lý do tại sao chúng ta nói O (n) giống với O (3n).
Vì vậy, nếu bạn cần 1 bước, đó là O (1) ... và vì chương trình của bạn ít nhất cần 1 bước, thuật toán tối thiểu có thể đi là O (1). Trừ khi chúng ta không làm điều đó, thì đó là O (0), tôi nghĩ sao? Nếu chúng ta làm bất cứ điều gì, thì đó là O (1), và đó là mức tối thiểu có thể đi.
(Nếu chúng ta chọn không làm điều đó, thì nó có thể trở thành một câu hỏi Zen hoặc Tao ... trong lĩnh vực lập trình, O (1) vẫn là mức tối thiểu).
Hoặc làm thế nào về điều này:
lập trình viên : ông chủ, tôi đã tìm ra cách để làm điều đó trong thời gian O (1)!
ông chủ : không cần làm điều đó, chúng tôi phá sản sáng nay.
lập trình viên : oh sau đó, nó trở thành O (0).
Không, điều này là không thể:
Khi n có xu hướng vô cùng trong 1 / n, cuối cùng chúng ta đạt được 1 / (inf), có hiệu quả là 0.
Do đó, lớp lớn của vấn đề sẽ là O (0) với n lớn, nhưng gần với thời gian không đổi với n thấp. Điều này là không hợp lý, vì điều duy nhất có thể được thực hiện trong thời gian nhanh hơn thời gian không đổi là:
void nothing() {};
Và thậm chí điều này là tranh cãi!
Ngay khi bạn thực thi một lệnh, bạn sẽ có ít nhất O (1), vì vậy không, chúng ta không thể có một lớp O (1 / n) lớn!
Điều gì về việc không chạy chức năng nào cả (NOOP)? hoặc sử dụng một giá trị cố định. Có tính không?
Tôi thường sử dụng O (1 / n) để mô tả các xác suất trở nên nhỏ hơn khi các đầu vào trở nên lớn hơn - ví dụ: xác suất mà một đồng xu công bằng xuất hiện trên các lần lật log2 (n) là O (1 / n).
O (1) đơn giản có nghĩa là "thời gian không đổi".
Khi bạn thêm một lối thoát sớm vào một vòng lặp [1], bạn (theo ký hiệu big-O) biến thuật toán O (1) thành O (n), nhưng làm cho nó nhanh hơn.
Thủ thuật nói chung là thuật toán thời gian không đổi là tốt nhất và tuyến tính tốt hơn theo cấp số nhân, nhưng với số lượng nhỏ n, thuật toán hàm mũ thực sự có thể nhanh hơn.
1: Giả sử độ dài danh sách tĩnh cho ví dụ này
Đối với bất kỳ ai đọc câu hỏi này và muốn hiểu cuộc trò chuyện nói về điều gì, điều này có thể giúp:
| |constant |logarithmic |linear| N-log-N |quadratic| cubic | exponential |
| n | O(1) | O(log n) | O(n) |O(n log n)| O(n^2) | O(n^3) | O(2^n) |
| 1 | 1 | 1 | 1| 1| 1| 1 | 2 |
| 2 | 1 | 1 | 2| 2| 4| 8 | 4 |
| 4 | 1 | 2 | 4| 8| 16| 64 | 16 |
| 8 | 1 | 3 | 8| 24| 64| 512 | 256 |
| 16 | 1 | 4 | 16| 64| 256| 4,096 | 65536 |
| 32 | 1 | 5 | 32| 160| 1,024| 32,768 | 4,294,967,296 |
| 64 | 1 | 6 | 64| 384| 4,069| 262,144 | 1.8 x 10^19 |
Tôi tin rằng các thuật toán lượng tử có thể thực hiện nhiều phép tính "cùng một lúc" thông qua sự chồng chất ...
Tôi nghi ngờ đây là một câu trả lời hữu ích.
Nhiều người đã có câu trả lời đúng (Không) Đây là một cách khác để chứng minh điều đó: Để có chức năng, bạn phải gọi chức năng đó và bạn phải trả lời câu trả lời. Điều này cần một lượng thời gian nhất định. NGAY CẢ NẾU phần còn lại của quá trình xử lý mất ít thời gian hơn cho các đầu vào lớn hơn, việc in ra câu trả lời (mà chúng ta có thể giả sử là một bit) mất ít nhất thời gian không đổi.
Nếu giải pháp tồn tại, nó có thể được chuẩn bị và truy cập trong thời gian không đổi = ngay lập tức. Chẳng hạn, sử dụng cấu trúc dữ liệu LIFO nếu bạn biết truy vấn sắp xếp là theo thứ tự ngược lại. Sau đó, dữ liệu đã được sắp xếp, với điều kiện là mô hình thích hợp (LIFO) đã được chọn.
Những vấn đề nào trở nên dễ dàng hơn khi dân số tăng lên? Một câu trả lời là một thứ giống như bittorrent trong đó tốc độ tải xuống là một hàm nghịch đảo của số lượng nút. Trái ngược với một chiếc xe hơi, nó làm chậm bạn tải nó nhiều hơn, một mạng chia sẻ tệp như bittorrent tăng tốc độ các nút được kết nối nhiều hơn.
Bạn không thể xuống dưới O (1), tuy nhiên O (k) trong đó k nhỏ hơn N là có thể. Chúng tôi gọi chúng là thuật toán thời gian tuyến tính . Trong một số vấn đề, thuật toán thời gian tuyến tính chỉ có thể đưa ra các giải pháp gần đúng cho một vấn đề cụ thể. Tuy nhiên, đôi khi, một giải pháp gần đúng là tốt, có thể là do tập dữ liệu quá lớn hoặc quá tốn kém để tính toán tất cả.
Cái này thì sao:
void FindRandomInList(list l)
{
while(1)
{
int rand = Random.next();
if (l.contains(rand))
return;
}
}
khi kích thước của danh sách tăng lên, thời gian chạy dự kiến của chương trình sẽ giảm.
constains
là O (1)
O (1 / n) không nhỏ hơn O (1), về cơ bản có nghĩa là bạn càng có nhiều dữ liệu, thuật toán càng nhanh. Giả sử bạn nhận được một mảng và luôn điền vào tối đa 10 100 phần tử nếu nó có ít hơn và không làm gì nếu có nhiều hơn. Tất nhiên, đây không phải là O (1 / n) mà là một cái gì đó như O (-n) :) Ký hiệu O-big quá tệ không cho phép các giá trị âm.
Như đã chỉ ra, ngoài ngoại lệ có thể có của hàm null, không thể có O(1/n)
chức năng nào , vì thời gian thực hiện sẽ phải tiếp cận 0.
Tất nhiên, có một số thuật toán, giống như được xác định bởi Konrad, có vẻ như chúng nên ít hơn O(1)
trong một số ý nghĩa.
def get_faster(list):
how_long = 1/len(list)
sleep(how_long)
Nếu bạn muốn điều tra các thuật toán này, bạn nên xác định phép đo tiệm cận của riêng bạn hoặc khái niệm thời gian của riêng bạn. Ví dụ, trong thuật toán trên, tôi có thể cho phép sử dụng một số thao tác "miễn phí" trong một khoảng thời gian đã đặt. Trong thuật toán trên, nếu tôi xác định t 'bằng cách loại trừ thời gian cho mọi thứ trừ giấc ngủ, thì t' = 1 / n, đó là O (1 / n). Có lẽ có những ví dụ tốt hơn, vì hành vi tiệm cận là tầm thường. Trên thực tế, tôi chắc chắn rằng ai đó ngoài kia có thể đưa ra các giác quan mang lại kết quả không hề nhỏ.
Hầu hết các câu trả lời còn lại diễn giải big-O chỉ dành riêng cho thời gian chạy của thuật toán. Nhưng vì câu hỏi không đề cập đến nó, tôi nghĩ rằng đáng để đề cập đến ứng dụng khác của big-O trong phân tích số, đó là về lỗi.
Nhiều thuật toán có thể là O (h ^ p) hoặc O (n ^ {- p}) tùy thuộc vào việc bạn đang nói về kích thước bước (h) hay số lần phân chia (n). Ví dụ: trong phương pháp Euler , bạn tìm ước tính y (h) cho rằng bạn biết y (0) và dy / dx (đạo hàm của y). Ước tính của bạn về y (h) chính xác hơn, h gần hơn đến 0. Vì vậy, để tìm y (x) cho một số x tùy ý, người ta lấy khoảng 0 đến x, tách nó ra cho đến khi n mảnh và chạy phương thức Euler tại mỗi điểm, để chuyển từ y (0) đến y (x / n) đến y (2x / n), v.v.
Vì vậy, phương pháp của Euler là thuật toán O (h) hoặc O (1 / n), trong đó h thường được hiểu là kích thước bước và n được hiểu là số lần bạn chia một khoảng.
Bạn cũng có thể có O (1 / h) trong các ứng dụng phân tích số thực, do các lỗi làm tròn dấu phẩy động . Bạn thực hiện khoảng thời gian của mình càng nhỏ, việc hủy bỏ xảy ra càng nhiều đối với việc thực hiện một số thuật toán nhất định, mất nhiều chữ số có nghĩa hơn và do đó càng có nhiều lỗi được truyền qua thuật toán.
Đối với phương pháp của Euler, nếu bạn đang sử dụng các dấu phẩy động, hãy sử dụng một bước đủ nhỏ và hủy bỏ và bạn đang thêm một số nhỏ vào một số lớn, giữ nguyên số lớn. Đối với các thuật toán tính đạo hàm thông qua phép trừ hai số khác từ một hàm được đánh giá ở hai vị trí rất gần nhau, xấp xỉ y '(x) với (y (x + h) - y (x) / h), trong các hàm trơn y (x + h) gần với y (x) dẫn đến hủy lớn và ước tính cho đạo hàm có số liệu ít quan trọng hơn. Điều này sẽ lần lượt lan truyền đến bất kỳ thuật toán nào bạn yêu cầu đạo hàm cho (ví dụ: một vấn đề giá trị biên).
OK, tôi đã suy nghĩ một chút về nó và có lẽ tồn tại một thuật toán có thể theo dạng chung này:
Bạn cần tính toán bài toán nhân viên bán hàng du lịch cho biểu đồ 1000 nút, tuy nhiên, bạn cũng được cung cấp một danh sách các nút mà bạn không thể truy cập. Khi danh sách các nút không mong muốn phát triển lớn hơn, vấn đề trở nên dễ giải quyết hơn.
Tôi thấy một thuật toán được O (1 / n) thừa nhận ở giới hạn trên:
Bạn có một loạt lớn các đầu vào đang thay đổi do một thứ gì đó bên ngoài thói quen (có thể chúng phản ánh phần cứng hoặc thậm chí có thể là một số lõi khác trong bộ xử lý làm việc đó.) Và bạn phải chọn một đầu vào ngẫu nhiên nhưng hợp lệ.
Bây giờ, nếu nó không thay đổi, bạn chỉ cần tạo một danh sách các mục, chọn ngẫu nhiên một mục và nhận O (1) thời gian. Tuy nhiên, bản chất động của dữ liệu ngăn chặn việc lập danh sách, bạn chỉ cần thăm dò ngẫu nhiên và kiểm tra tính hợp lệ của đầu dò. (Và lưu ý rằng vốn dĩ không có gì đảm bảo câu trả lời vẫn còn hiệu lực khi được trả về. Điều này vẫn có thể sử dụng - giả sử, AI cho một đơn vị trong trò chơi. Nó có thể bắn vào mục tiêu bị khuất tầm nhìn trong khi nó bóp cò.)
Điều này có hiệu suất trường hợp xấu nhất là vô hạn nhưng hiệu suất trường hợp trung bình giảm khi không gian dữ liệu lấp đầy.
Trong phân tích số, các thuật toán gần đúng nên có độ phức tạp tiệm cận phụ không đổi trong dung sai xấp xỉ.
class Function
{
public double[] ApproximateSolution(double tolerance)
{
// if this isn't sub-constant on the parameter, it's rather useless
}
}
Tôi đoán ít hơn O (1) là không thể. Bất kỳ thời gian nào của thuật toán được gọi là O (1). Nhưng đối với O (1 / n) thì làm thế nào về chức năng dưới đây. (Tôi biết có nhiều biến thể đã được trình bày trong giải pháp này, nhưng tôi đoán tất cả chúng đều có một số sai sót (không phải là chính, chúng giải thích khái niệm này tốt). Vì vậy, đây là một, chỉ để tranh luận:
def 1_by_n(n, C = 10): #n could be float. C could be any positive number
if n <= 0.0: #If input is actually 0, infinite loop.
while True:
sleep(1) #or pass
return #This line is not needed and is unreachable
delta = 0.0001
itr = delta
while delta < C/n:
itr += delta
Do đó, khi n tăng chức năng sẽ mất ít thời gian hơn. Ngoài ra, đảm bảo rằng nếu đầu vào thực sự là 0, thì hàm sẽ mất mãi mãi để trả về.
Người ta có thể lập luận rằng nó sẽ bị giới hạn bởi độ chính xác của máy. do đó, eit chân có giới hạn trên là O (1). Nhưng chúng ta cũng có thể bỏ qua điều đó bằng cách lấy đầu vào của n và C trong chuỗi. Và bổ sung và so sánh được thực hiện trên chuỗi. Ý tưởng là, với điều này, chúng ta có thể giảm n nhỏ tùy ý. Do đó, giới hạn trên của hàm không bị giới hạn, ngay cả khi chúng ta bỏ qua n = 0.
Tôi cũng tin rằng chúng ta không thể chỉ nói rằng thời gian chạy là O (1 / n). Nhưng chúng ta nên nói một cái gì đó như O (1 + 1 / n)
Có thể xây dựng một thuật toán là O (1 / n). Một ví dụ sẽ là một vòng lặp lặp lại một số bội số của f (n) trong đó f (n) là một số hàm có giá trị được đảm bảo lớn hơn n và giới hạn của f (n) -n khi n tiến đến vô cùng là số không. Việc tính toán f (n) cũng cần phải là hằng số cho tất cả n. Tôi không biết f (n) sẽ trông như thế nào hoặc thuật toán như vậy sẽ có ứng dụng gì, theo tôi thì chức năng đó có thể tồn tại nhưng thuật toán kết quả sẽ không có mục đích nào khác ngoài việc chứng minh khả năng của thuật toán với O (1 / n).
Tôi không biết về các thuật toán nhưng độ phức tạp nhỏ hơn O (1) xuất hiện trong các thuật toán ngẫu nhiên. Trên thực tế, o (1) (ít o) nhỏ hơn O (1). Loại phức tạp này thường xuất hiện trong các thuật toán ngẫu nhiên. Ví dụ, như bạn đã nói, khi xác suất của một số sự kiện là thứ tự 1 / n, chúng biểu thị nó bằng o (1). Hoặc khi họ muốn nói rằng một cái gì đó xảy ra với xác suất cao (ví dụ 1 - 1 / n) thì họ biểu thị nó bằng 1 - o (1).
Nếu câu trả lời là như nhau bất kể dữ liệu đầu vào thì bạn có thuật toán O (0).
hay nói cách khác - câu trả lời được biết trước khi dữ liệu đầu vào được gửi - chức năng có thể được tối ưu hóa - vì vậy O (0)
Ký hiệu Big-O đại diện cho trường hợp xấu nhất đối với thuật toán không giống với thời gian chạy thông thường của nó. Thật đơn giản để chứng minh rằng thuật toán O (1 / n) là thuật toán O (1). Theo định nghĩa,
O (1 / n) -> T (n) <= 1 / n, với mọi n> = C> 0
O (1 / n) -> T (n) <= 1 / C, Vì 1 / n <= 1 / C với mọi n> = C
O (1 / n) -> O (1), vì ký hiệu Big-O bỏ qua các hằng số (tức là giá trị của C không quan trọng)
hashtable-contains
thuật toán có thể được ký hiệu là O (1) - và trường hợp xấu nhất có thể được đưa ra rất chính xác là Theta (n)! Omega và Theta đơn giản có thể được sử dụng để biểu thị các giới hạn khác nhưng nói lại một lần nữa : chúng không liên quan gì đến trường hợp trung bình hoặc tốt nhất.
Không có gì nhỏ hơn O (1) Ký hiệu Big-O ngụ ý thứ tự phức tạp lớn nhất cho một thuật toán
Nếu một thuật toán có thời gian chạy là n ^ 3 + n ^ 2 + n + 5 thì đó là O (n ^ 3) Các quyền hạn thấp hơn không quan trọng ở đây vì vì n -> Inf, n ^ 2 sẽ không liên quan so với 3 ^
Tương tự như n -> Inf, O (1 / n) sẽ không liên quan so với O (1) do đó 3 + O (1 / n) sẽ giống như O (1) do đó làm cho O (1) tính toán nhỏ nhất có thể phức tạp
inline void O0Algorithm() {}
Đây là một thuật toán O (1 / n) đơn giản. Và nó thậm chí còn làm một cái gì đó thú vị!
function foo(list input) {
int m;
double output;
m = (1/ input.size) * max_value;
output = 0;
for (int i = 0; i < m; i++)
output+= random(0,1);
return output;
}
O (1 / n) là có thể vì nó mô tả cách đầu ra của hàm thay đổi khi tăng kích thước đầu vào. Nếu chúng ta đang sử dụng hàm 1 / n để mô tả số lượng lệnh mà hàm thực thi thì không có yêu cầu nào hàm đó thực hiện các lệnh không cho bất kỳ kích thước đầu vào nào. Thay vào đó, với mỗi kích thước đầu vào, n trên một số ngưỡng, số lượng lệnh được yêu cầu được giới hạn ở trên bởi một hằng số dương nhân với 1 / n. Vì không có số thực tế trong đó 1 / n là 0 và hằng số là dương, nên không có lý do gì mà hàm sẽ bị hạn chế thực hiện 0 hoặc ít hơn các hướng dẫn.