Thuật toán O (nlogn) - Tìm ba thuật toán cách đều nhau trong chuỗi nhị phân


173

Tôi đã có câu hỏi này trong bài kiểm tra Thuật toán ngày hôm qua và tôi không thể tìm ra câu trả lời. Nó đang khiến tôi phát điên hoàn toàn, vì nó đáng giá khoảng 40 điểm. Tôi nghĩ rằng hầu hết các lớp học đã không giải quyết nó một cách chính xác, bởi vì tôi đã không đưa ra một giải pháp trong 24 giờ qua.

Cho một chuỗi nhị phân tùy ý có độ dài n, tìm ba chuỗi cách đều nhau trong chuỗi nếu chúng tồn tại. Viết một thuật toán giải quyết điều này trong thời gian O (n * log (n)).

Vì vậy, các chuỗi như thế này có ba chuỗi "cách đều nhau": 11100000, 0100100100

chỉnh sửa: Nó là một số ngẫu nhiên, vì vậy nó sẽ có thể làm việc cho bất kỳ số nào. Các ví dụ tôi đưa ra là để minh họa cho thuộc tính "cách đều nhau". Vậy 1001011 là một con số hợp lệ. Với 1, 4 và 7 là những khoảng cách đều nhau.


4
Là sau đây có thể: 10011010000? Nó có ba số 1 (thứ nhất, thứ hai, thứ tư) cách đều nhau, nhưng cũng có thêm 1 số.
Anna

5
Robert, bạn cần phải nhờ giáo sư của bạn cung cấp cho bạn câu trả lời cho điều này và đăng nó ở đây. Vấn đề này đang đẩy tôi lên tường. Tôi có thể tìm ra cách để làm điều đó trong n ^ 2 nhưng không phải n * log (n).
James McMahon

3
Hmm, tôi đã dành một khoảng thời gian dài để cố gắng tìm ra điều này, nhưng vẫn chưa tìm thấy một câu trả lời hay. Có lẽ bạn đã hiểu nhầm câu hỏi? Ví dụ: nếu câu hỏi yêu cầu, hãy tìm một thuật toán chạy trong O (n log n) xác định vị trí của một chuỗi khoảng cách đều nhau k, trong một chuỗi lớn hơn nhiều, điều này có thể dễ dàng thực hiện bằng cách sử dụng biến đổi Fourier nhanh.
ldog

2
Nếu prof của bạn đưa ra một giải pháp xin vui lòng gửi nó như là một câu trả lời.
ldog

5
Xem xét thực tế rằng Klaus Roth đã nhận được Huy chương Trường 1958 cho (trong số những thứ khác) chứng minh rằng với mỗi mật độ d> 0, có một số tự nhiên N sao cho mỗi tập con của {1, ..., N} với ít nhất d * N phần tử chứa một tiến trình số học có độ dài 3, tôi không ngạc nhiên rằng cho đến nay vẫn chưa có ai tìm thấy một thuật toán thuyết phục cho vấn đề này. Xem thêm en.wikipedia.org/wiki/Szemer%C3%A9di%27s_theorem
jp

Câu trả lời:


128

Cuối cùng! Theo dõi các khách hàng tiềm năng trong câu trả lời của sdcvvc , chúng tôi có nó: thuật toán O (n log n) cho vấn đề này! Nó cũng đơn giản, sau khi bạn hiểu nó. Những người đoán FFT đã đúng.

Vấn đề: chúng tôi được cung cấp một chuỗi nhị phân Scó độ dài n và chúng tôi muốn tìm ba số 1 cách đều nhau trong đó. Ví dụ, Scó thể 110110010, trong đó n = 9. Nó có khoảng cách đều nhau ở các vị trí 2, 5 và 8.

  1. Quét Stừ trái sang phải và tạo danh sách Lcác vị trí của 1. Đối với phần S=110110010trên, chúng tôi có danh sách L = [1, 2, 4, 5, 8]. Bước này là O (n). Vấn đề bây giờ là tìm một cấp số cộng có độ dài 3 năm L, tức là để tìm riêng biệt a, b, c trong Lđó có ba = cb , hoặc tương đương a + c = 2b . Đối với ví dụ trên, chúng tôi muốn tìm tiến trình (2, 5, 8).

  2. Tạo một đa thức p với các điều khoản x k cho mỗi k trong L. Ví dụ trên, chúng ta tạo đa thức p (x) = (x + x 2 + x 4 + x 5 + x 8 ) . Bước này là O (n).

  3. Tìm đa thức q= p 2 , sử dụng Biến đổi Fourier nhanh . Ví dụ trên, chúng ta nhận được đa thức q (x) = x 16 + 2x 13 + 2x 12 + 3x 10 + 4x 9 + x 8 + 2x 7 + 4x 6 + 2x 5 + x 4 + 2x 3 + x 2 . Bước này là O (n log n).

  4. Bỏ qua tất cả các điều khoản ngoại trừ những điều khoản tương ứng với x 2k cho một số k trong L. Đối với ví dụ trên, chúng tôi nhận được các điều khoản x 16 , 3x 10 , x 8 , x 4 , x 2 . Bước này là O (n), nếu bạn chọn làm tất cả.

Dưới đây là những điểm quan trọng: hệ số của bất kỳ x 2b cho b trong Lchính xác số lượng các cặp (a, c) trong Lđó có a + c = 2b . [CLRS, vd 30.1-7] Một cặp như vậy luôn luôn là (b, b) (vì vậy hệ số ít nhất là 1), nhưng nếu tồn tại bất kỳ cặp nào khác (a, c) , thì hệ số này ít nhất là 3, từ (a, c )(c, a) . Đối với ví dụ trên, chúng ta có hệ số x 10 là 3 chính xác vì AP (2,5,8). (Các hệ số x 2bsẽ luôn là số lẻ, vì những lý do trên. Và tất cả các hệ số khác trong q sẽ luôn là số chẵn.)

Vì vậy, thuật toán là xem xét các hệ số của các số hạng x 2b này và xem liệu bất kỳ trong số chúng có lớn hơn 1. Nếu không có, thì không có khoảng cách 1 đều nhau. Nếu có một b trong Lmà hệ số của x 2b là lớn hơn 1, sau đó chúng ta biết rằng có một số cặp (a, c) - khác hơn (b, b) - mà a + c = 2b . Để tìm cặp thực tế, chúng ta chỉ cần thử từng a trong L( c tương ứng sẽ là 2b-a ) và xem nếu có 1 ở vị trí 2b-a in S. Bước này là O (n).

Đó là tất cả mọi người.


Người ta có thể hỏi: chúng ta có cần sử dụng FFT không? Nhiều câu trả lời, chẳng hạn như beta , flybywirersp , đề xuất rằng phương pháp kiểm tra từng cặp 1 và xem liệu có 1 ở vị trí "thứ ba" hay không, có thể hoạt động ở O (n log n), dựa trên trực giác rằng nếu có quá nhiều số 1, chúng ta sẽ dễ dàng tìm thấy một bộ ba và nếu có quá ít số 1, việc kiểm tra tất cả các cặp mất ít thời gian. Thật không may, trong khi trực giác này là chính xác và cách tiếp cận đơn giản tốt hơn O (n 2 ), nó không tốt hơn đáng kể. Như trong câu trả lời của sdcvvc , chúng ta có thể lấy "tập hợp giống Cantor" của các chuỗi có độ dài n = 3 k, với 1s tại các vị trí có đại diện ternary chỉ có 0s và 2s (không có 1) trong đó. Một chuỗi như vậy có 2 k = n (log 2) / (log 3) ≈ n 0,63 trong đó và không có khoảng cách đều nhau 1s, vì vậy việc kiểm tra tất cả các cặp sẽ theo thứ tự bình phương của số 1s trong đó: đó là 4 k ≈ n 1,26 mà không may là không có triệu chứng lớn hơn nhiều so với (n log n). Trong thực tế, trường hợp xấu nhất thậm chí còn tồi tệ hơn: Leo Moser vào năm 1953 đã xây dựng (một cách hiệu quả) các chuỗi như vậy có n 1-c / (log n) 1 trong chúng nhưng không có khoảng cách 1 đều nhau, có nghĩa là trên các chuỗi đó, đơn giản cách tiếp cận sẽ mất (n 2-2c / √ (log n) )- chỉ là một nhỏ chút tốt hơn so với Θ (n 2 ) , đáng ngạc nhiên!


Về số lượng tối đa 1s trong một chuỗi có độ dài n không có 3 cái đều nhau (mà chúng ta đã thấy ở trên là ít nhất n 0,63 từ dễ Cantor giống như xây dựng, và ít nhất n 1-c / √ (log n) với Cấu trúc của Moser) - đây là OEIS A003002 . Nó cũng có thể được tính trực tiếp từ OEIS A065825 vì k sao cho A065825 (k) ≤ n <A065825 (k + 1). Tôi đã viết một chương trình để tìm những thứ này, và hóa ra thuật toán tham lam không đưa ra chuỗi dài nhất như vậy. Ví dụ: với n = 9, chúng ta có thể nhận được 5 1 giây (110100011) nhưng sự tham lam chỉ mang lại 4 (110110000), cho n= 26 chúng ta có thể nhận được 11 1 giây (11001010001000010110001101) nhưng tham lam chỉ cho 8 (11011000011011000000000000), và với n = 74, chúng ta có thể nhận được 22 1s Tuy nhiên, họ đồng ý ở một vài nơi cho đến 50 (ví dụ: tất cả từ 38 đến 50). Như các tài liệu tham khảo của OEIS nói, có vẻ như Jaroslaw Classicalblewski quan tâm đến câu hỏi này và anh ta duy trì một trang web về các bộ không trung bình này . Con số chính xác chỉ được biết đến tối đa 194.


27
Rất đẹp. Ấn tượng. Có vẻ hơi nhiều để mong đợi ai đó sẽ đưa ra điều này trong một bài kiểm tra.
hughdbrown

4
Chà, Bước 1, chuyển vấn đề sang tìm AP, thật đơn giản. Bước 3, đa thức đó có thể được nhân lên trong thời gian O (n log n), chỉ là một thực tế. Bí quyết thực sự, và điều làm cho vấn đề trở nên khó khăn, là ý tưởng của năm 11011 là đa thức với các hệ số [1,1,0,1,1], v.v ... Đây là một ý tưởng thông minh và thường hữu ích, đi tất cả đường về Euler. [Xem cuốn sách "tạo chức năng" tuyệt vời của Wilf để biết một giải trình hiện đại: math.upenn.edu/~wilf/DownldGF.html ] Vì vậy, nó phụ thuộc vào việc các sinh viên có được tiếp xúc với việc tạo các hàm trong bộ nhớ gần đây hay không. :-)
ShreevatsaR

2
Xin lỗi có tính toán của tôi hoàn toàn sai. Nó phải là 110110010 ^ 2 = 12124214302200100. Nhưng ý tưởng đứng. Chỉ cần lưu ý vị trí của 3.
Guillermo Phillips

11
Rất ấn tượng. Thật tuyệt khi thấy chủ đề / câu hỏi này kết hợp với nhau và tìm ra giải pháp. Tôi đã bắt đầu nghĩ rằng nó không thể. Ngoài ra, giáo sư này là ác.
KingNestor

1
@RexE: Nếu p có độ n-1 (có n số hạng), q = p ^ 2 là độ 2n-2 (có nhiều nhất 2n-1 số hạng). Làm thế nào bạn có được n ^ 2? (Ngoài ra, nhân hai đa thức bậc n trong thời gian O (n log n) bằng FFT là một thao tác khá chuẩn; vui lòng nhấp vào liên kết trong câu trả lời hoặc xem bài viết Wikipedia .)
ShreevatsaR

35

Vấn đề của bạn được gọi là AVERAGE trong bài báo này (1999):

Một vấn đề khó 3SUM nếu có giảm bậc hai phụ từ bài toán 3SUM: Cho một tập hợp A gồm n số nguyên, có các phần tử a, b, c trong A sao cho a + b + c = 0? Người ta không biết liệu AVERAGE có cứng 3SUM hay không. Tuy nhiên, có một giảm thời gian tuyến tính đơn giản từ AVERAGE xuống 3SUM, mà chúng tôi bỏ qua mô tả.

Wikipedia :

Khi các số nguyên nằm trong phạm vi [−u ... u], 3SUM có thể được giải quyết trong thời gian O (n + u lg u) bằng cách biểu diễn S dưới dạng một vectơ bit và thực hiện phép tích chập bằng FFT.

Điều này là đủ để giải quyết vấn đề của bạn :).

Điều rất quan trọng là O (n log n) rất phức tạp về số lượng số 0 và số 0, chứ không phải số lượng (có thể được đưa ra dưới dạng một mảng, như [1,5,9,15]). Kiểm tra xem một tập hợp có tiến trình số học hay không, các số hạng của 1, có khó không, và theo bài báo đó vào năm 1999, không có thuật toán nào nhanh hơn O (n 2 ), và được phỏng đoán rằng nó không tồn tại. Mọi người không tính đến điều này đều cố gắng giải quyết vấn đề mở.

Thông tin thú vị khác, chủ yếu là không phù hợp:

Chặn dưới:

Một giới hạn dưới dễ dàng là tập hợp giống Cantor (số 1..3 ^ n-1 không chứa 1 trong phần mở rộng phía sau của chúng) - mật độ của nó là n ^ (log_3 2) (khoảng 0,631). Vì vậy, bất kỳ kiểm tra nào nếu tập hợp không quá lớn, và sau đó kiểm tra tất cả các cặp là không đủ để lấy O (n log n). Bạn phải điều tra trình tự thông minh hơn. Giới hạn dưới tốt hơn được trích dẫn ở đây - đó là 1-c / (log (n)) ^ (1/2) . Điều này có nghĩa là bộ Cantor không tối ưu.

Giới hạn trên - thuật toán cũ của tôi:

Được biết, đối với n lớn, một tập hợp con {1,2, ..., n} không chứa tiến trình số học có nhiều nhất n / (log n) ^ (1/20). Bài viết về bộ ba trong tiến trình số học chứng minh nhiều hơn: tập hợp không thể chứa nhiều hơn n * 2 28 * (log log n / log n) 1/2 phần tử. Vì vậy, bạn có thể kiểm tra xem ràng buộc đó có đạt được không và nếu không, hãy kiểm tra các cặp một cách ngây thơ. Đây là thuật toán O (n 2 * log log n / log n), nhanh hơn O (n 2 ). Thật không may "Trên bộ ba ..." là trên Springer - nhưng trang đầu tiên có sẵn và giải trình của Ben Green có sẵn ở đây , trang 28, định lý 24.

Nhân tiện, các bài báo là từ năm 1999 - cùng năm với bài đầu tiên tôi đã đề cập, vì vậy đó có lẽ là lý do tại sao bài báo đầu tiên không đề cập đến kết quả đó.


2
Câu trả lời tuyệt vời, đầu tiên nói bất cứ điều gì dứt khoát về vấn đề này. Vì vậy, tập hợp giống Cantor có n ^ 0,63 1s, có nghĩa là thuật toán "kiểm tra tất cả các cặp 1s" ít nhất là n ^ 1,26 (log n log n) trong trường hợp xấu nhất. Thấp hơn bị ràng buộc được trích dẫn trong bài báo Szemeredi của (BTW giấy Moser ông trích dẫn có sẵn ở đây: books.google.com/books?id=Cvtwu5vVZF4C&pg=PA245 ) dường như thực sự bao hàm n ^ (2-o (1)), nhưng chúng ta phải hãy cẩn thận một chút vì chúng ta có các số được rút ra từ {1, ..., n} nhưng đây là tổng của các số trong dãy là n.
ShreevatsaR

Er, chính xác thì chuỗi nhị phân "giống như Cantor" chứa n ^ (log_3 2) 1s trong đó và không có ba số 1 cách đều nhau?
ShreevatsaR

Ví dụ: 101000101000000000101000101. Chiều dài của nó là 3 ^ n và có 2 ^ n cái (mật độ n ^ 0,63). Nếu bạn ghi lại các vị trí của 1 trong nhị phân, nó sẽ là {0,2,20,22,200,202,220,222}. Một cách khác có thể nghĩ về nó là lấy một chuỗi các cái và liên tục loại bỏ những cái "giữa" như trong cấu trúc bộ Cantor bình thường: 111111111 -> 111000111 -> 101000101. Lý do tại sao nó không chứa tiến trình số học là: if x , y, z tạo thành một, sau đó y = (x + z) / 2 và x và z khác nhau ở một số vị trí mở rộng. Lấy cái quan trọng nhất Nói x có 0 và z có 2. Khi đó y phải có 1 ở đó. mâu thuẫn.
sdcvvc

3
Một lần nữa, nghiên cứu tuyệt vời! Tôi đã theo dõi bài báo 3SUM năm 2008 và nó đề cập đến Bài tập CLRS. 30.1-7, sau khi nhìn vào đó tôi đã có câu trả lời - thuật toán O (n log n) thực sự khá đơn giản! (Chỉ bình phương một đa thức / hàm tạo.) Tôi đã đăng câu trả lời bên dưới. (Bây giờ tự đá mình vì đã không nghĩ về nó sớm hơn ... các giải pháp đơn giản luôn gợi ra phản ứng đó: p)
ShreevatsaR

Vì vậy, câu trả lời cho câu hỏi thi của anh ấy là đại loại như: "Vấn đề này có thể giải quyết được với vấn đề khó 3-SUM và khó 3-SUM không có giải pháp bậc hai, vì vậy vấn đề này không thể giải quyết được trong O (n logn). " Đúng?
hughdbrown

8

Đây không phải là một giải pháp, mà là một dòng suy nghĩ tương tự như những gì Olexiy đang nghĩ

Tôi đã chơi xung quanh với việc tạo các chuỗi với số lượng tối đa, và chúng đều khá thú vị, tôi đã có tới 125 chữ số và đây là 3 số đầu tiên được tìm thấy bằng cách cố gắng chèn càng nhiều bit '1' càng tốt:

  • 110110000110110000000000000011011000011011000000000000000000000000000000000000000000000110110000110110000000000000011011000011011
  • 101101000101101000000000000101101000101101000000000000000000000000000000000000000000000101101000101101000000000000101101000101101
  • 100110010100110010000000000100110010100110010000000000000000000000000000000000000000010011001010011001000000000010011001010011001

Lưu ý rằng tất cả chúng đều là fractals (không quá ngạc nhiên khi đưa ra các ràng buộc). Có thể có một cái gì đó trong suy nghĩ ngược, có lẽ nếu chuỗi không phải là một fractal với một đặc tính, thì nó phải có một mẫu lặp lại?

Nhờ beta cho thuật ngữ tốt hơn để mô tả những con số này.

Cập nhật: Than ôi có vẻ như mô hình bị hỏng khi bắt đầu với một chuỗi ban đầu đủ lớn, chẳng hạn như: 10000000000001:

100000000000011
10000000000001101
100000000000011011
10000000000001101100001
100000000000011011000011
10000000000001101100001101
100000000000011011000011010000000001
100000000000011011000011010000000001001
1000000000000110110000110100000000010011
1000000000000110110000110100000000010011001
10000000000001101100001101000000000100110010000000001
10000000000001101100001101000000000100110010000000001000001
1000000000000110110000110100000000010011001000000000100000100000000000001
10000000000001101100001101000000000100110010000000001000001000000000000011
1000000000000110110000110100000000010011001000000000100000100000000000001101
100000000000011011000011010000000001001100100000000010000010000000000000110100001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001
1000000000000110110000110100000000010011001000000000100000100000000000001101000010010000010000001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001
1000000000000110110000110100000000010011001000000000100000100000000000001101000010010000010000001100010000000010000000000000000000000000000000000000000100000010000000000000011
1000000000000110110000110100000000010011001000000000100000100000000000001101000010010000010000001100010000000010000000000000000000000000000000000000000100000010000000000000011000000001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001000001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001000001001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001000010000010010001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001000010000010010001001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001000010000010010001001000001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001000001001000100100000100000000000001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001000010000010010001001000001000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001000001001000100100000100000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001000010000010010001001000001000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000011
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001000010000010010001001000001000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000011000001
1000000000000110110000110100000000010011001000000000100000100000000000001101000010010000010000001100010000000010000000000000000000000000000000000000000100000010000000000000011000000001100100000000100100000000000010000000010000100000100100010010000010000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000110000010000000000000000000001
1000000000000110110000110100000000010011001000000000100000100000000000001101000010010000010000001100010000000010000000000000000000000000000000000000000100000010000000000000011000000001100100000000100100000000000010000000010000100000100100010010000010000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000110000010000000000000000000001001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001000001001000100100000100000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000001100000100000000000000000000010010000000000000000000000000000000000001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001000010000010010001001000001000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000011000001000000000000000000000100100000000000000000000000000000000000011
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001000010000010010001001000001000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000011000001000000000000000000000100100000000000000000000000000000000000011001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001000001001000100100000100000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000001100000100000000000000000000010010000000000000000000000000000000000001100100000000000000000000001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001000001001000100100000100000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000001100000100000000000000000000010010000000000000000000000000000000000001100100000000000000000000001001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001000001001000100100000100000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000001100000100000000000000000000010010000000000000000000000000000000000001100100000000000000000000001001000001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001000010000010010001001000001000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000011000001000000000000000000000100100000000000000000000000000000000000011001000000000000000000000010010000010000001
1000000000000110110000110100000000010011001000000000100000100000000000001101000010010000010000001100010000000010000000000000000000000000000000000000000100000010000000000000011000000001100100000000100100000000000010000000010000100000100100010010000010000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000110000010000000000000000000001001000000000000000000000000000000000000110010000000000000000000000100100000100000011
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001000001001000100100000100000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000001100000100000000000000000000010010000000000000000000000000000000000001100100000000000000000000001001000001000000110000000000001

2
Thánh * @ !!, đây là những CHI TIẾT! Nếu điều này giữ vững, nó sẽ đặt giới hạn trên của số 1 và nó nhỏ hơn O (n).
Beta

fractals, đó là một thuật ngữ tốt hơn nhiều để mô tả chúng. Cảm ơn
z -

Thật thú vị, những mẫu này gần giống với bộ ternary của Cantor ( en.wikipedia.org/wiki/Cantor_set ). Nếu điều này là như vậy, thì tỷ lệ của những người phải có xu hướng bằng không ...
flybywire

Rõ ràng là các chuỗi có số lượng tối đa 1 giây không có bộ ba có liên quan trực tiếp đến thời gian chạy trường hợp xấu nhất của thuật toán không? Có thể hình dung rằng bạn có thể có các chuỗi có rất nhiều 1 nhưng trong đó bạn chỉ tìm thấy các bộ ba rất muộn, vì các số đó nằm ở vị trí được kiểm tra muộn bởi thuật toán của bạn.
ShreevatsaR

3
Phân tích của tôi về số lượng các chuỗi trong chuỗi so với kích thước tổng thể của chúng dường như chỉ ra rằng có một mối quan hệ tuyến tính giữa số lượng và kích thước của chuỗi, khiến tôi tin rằng không có giới hạn trên hạnh phúc nào cho phép chúng tôi nói rằng số lượng các chuỗi trong một chuỗi sẽ nhiều nhất là log (n) cho một chuỗi đã cho. Vì vậy, các giải pháp chỉ xử lý các vị trí của các vị trí chứ không phải toàn bộ chuỗi cũng sẽ là O (n ^ 2). Hay chính xác hơn là O (n + m ^ 2), trong đó m là số lượng trong chuỗi và n là kích thước của chuỗi và m là big-theta (n).
Welbog

6

Tôi nghi ngờ rằng một cách tiếp cận đơn giản trông giống O (n ^ 2) sẽ thực sự mang lại điều gì đó tốt hơn, như O (n ln (n)). Các trình tự mất nhiều thời gian nhất để kiểm tra (đối với bất kỳ n đã cho nào) là các chuỗi không chứa bộ ba và điều đó đặt ra các hạn chế nghiêm trọng đối với số 1 có thể có trong chuỗi.

Tôi đã đưa ra một số tranh luận vẫy tay, nhưng tôi không thể tìm thấy một bằng chứng gọn gàng. Tôi sẽ đâm đầu vào bóng tối: câu trả lời là một ý tưởng rất thông minh mà giáo sư đã biết từ lâu đến nỗi nó dường như rõ ràng, nhưng nó quá khó đối với các sinh viên. (Hoặc là hoặc bạn đã ngủ qua bài giảng bao trùm nó.)


2
lol, không tôi đã không ngủ qua bất kỳ bài giảng. Tôi đã nói chuyện với một vài sinh viên khác, và không ai có ý tưởng rõ ràng về cách giải quyết nó. Hầu hết đã viết một số BS về sự chia rẽ và chinh phục trong một lời cầu xin để có được một phần tín dụng.
Robert Parker

3

Sửa đổi: 2009-10-17 23:00

Tôi đã chạy nó với số lượng lớn (như, chuỗi 20 triệu) và bây giờ tôi tin rằng thuật toán này không phải là O (n logn). Mặc dù vậy, đó là một triển khai đủ tuyệt vời và chứa một số tối ưu hóa giúp nó chạy rất nhanh. Nó đánh giá tất cả sự sắp xếp của các chuỗi nhị phân từ 24 chữ số trở xuống trong vòng dưới 25 giây.

Tôi đã cập nhật mã để bao gồm các 0 <= L < M < U <= X-1quan sát từ đầu ngày hôm nay.


Nguyên

Đây là, trong khái niệm, tương tự như một câu hỏi khác mà tôi đã trả lời . Mã đó cũng đã xem xét ba giá trị trong một chuỗi và xác định xem một bộ ba có thỏa mãn một điều kiện hay không. Đây là mã C # được điều chỉnh từ đó:

using System;
using System.Collections.Generic;

namespace StackOverflow1560523
{
    class Program
    {
        public struct Pair<T>
        {
            public T Low, High;
        }
        static bool FindCandidate(int candidate, 
            List<int> arr, 
            List<int> pool, 
            Pair<int> pair, 
            ref int iterations)
        {
            int lower = pair.Low, upper = pair.High;
            while ((lower >= 0) && (upper < pool.Count))
            {
                int lowRange = candidate - arr[pool[lower]];
                int highRange = arr[pool[upper]] - candidate;
                iterations++;
                if (lowRange < highRange)
                    lower -= 1;
                else if (lowRange > highRange)
                    upper += 1;
                else
                    return true;
            }
            return false;
        }
        static List<int> BuildOnesArray(string s)
        {
            List<int> arr = new List<int>();
            for (int i = 0; i < s.Length; i++)
                if (s[i] == '1')
                    arr.Add(i);
            return arr;
        }
        static void BuildIndexes(List<int> arr, 
            ref List<int> even, ref List<int> odd, 
            ref List<Pair<int>> evenIndex, ref List<Pair<int>> oddIndex)
        {
            for (int i = 0; i < arr.Count; i++)
            {
                bool isEven = (arr[i] & 1) == 0;
                if (isEven)
                {
                    evenIndex.Add(new Pair<int> {Low=even.Count-1, High=even.Count+1});
                    oddIndex.Add(new Pair<int> {Low=odd.Count-1, High=odd.Count});
                    even.Add(i);
                }
                else
                {
                    oddIndex.Add(new Pair<int> {Low=odd.Count-1, High=odd.Count+1});
                    evenIndex.Add(new Pair<int> {Low=even.Count-1, High=even.Count});
                    odd.Add(i);
                }
            }
        }

        static int FindSpacedOnes(string s)
        {
            // List of indexes of 1s in the string
            List<int> arr = BuildOnesArray(s);
            //if (s.Length < 3)
            //    return 0;

            //  List of indexes to odd indexes in arr
            List<int> odd = new List<int>(), even = new List<int>();

            //  evenIndex has indexes into arr to bracket even numbers
            //  oddIndex has indexes into arr to bracket odd numbers
            List<Pair<int>> evenIndex = new List<Pair<int>>(), 
                oddIndex = new List<Pair<int>>(); 
            BuildIndexes(arr, 
                ref even, ref odd, 
                ref evenIndex, ref oddIndex);

            int iterations = 0;
            for (int i = 1; i < arr.Count-1; i++)
            {
                int target = arr[i];
                bool found = FindCandidate(target, arr, odd, oddIndex[i], ref iterations) || 
                    FindCandidate(target, arr, even, evenIndex[i], ref iterations);
                if (found)
                    return iterations;
            }
            return iterations;
        }
        static IEnumerable<string> PowerSet(int n)
        {
            for (long i = (1L << (n-1)); i < (1L << n); i++)
            {
                yield return Convert.ToString(i, 2).PadLeft(n, '0');
            }
        }
        static void Main(string[] args)
        {
            for (int i = 5; i < 64; i++)
            {
                int c = 0;
                string hardest_string = "";
                foreach (string s in PowerSet(i))
                {
                    int cost = find_spaced_ones(s);
                    if (cost > c)
                    {
                        hardest_string = s;
                        c = cost;
                        Console.Write("{0} {1} {2}\r", i, c, hardest_string);
                    }
                }
                Console.WriteLine("{0} {1} {2}", i, c, hardest_string);
            }
        }
    }
}

Sự khác biệt chính là:

  1. Tìm kiếm toàn diện các giải pháp
    Mã này tạo ra một tập hợp dữ liệu để tìm đầu vào khó nhất để giải cho thuật toán này.
  2. Tất cả các giải pháp so với khó giải quyết nhất
    Mã cho câu hỏi trước đã tạo ra tất cả các giải pháp bằng cách sử dụng trình tạo python. Mã này chỉ hiển thị khó nhất cho mỗi chiều dài mẫu.
  3. Thuật toán chấm điểm
    Mã này kiểm tra khoảng cách từ phần tử giữa đến cạnh trái và phải của nó. Mã python đã kiểm tra xem một tổng có cao hơn 0 hay không.
  4. Hội tụ một ứng cử viên
    Mã hiện tại hoạt động từ giữa về phía rìa để tìm một ứng cử viên. Mã trong bài toán trước làm việc từ các cạnh về giữa. Thay đổi cuối cùng này mang lại một cải tiến hiệu suất lớn.
  5. Sử dụng các nhóm chẵn và lẻ
    Dựa trên các quan sát ở phần cuối của bài viết này, mã tìm kiếm các cặp số chẵn của các cặp số lẻ để tìm L và U, giữ M cố định. Điều này làm giảm số lượng tìm kiếm bằng thông tin tiền điện toán. Theo đó, mã sử dụng hai cấp độ gián tiếp trong vòng lặp chính của FindCandidate và yêu cầu hai lệnh gọi FindCandidate cho mỗi phần tử ở giữa: một lần cho các số chẵn và một lần cho các số lẻ.

Ý tưởng chung là làm việc trên các chỉ mục, không phải là biểu diễn thô của dữ liệu. Tính toán một mảng trong đó xuất hiện 1 cho phép thuật toán chạy theo thời gian tỷ lệ với số 1 trong dữ liệu thay vì theo thời gian tỷ lệ thuận với độ dài của dữ liệu. Đây là một chuyển đổi tiêu chuẩn: tạo cấu trúc dữ liệu cho phép hoạt động nhanh hơn trong khi vẫn giữ vấn đề tương đương.

Kết quả đã hết hạn: loại bỏ.


Chỉnh sửa: 2009-10-16 18:48

Trên dữ liệu của yx, được tin cậy trong các phản hồi khác với tư cách là đại diện cho dữ liệu cứng để tính toán, tôi nhận được các kết quả này ... Tôi đã xóa những kết quả này. Họ đã hết hạn.

Tôi sẽ chỉ ra rằng dữ liệu này không phải là khó nhất đối với thuật toán của tôi, vì vậy tôi nghĩ rằng giả định rằng các fractals của yx là khó giải quyết nhất là nhầm lẫn. Trường hợp xấu nhất đối với một thuật toán cụ thể, tôi dự đoán, sẽ phụ thuộc vào chính thuật toán đó và sẽ không có khả năng thống nhất giữa các thuật toán khác nhau.


Chỉnh sửa: 2009-10-17 13:30

Quan sát thêm về điều này.

Đầu tiên, chuyển đổi chuỗi 0 và 1 thành một mảng các chỉ mục cho từng vị trí của 1. Nói độ dài của mảng A là X. Sau đó, mục tiêu là tìm

0 <= L < M < U <= X-1

như vậy mà

A[M] - A[L] = A[U] - A[M]

hoặc là

2*A[M] = A[L] + A[U]

Vì A [L] và A [U] tổng thành số chẵn, nên chúng không thể là (chẵn, lẻ) hoặc (lẻ, chẵn). Việc tìm kiếm một trận đấu có thể được cải thiện bằng cách chia A [] thành các nhóm lẻ và chẵn và tìm kiếm các trận đấu trên A [M] trong các nhóm ứng viên lẻ và thậm chí lần lượt.

Tuy nhiên, đây là một sự tối ưu hóa hiệu suất hơn là một cải tiến thuật toán, tôi nghĩ vậy. Số lượng so sánh sẽ giảm, nhưng thứ tự của thuật toán nên giống nhau.


Chỉnh sửa 2009-10-18 00:45

Tuy nhiên, một tối ưu hóa khác xảy ra với tôi, trong cùng một xu hướng như tách các ứng cử viên thành chẵn và lẻ. Vì ba chỉ mục phải thêm vào bội số của 3 (a, a + x, a + 2x - mod 3 là 0, bất kể a và x), bạn có thể tách L, M và U thành các giá trị mod 3 của chúng :

M  L  U
0  0  0
   1  2
   2  1
1  0  2
   1  1
   2  0
2  0  1
   1  0
   2  2

Trên thực tế, bạn có thể kết hợp điều này với quan sát chẵn / lẻ và tách chúng thành các giá trị mod 6 của chúng:

M  L  U
0  0  0
   1  5
   2  4
   3  3
   4  2
   5  1

và như thế. Điều này sẽ cung cấp một tối ưu hóa hiệu suất hơn nữa nhưng không phải là một sự tăng tốc thuật toán.


2

Chưa thể đưa ra giải pháp :(, nhưng có một số ý tưởng.

Điều gì sẽ xảy ra nếu chúng ta bắt đầu từ một vấn đề ngược lại: xây dựng một chuỗi với số lượng tối đa là 1 giây và KHÔNG CÓ bất kỳ bộ ba cách đều nhau. Nếu bạn có thể chứng minh số lượng tối đa 1s là o (n), thì bạn có thể cải thiện ước tính của mình bằng cách chỉ lặp qua danh sách 1s.


Chà, số 1 chắc chắn được giới hạn ở trên bởi O (n). Không thể là O (n ** 2), phải không - số lượng 1 tăng nhanh hơn dữ liệu? Câu hỏi quan trọng là liệu giới hạn trên có thấp hơn không.
hughdbrown

Tôi đã sử dụng chữ o nhỏ, không phải chữ lớn
Olexiy

2

Điều này có thể giúp ....

Vấn đề này giảm xuống như sau:

Đưa ra một chuỗi các số nguyên dương, tìm một dãy con liền kề được phân chia thành một tiền tố và một hậu tố sao cho tổng tiền tố của chuỗi con bằng với tổng của hậu tố của chuỗi con.

Ví dụ, được đưa ra một chuỗi [ 3, 5, 1, 3, 6, 5, 2, 2, 3, 5, 6, 4 ], chúng ta sẽ tìm thấy một chuỗi con [ 3, 6, 5, 2, 2]có tiền tố [ 3, 6 ]với tổng tiền tố 9và hậu tố là[ 5, 2, 2 ] có tổng hậu tố là 9.

Việc giảm như sau:

Đưa ra một chuỗi các số không và số một, và bắt đầu từ cái ngoài cùng bên trái, tiếp tục di chuyển sang bên phải. Mỗi lần gặp phải một lần khác, hãy ghi lại số lần di chuyển kể từ lần trước đó gặp phải và nối số đó vào chuỗi kết quả.

Ví dụ, đưa ra một chuỗi [ 0, 1, 1, 0, 0, 1, 0, 0, 0, 1 0 ], chúng tôi sẽ tìm thấy sự giảm bớt [ 1, 3, 4]. Từ việc giảm này, chúng tôi tính toán phần tiếp theo của [ 1, 3, 4], tiền tố của [ 1, 3]tổng 4và hậu tố của [ 4 ]tổng 4.

Sự giảm này có thể được tính toán trong O(n).

Thật không may, tôi không chắc chắn nơi để đi từ đây.


1
Đó là một ký hiệu nhỏ gọn hơn, nhưng nó sẽ không giúp phức tạp thời gian. Tập hợp các phân vùng "tiền tố" là đẳng cấu cho tìm kiếm tất cả các cặp trong tất cả các lần xuất hiện của "1", đó là O (n ^ 2).
p00ya

Có những thuật toán rõ ràng ngoài kia liên quan đến các khoản tiền kế tiếp nhau. Thật không may, tất cả chúng dường như đối phó với việc tìm kiếm phần tiếp theo liền kề với tổng tối đa trong O (n).
yfeldblum

@ p00ya điều này không đúng. Sử dụng algorhitm này, độ đồng bộ thời gian phụ thuộc vào giới hạn trên của số lượng sai, bởi assupton trên chuỗi tạo ra Cantor là ((3/2) ^ (log (n) / log (3))) và độ phức tạp không gian trở thành này, nhưng độ phức tạp thời gian trở thành này nhân với n. Kiểm tra câu trả lời thứ hai của tôi. (không phải là tiêu cực): D
Luka Rahne

@ralu: đó là theo giả định của bạn rằng các chuỗi do Cantor tạo ra là trường hợp xấu nhất, đó là sai. Đối với bản ghi, số lượng cặp chắc chắn là O (n ^ 2); nhưng tôi đoán rằng tôi đã thực sự ngụ ý rằng đó là Omega lớn (n ^ 2), không chính xác với những kết quả này (xem liên kết NrootN đặc biệt), cho thấy giới hạn thấp hơn trong các cặp Omega lớn (n ^ (2 / 1.52 )) bằng chứng hoặc big-Omega (n ^ (4/3)) bằng phỏng đoán.
p00ya

1

Đối với loại vấn đề đơn giản (nghĩa là bạn tìm kiếm ba "1" chỉ với (nghĩa là 0 hoặc nhiều hơn) "0" giữa nó), khá đơn giản: Bạn chỉ có thể chia chuỗi ở mỗi "1" và tìm kiếm hai chuỗi liền kề có cùng một chiều dài (dĩ nhiên là lần thứ hai không phải là lần cuối cùng). Rõ ràng, điều này có thể được thực hiện trong thời gian O (n) .

Đối với phiên bản phức tạp hơn (nghĩa là bạn tìm kiếm chỉ mục i và khoảng cách g > 0 sao cho s[i]==s[i+g]==s[i+2*g]=="1"), tôi không chắc chắn, nếu có tồn tại giải pháp O (n log n) , vì có thể có bộ ba O (n²) tài sản này (nghĩ về một chuỗi tất cả những cái, có khoảng n² / 2 bộ ba như vậy). Tất nhiên, bạn chỉ tìm kiếm một trong số này, nhưng hiện tại tôi không biết, làm thế nào để tìm thấy nó ...


Vâng, chúng tôi đang thảo luận về phiên bản khó hơn của vấn đề. Tuy nhiên, giải pháp n * log (n) có thể có thể.
Olexiy

1
Thực tế có n chọn 3 là O (n ^ 3) có thể gấp ba lần, tôi nghĩ khi bạn nói khoảng n ^ 2/2 bạn nghĩ n chọn 2
ldog

@gmatt: n chọn 2 là đủ; nếu chúng ta sửa hai số 1 thì vị trí của số thứ ba được xác định và sẽ không đổi để xem có số 1 ở vị trí đó hay không.
ShreevatsaR

@ShreevatsaR: đúng vậy, tôi nghĩ rằng, tôi đã nghĩ về trường hợp không bị ràng buộc.
ldog

1
@gmatt: thực ra, chúng tôi đang tìm kiếm Tuples (i, g) như được định nghĩa ở trên với các ràng buộc là 0 <= i <(n-3) và 0 <g <(ni-1) / 2, do đó ước tính n ^ 2/2 ...
MartinStettner

1

Một câu hỏi thú vị, nhưng một khi bạn nhận ra rằng mô hình thực tế giữa hai '1' không quan trọng, thuật toán sẽ trở thành:

  • quét tìm kiếm '1'
  • bắt đầu từ lần quét vị trí tiếp theo để tìm '1' khác (đến cuối mảng trừ đi khoảng cách từ '1' đầu tiên hiện tại hoặc nếu không thì '1' sẽ nằm ngoài giới hạn)
  • nếu ở vị trí của 2 '1' cộng với khoảng cách đến 1 'một phần ba' 1 'đầu tiên được tìm thấy, chúng ta có các khoảng trắng đều nhau.

Trong mã, thời trang JTest, (Lưu ý mã này không được viết là hiệu quả nhất và tôi đã thêm một số println để xem điều gì xảy ra.)

import java.util.Random;

import junit.framework.TestCase;

public class AlgorithmTest extends TestCase {

 /**
  * Constructor for GetNumberTest.
  *
  * @param name The test's name.
  */
 public AlgorithmTest(String name) {
  super(name);
 }

 /**
  * @see TestCase#setUp()
  */
 protected void setUp() throws Exception {
  super.setUp();
 }

 /**
  * @see TestCase#tearDown()
  */
 protected void tearDown() throws Exception {
  super.tearDown();
 }

 /**
  * Tests the algorithm.
  */
 public void testEvenlySpacedOnes() {

  assertFalse(isEvenlySpaced(1));
  assertFalse(isEvenlySpaced(0x058003));
  assertTrue(isEvenlySpaced(0x07001));
  assertTrue(isEvenlySpaced(0x01007));
  assertTrue(isEvenlySpaced(0x101010));

  // some fun tests
  Random random = new Random();

  isEvenlySpaced(random.nextLong());
  isEvenlySpaced(random.nextLong());
  isEvenlySpaced(random.nextLong());
 }

 /**
  * @param testBits
  */
 private boolean isEvenlySpaced(long testBits) {
  String testString = Long.toBinaryString(testBits);
  char[] ones = testString.toCharArray();
  final char ONE = '1';

  for (int n = 0; n < ones.length - 1; n++) {

   if (ONE == ones[n]) {
    for (int m = n + 1; m < ones.length - m + n; m++) {

     if (ONE == ones[m] && ONE == ones[m + m - n]) {
      System.out.println(" IS evenly spaced: " + testBits + '=' + testString);
      System.out.println("               at: " + n + ", " + m + ", " + (m + m - n));
      return true;
     }
    }
   }
  }

  System.out.println("NOT evenly spaced: " + testBits + '=' + testString);
  return false;
 }
}

4
Nếu tôi không nhầm, đây là O (n²) vì vòng lặp bên ngoài chạy n lần và vòng lặp bên trong chạy trung bình n / 2 lần.
StriplingWar Warrior

Vòng lặp bên ngoài chạy trung bình n lần và vòng lặp bên trong chạy trung bình n / 4 nhưng chỉ được bắt đầu từ các vị trí theo sau '1'. Để tiếp cận hành vi n ^ 2, số lượng '1' phải cao dẫn đến kết quả thực sự sớm do đó dừng xử lý. Do đó, hành vi n ^ 2 sẽ không bao giờ xảy ra. Cách xác định O dựa trên các thuộc tính đã biết của dữ liệu thoát khỏi tôi vào lúc này.
rsp

Đáng tiếc đó không phải là về thời gian chạy thực tế trung bình mà là thời gian chạy Big O lý thuyết. Và cách tiếp cận của bạn là O (n²) (giống như của tôi vì cách tiếp cận của bạn giống với của tôi)
DaClown

Tôi đã không nói về hành vi trung bình, nhưng hành vi tối đa. Tôi sẽ không ngạc nhiên nếu có thể chứng minh rằng entropy tối đa thất bại trong bài kiểm tra có chứa log n '1' trong chuỗi.
rsp

Điều gì sẽ xảy ra nếu bạn cập nhật chỉ mục trong vòng lặp bên ngoài với chỉ số của 1 đầu tiên được tìm thấy trong vòng lặp bên trong tức là if (cái [m] == ONE) {n = m}? Điều đó có giúp ích gì cho O lớn không?
hấp25

1

Tôi nghĩ về một cách tiếp cận chia rẽ và có thể làm việc.

Đầu tiên, trong quá trình tiền xử lý, bạn cần chèn tất cả các số nhỏ hơn một nửa kích thước đầu vào ( n / 3) vào danh sách.

Đưa ra một chuỗi: 0000010101000100(lưu ý rằng ví dụ cụ thể này là hợp lệ)

Chèn tất cả các số nguyên tố (và 1) từ 1 đến (16/2) vào danh sách: {1, 2, 3, 4, 5, 6, 7}

Sau đó chia nó làm đôi:

100000101 01000100

Tiếp tục làm điều này cho đến khi bạn nhận được các chuỗi có kích thước 1. Đối với tất cả các chuỗi có một kích thước có 1 trong đó, hãy thêm chỉ mục của chuỗi vào danh sách các khả năng; mặt khác, trả về -1 cho thất bại.

Bạn cũng cần trả về một danh sách các khoảng cách khoảng cách vẫn có thể, được liên kết với từng chỉ số bắt đầu. (Bắt đầu với danh sách bạn đã thực hiện ở trên và xóa các số khi bạn đi) Ở đây, một danh sách trống có nghĩa là bạn chỉ giao dịch với 1 và vì vậy bất kỳ khoảng cách nào đều có thể vào thời điểm này; mặt khác, danh sách bao gồm các khoảng cách phải được loại trừ.

Vì vậy, tiếp tục với ví dụ trên:

1000 0101 0100 0100

10 00 01 01 01 00 01 00

1 0 0 0 0 1 0 1 0 1 0 0 0 1 0 0

Trong bước kết hợp đầu tiên, chúng ta có tám bộ hai bây giờ. Đầu tiên, chúng ta có khả năng của một tập hợp, nhưng chúng ta học được rằng khoảng cách 1 là không thể vì không có số 0 nào ở đó. Vì vậy, chúng tôi trả về 0 (cho chỉ mục) và {2,3,4,5,7} vì thực tế là khoảng cách bằng 1 là không thể. Trong lần thứ hai, chúng ta không có gì và vì vậy trả về -1. Trong phần ba, chúng ta có một trận đấu không có khoảng cách nào bị loại bỏ trong chỉ số 5, vì vậy hãy trả về 5, {1,2,3,4,5,7}. Trong cặp thứ tư, chúng tôi trả về 7, {1,2,3,4,5,7}. Trong phần năm, trả về 9, {1,2,3,4,5,7}. Trong lần thứ sáu, trả về -1. Trong phần bảy, trả lại 13, {1,2,3,4,5,7}. Trong lần thứ tám, trả về -1.

Kết hợp lại thành bốn bộ bốn, chúng ta có:

1000: Trả về (0, {4,5,6,7}) 0101: Trả về (5, {2,3,4,5,6,7}), (7, {1,2,3,4,5,6 , 7}) 0100: Trả lại (9, {3,4,5,6,7}) 0100: Trả lại (13, {3,4,5,6,7})

Kết hợp thành bộ tám:

10000101: Trả về (0, {5,7}), (5, {2,3,4,5,6,7}), (7, {1,2,3,4,5,6,7}) 01000100: Trả lại (9, {4,7}), (13, {3,4,5,6,7})

Kết hợp thành một bộ mười sáu:

10000101 01000100

Khi chúng tôi tiến bộ, chúng tôi tiếp tục kiểm tra tất cả các khả năng cho đến nay. Cho đến bước này, chúng tôi đã để lại những thứ vượt quá cuối chuỗi, nhưng bây giờ chúng tôi có thể kiểm tra tất cả các khả năng.

Về cơ bản, chúng tôi kiểm tra 1 đầu tiên với các khoảng cách 5 và 7 và thấy rằng chúng không xếp hàng lên 1. (Lưu ý rằng mỗi kiểm tra là CONSTANT, không phải thời gian tuyến tính) Sau đó, chúng tôi kiểm tra lần thứ hai (chỉ số 5) với các khoảng cách 2, 3, 4, 5, 6 và 7-- hoặc chúng tôi có thể dừng lại ở 2 mà thực sự phù hợp lên.

Phù! Đó là một thuật toán khá dài.

Tôi không biết 100% nếu đó là O (n log n) vì bước cuối cùng, nhưng mọi thứ cho đến đó chắc chắn là O (n log n) theo như tôi có thể nói. Tôi sẽ quay lại vấn đề này sau và cố gắng tinh chỉnh bước cuối cùng.

EDIT: Thay đổi câu trả lời của tôi để phản ánh nhận xét của Welbog. Xin lỗi vì lỗi. Tôi cũng sẽ viết một số mã giả sau, khi tôi có thêm một chút thời gian để giải mã những gì tôi đã viết lại. ;-)


Tôi không tuân theo thuật toán của bạn, nhưng +1 để thử một thuật toán thực sự cố gắng là O (n log n)
ldog

Cảm ơn. Tôi sẽ cố gắng giải thích nó tốt hơn khi tôi có nhiều thời gian hơn (có thể viết một số mã giả hoặc một cái gì đó).
Bạch kim Azure

Tại sao bạn chỉ nhìn vào khả năng khoảng cách của số nguyên tố? Làm thế nào bạn sẽ đề xuất để phù hợp với một chuỗi như thế 100010001nào? Nếu tôi hiểu chính xác cách tiếp cận của bạn, nó sẽ không thể phù hợp với nó bởi vì câu trả lời đúng (0,{4})không thể tính được. Tôi nghĩ rằng bạn cần các số nguyên tố trong danh sách của mình, thật dễ dàng để đưa ra các chuỗi bệnh lý làm tăng danh sách các khả năng mà bạn cần kiểm tra lên cao hơn O (n log (n)), tôi nghĩ.
Welbog

thề , ban đầu tôi sẽ làm bội số, nhưng tôi đã thay đổi câu trả lời giữa chừng và không thay đổi mọi thứ. Lấy làm tiếc. Sẽ khắc phục trong thời gian ngắn
Platinum Azure

3
Tôi không nghĩ đó là O (n log n). Trong bước kết hợp đầu tiên, bạn xử lý (n / 2) bộ, mỗi bộ có thể trả về một bộ khoảng cách O (n) có thể. Điều này một mình làm cho nó O (n ^ 2), thật không may.
MartinStettner

1

Tôi sẽ đưa ra dự đoán sơ bộ của mình ở đây và để những người giỏi hơn trong việc tính toán độ phức tạp giúp tôi về cách thuật toán của tôi xuất hiện trong ký hiệu O khôn ngoan

  1. đưa ra chuỗi nhị phân 0000010101000100 (ví dụ)
  2. cắt đầu và đuôi của số không -> 00000 101010001 00
  3. chúng tôi nhận được 101010001 từ tính toán trước
  4. kiểm tra xem bit giữa có phải là 'một' hay không, nếu đúng, tìm thấy ba giá trị cách đều nhau 'cái' (chỉ khi số bit được đánh số lẻ)
  5. tương ứng, nếu số bit bị cắt còn lại được đánh số chẵn, đầu và đuôi 'một' không thể là một phần của khoảng cách đều nhau 'một',
  6. chúng tôi sử dụng 1010100001 làm ví dụ (có thêm 'zero' để trở thành cây trồng được đánh số chẵn), trong trường hợp này chúng tôi cần cắt lại, sau đó trở thành -> 10101 00001
  7. chúng tôi nhận được 10101 từ tính toán trước đó và kiểm tra bit giữa và chúng tôi đã tìm thấy bit cách đều nhau một lần nữa

Tôi không có ý tưởng làm thế nào để tính toán sự phức tạp cho việc này, bất cứ ai có thể giúp đỡ?

chỉnh sửa: thêm một số mã để minh họa ý tưởng của tôi

edit2: đã cố gắng biên dịch mã của tôi và tìm thấy một số lỗi lớn, đã sửa

char *binaryStr = "0000010101000100";

int main() {
   int head, tail, pos;
   head = 0;
   tail = strlen(binaryStr)-1;
   if( (pos = find3even(head, tail)) >=0 )
      printf("found it at position %d\n", pos);
   return 0;
}

int find3even(int head, int tail) {
   int pos = 0;
   if(head >= tail) return -1;
   while(binaryStr[head] == '0') 
      if(head<tail) head++;
   while(binaryStr[tail] == '0') 
      if(head<tail) tail--;
   if(head >= tail) return -1;
   if( (tail-head)%2 == 0 && //true if odd numbered
       (binaryStr[head + (tail-head)/2] == '1') ) { 
         return head;
   }else {
      if( (pos = find3even(head, tail-1)) >=0 )
         return pos;
      if( (pos = find3even(head+1, tail)) >=0 )
         return pos;
   }
   return -1;
}

@recursive Tôi nghĩ rằng nó sẽ hoạt động khi đạt được cuộc gọi find3even (head + 1, tail), sau đó sẽ cắt nó thành 111 ở đầu = 4, bạn có thể kiểm tra lại cho tôi không?
andycjw

@recursive vui lòng kiểm tra mã tôi đã thêm để giải thích rõ hơn mã giả tôi đã tạo trước đó, không nghiêm ngặt và súc tích
andycjw

Đây là nlogn - đối với n bit, chúng tôi mong đợi các lần lặp logn xấp xỉ kiểm tra các bit n * c trong đó C là hằng số.
Ron Warholic

Vâng, điều này dường như thất bại vào 111001 và 100111 là trường hợp đơn giản nhất. Khoảng cách đều nhau 1 không phải tập trung vào bit giữa.
Dean J

Nó xử lý các trường hợp đó một cách chính xác, 111001 có số bit chẵn nên ngay lập tức được chia thành 111 và 001. Vì 111 có số bit lẻ và bit giữa là một bit nên nó trả về thành công.
Ron Warholic

1

Tôi đã nghĩ ra một cái gì đó như thế này:

def IsSymetric(number):
    number = number.strip('0')

    if len(number) < 3:
        return False
    if len(number) % 2 == 0:
        return IsSymetric(number[1:]) or IsSymetric(number[0:len(number)-2])
    else:
        if number[len(number)//2] == '1':
            return True
        return IsSymetric(number[:(len(number)//2)]) or IsSymetric(number[len(number)//2+1:])
    return False

Điều này được lấy cảm hứng từ andycjw.

  1. Cắt bớt các số không.
  2. Nếu thậm chí sau đó kiểm tra hai chuỗi con 0 - (len-2) (bỏ qua ký tự cuối cùng) và từ 1 - (len-1) (bỏ qua ký tự đầu tiên)
  3. Nếu không thậm chí hơn nếu char giữa là một thì chúng ta có thành công. Khác phân chia chuỗi ở giữa mà không có phần tử midle và kiểm tra cả hai phần.

Đối với độ phức tạp, đây có thể là O (nlogn) vì trong mỗi lần đệ quy chúng ta chia cho hai.

Hy vọng nó giúp.


Có vẻ như bạn đang chuyển đổi một vấn đề với các yếu tố N thành 2 vấn đề với các yếu tố N-1. Chia nó thành một nửa có nghĩa là chuyển đổi nó thành 2 vấn đề với các yếu tố N / 2.
RHSeeger

Đó chỉ là trường hợp cho chiều dài thậm chí. Vì vậy, nếu len là 8, thuật toán tạo ra các chuỗi có độ dài: 7, 7, 3, 3, 3, 3. Độ cao của cây đệ quy là 3 và bằng với lg (8).
Beku

1

Ok, tôi sẽ có một cú đâm khác vào vấn đề. Tôi nghĩ rằng tôi có thể chứng minh thuật toán O (n log (n)) tương tự như thuật toán đã được thảo luận bằng cách sử dụng cây nhị phân cân bằng để lưu trữ khoảng cách giữa 1 giây. Cách tiếp cận này được lấy cảm hứng từ quan sát của Justice về việc giảm vấn đề thành một danh sách khoảng cách giữa các 1.

Chúng ta có thể quét chuỗi đầu vào để xây dựng cây nhị phân cân bằng xung quanh vị trí của 1 sao cho mỗi nút lưu vị trí của 1 và mỗi cạnh được gắn nhãn với khoảng cách đến 1 liền kề cho mỗi nút con. Ví dụ:

10010001 gives the following tree

      3
     / \
  2 /   \ 3
   /     \
  0       7

Điều này có thể được thực hiện trong O (n log (n)) vì, đối với một chuỗi có kích thước n, mỗi lần chèn sẽ lấy O (log (n)) trong trường hợp xấu nhất.

Sau đó, vấn đề là tìm kiếm cây để khám phá xem, tại bất kỳ nút nào, có một đường dẫn từ nút đó qua con trái có cùng khoảng cách với đường dẫn qua con phải hay không. Điều này có thể được thực hiện đệ quy trên mỗi cây con. Khi hợp nhất hai cây con trong tìm kiếm, chúng ta phải so sánh khoảng cách từ các đường dẫn trong cây con bên trái với khoảng cách từ các đường dẫn ở bên phải. Vì số lượng đường dẫn trong một cây con sẽ tỷ lệ thuận với log (n) và số lượng nút là n, tôi tin rằng điều này có thể được thực hiện trong thời gian O (n log (n)).

Tôi có bỏ lỡ điều gì không?


"Vì số lượng đường dẫn trong một cây con sẽ tỷ lệ thuận với log (n)" Tại sao không phải là n? Nói chung đây là một cách tiếp cận đầy hứa hẹn.
sdcvvc

@sdcwc: Nó tỷ lệ thuận với log (n) chứ không phải n vì trong một cây cân bằng, mỗi cây con có một nửa các nút và số đường dẫn đến gốc của cây con giống như số nút trong cây con (không bao gồm nguồn gốc).
Jeremy Bourque

0

Điều này có vẻ như là một vấn đề thú vị vì vậy tôi quyết định thử nó.

Tôi đang đưa ra giả định rằng 111000001 sẽ tìm thấy 3 cái đầu tiên và thành công. Về cơ bản, số lượng các số 0 theo sau 1 là điều quan trọng, vì 0111000 giống với 111000 theo định nghĩa của bạn. Khi bạn tìm thấy hai trường hợp của 1, thì 1 trường hợp tiếp theo sẽ hoàn thành bộ ba.

Đây là Python.

def find_three(bstring):
    print bstring
    dict = {}
    lastone = -1
    zerocount = 0
    for i in range(len(bstring)):
        if bstring[i] == '1':
            print i, ': 1'
            if lastone != -1:
                if(zerocount in dict):
                    dict[zerocount].append(lastone)
                    if len(dict[zerocount]) == 2:
                        dict[zerocount].append(i)
                        return True, dict
                else:
                    dict[zerocount] = [lastone]
            lastone = i
            zerocount = 0
        else:
            zerocount = zerocount + 1
    #this is really just book keeping, as we have failed at this point
    if lastone != -1:
        if(zerocount in dict):
            dict[zerocount].append(lastone)
        else:
            dict[zerocount] = [lastone]
    return False, dict

Đây là lần thử đầu tiên, vì vậy tôi chắc chắn điều này có thể được viết theo cách sạch sẽ hơn. Vui lòng liệt kê các trường hợp phương pháp này thất bại dưới đây.


@recursive, những cái đó không cách đều nhau.
James McMahon

Bạn có ý nghĩa gì bởi khoảng cách đều nhau? Nhìn vào chỉ số 0, 3 và 6. Tất cả những cái và hai cái tách nhau.
đệ quy

Ồ tôi hiểu rồi, như tôi đã hiểu, số không chỉ được bao gồm trong khoảng cách.
James McMahon

Câu hỏi không đề cập đến "1001011", trong đó điều này không hoạt động. Có một câu trả lời trước đó (bây giờ đã bị xóa), được đăng ngay sau khi câu hỏi được hỏi, giải quyết vấn đề tương tự (khác) như câu hỏi này. :-)
ShreevatsaR

Tôi đã xem xét điều này tại nơi làm việc ngày hôm nay và tôi không hiểu Rob có ý gì với bản chỉnh sửa của mình. Tôi đã chỉnh sửa câu hỏi cho rõ ràng. Tôi nên biết rằng tôi đã bỏ lỡ một cái gì đó khi tôi có một thời gian dễ dàng với nó.
James McMahon

0

Tôi giả sử lý do đây là nlog (n) là do:

  • Để tìm 1 là bắt đầu của bộ ba, bạn cần kiểm tra (n-2) ký tự. Nếu bạn chưa tìm thấy nó vào thời điểm đó, bạn sẽ không (ký tự n-1 và n không thể bắt đầu một bộ ba) (O (n))
  • Để tìm số 1 thứ hai là một phần của bộ ba (bắt đầu bằng số thứ nhất), bạn cần kiểm tra m / 2 (m = nx, trong đó x là phần bù của 1 ký tự đầu tiên). Điều này là do, nếu bạn không tìm thấy số 1 vào lúc bạn đi được nửa đường từ điểm đầu tiên đến điểm cuối, thì bạn sẽ không ... vì người thứ ba phải chính xác cùng khoảng cách với lần thứ hai. (O (nhật ký (n)))
  • Nó tìm O (1) để tìm 1 cuối cùng vì bạn biết chỉ số đó phải ở thời điểm bạn tìm thấy thứ nhất và thứ hai.

Vì vậy, bạn có n, log (n) và 1 ... O (nlogn)

Chỉnh sửa: Rất tiếc, xấu của tôi. Bộ não của tôi đã thiết lập rằng n / 2 là logn ... điều đó rõ ràng là không (nhân đôi số trên các vật phẩm vẫn tăng gấp đôi số lần lặp ở vòng lặp bên trong). Đây vẫn là n ^ 2, không giải quyết vấn đề. Chà, ít nhất tôi đã viết được một số mã :)


Thực hiện trong Tcl

proc get-triplet {input} {
    for {set first 0} {$first < [string length $input]-2} {incr first} {
        if {[string index $input $first] != 1} {
            continue
        }
        set start [expr {$first + 1}]
        set end [expr {1+ $first + (([string length $input] - $first) /2)}]
        for {set second $start} {$second < $end} {incr second} {
            if {[string index $input $second] != 1} {
                continue
            }
            set last [expr {($second - $first) + $second}]
            if {[string index $input $last] == 1} {
                return [list $first $second $last]
            }
        }
    }
    return {}
}

get-triplet 10101      ;# 0 2 4
get-triplet 10111      ;# 0 2 4
get-triplet 11100000   ;# 0 1 2
get-triplet 0100100100 ;# 1 4 7

0

Tôi nghĩ rằng tôi đã tìm ra cách giải quyết vấn đề, nhưng tôi không thể xây dựng một bằng chứng chính thức. Giải pháp tôi đã thực hiện được viết bằng Java và nó sử dụng bộ đếm 'n' để đếm xem có bao nhiêu danh sách / mảng truy cập. Vì vậy, n nên nhỏ hơn hoặc bằng chuỗi logLạng * log (chuỗiLạng) nếu đúng. Tôi đã thử nó cho các số từ 0 đến 2 ^ 22, và nó hoạt động.

Nó bắt đầu bằng cách lặp qua chuỗi đầu vào và lập danh sách tất cả các chỉ mục chứa một. Đây chỉ là O (n).

Sau đó, từ danh sách các chỉ mục, nó chọn một chỉ số đầu tiên và một chỉ số thứ hai lớn hơn chỉ số đầu tiên. Hai chỉ mục này phải giữ các chỉ mục, bởi vì chúng nằm trong danh sách các chỉ mục. Từ đó, chỉ số thứ ba có thể được tính toán. Nếu inputString [third Index] là 1 thì nó dừng lại.

public static int testString(String input){
//n is the number of array/list accesses in the algorithm
int n=0;

//Put the indices of all the ones into a list, O(n)
ArrayList<Integer> ones = new ArrayList<Integer>();
for(int i=0;i<input.length();i++){
    if(input.charAt(i)=='1'){
        ones.add(i);
    }
}

//If less than three ones in list, just stop
if(ones.size()<3){
    return n;
}

int firstIndex, secondIndex, thirdIndex;
for(int x=0;x<ones.size()-2;x++){
    n++;
    firstIndex = ones.get(x);

    for(int y=x+1; y<ones.size()-1; y++){
        n++;
        secondIndex = ones.get(y);
        thirdIndex = secondIndex*2 - firstIndex;

        if(thirdIndex >= input.length()){
            break;
        }

        n++;
        if(input.charAt(thirdIndex) == '1'){
            //This case is satisfied if it has found three evenly spaced ones
            //System.out.println("This one => " + input);
            return n;
        }
    }
}

return n;

}

lưu ý bổ sung: bộ đếm n không được tăng lên khi nó lặp qua chuỗi đầu vào để xây dựng danh sách các chỉ mục. Hoạt động này là O (n), vì vậy dù sao nó cũng không ảnh hưởng đến độ phức tạp thuật toán.


Bạn dường như vẫn có hai vòng lặp O (n), được lồng vào nhau, điều này làm cho nó trở thành O (n ^ 2)
RHSeeger

Mảng chỉ mục không cùng kích thước với chuỗi đầu vào. Điều này khiến tôi khó có thể viết một bằng chứng thực sự hoặc chứng minh nó không đúng. Tôi nghi ngờ rằng có một số ý tưởng toán học cơ bản làm cho công việc này.
Robert Parker

1
Tôi nghĩ mẹo cho vấn đề này là mặc dù thuật toán của bạn là O (n ^ 2), trường hợp xấu nhất có thể xảy ra của chuỗi bạn sẽ chỉ dẫn đến các lần lặp O (nlogn) nếu không bạn sẽ tìm thấy giải pháp bằng thuật toán của mình.
z -

2
thử nghiệm nó lên đến 2 ^ 22 không thực sự kiểm tra độ phức tạp của nó. 2 ^ 22 chỉ có 22 bit, có nghĩa là N của bạn là 22. Hãy thử nó với một vài giá trị trong đó N là vài triệu.
Peter Recore

1
Hãy thử thuật toán này với một trong những chuỗi "xấu" tối đa được đưa ra trong câu trả lời của yx và bạn sẽ thấy đây là một O(n^2)thuật toán.
Welbog

0

Một bước vào vấn đề là nghĩ về các yếu tố và thay đổi.

Với dịch chuyển, bạn so sánh chuỗi của số và số 0 với phiên bản đã dịch chuyển của chính nó. Sau đó, bạn có những người phù hợp. Lấy ví dụ này thay đổi bởi hai:

1010101010
  1010101010
------------
001010101000

Các kết quả 1 (bitwise ANDed), phải đại diện cho tất cả các số 1 được cách đều nhau bởi hai. Ví dụ tương tự được thay đổi bởi ba:

1010101010
   1010101010
-------------
0000000000000

Trong trường hợp này, không có số 1 nào cách đều nhau ba.

Vì vậy, những gì nói với bạn? Vâng, bạn chỉ cần kiểm tra ca làm việc là số nguyên tố. Ví dụ: bạn có hai số 1 cách nhau sáu. Bạn sẽ chỉ phải kiểm tra 'hai' ca và 'ba' ca (vì những lần chia sáu này). Ví dụ:

10000010 
  10000010 (Shift by two)
    10000010
      10000010 (We have a match)

10000010
   10000010 (Shift by three)
      10000010 (We have a match)

Vì vậy, các ca làm việc duy nhất bạn cần kiểm tra là 2,3,5,7,11,13, v.v ... Lên đến số nguyên tố gần nhất với căn bậc hai kích thước của chuỗi chữ số.

Gần giải quyết xong?

Tôi nghĩ rằng tôi gần hơn với một giải pháp. Về cơ bản:

  1. Quét chuỗi trong 1 giây. Đối với mỗi 1 lưu ý, phần còn lại sau khi lấy mô-đun vị trí của nó. Các mô-đun dao động từ 1 đến một nửa kích thước của chuỗi. Điều này là do kích thước tách lớn nhất có thể là một nửa chuỗi. Điều này được thực hiện trong O (n ^ 2). NHƯNG. Chỉ các mô đun nguyên tố cần được kiểm tra để O (n ^ 2 / log (n))
  2. Sắp xếp danh sách các mô đun / phần dư theo thứ tự mô đun lớn nhất trước, điều này có thể được thực hiện trong thời gian O (n * log (n)).
  3. Tìm kiếm ba mô-đun / phần còn lại liên tiếp giống nhau.
  4. Bằng cách nào đó lấy lại vị trí của những người!

Tôi nghĩ rằng manh mối lớn nhất cho câu trả lời, đó là các thuật toán sắp xếp nhanh nhất, là O (n * log (n)).

SAI LẦM

Bước 1 là sai như được chỉ ra bởi một đồng nghiệp. Nếu chúng ta có 1 ở vị trí 2,12 và 102. Sau đó, lấy mô-đun 10, tất cả chúng sẽ có cùng số dư, và không cách nhau như nhau! Lấy làm tiếc.


Đây là một cách tiếp cận thú vị, cho chúng tôi biết nếu bạn đưa ra một giải pháp đầy đủ.
James McMahon

thay đổi theo một số k O (n) lần và sau đó kiểm tra O (n) trên mỗi ca mang lại thuật toán O (n ^ 2), ngay cả khi bạn thay đổi một số. Thuật toán của bạn sẽ phải thay đổi nhiều hơn một số.
ldog

0

Dưới đây là một số suy nghĩ rằng, mặc dù những nỗ lực tốt nhất của tôi, dường như sẽ không bao bọc mình trong một cái cung. Tuy nhiên, chúng có thể là điểm khởi đầu hữu ích cho phân tích của ai đó.

Hãy xem xét giải pháp được đề xuất như sau, đây là cách tiếp cận mà một số người đã đề xuất, bao gồm cả bản thân tôi trong phiên bản trước của câu trả lời này. :)

  1. Cắt số 0 hàng đầu và dấu.
  2. Quét chuỗi tìm kiếm 1.
  3. Khi tìm thấy 1:
    1. Giả sử rằng đó là 1 giữa của giải pháp.
    2. Đối với mỗi 1 trước, sử dụng vị trí đã lưu của nó để tính toán vị trí dự đoán của 1 cuối cùng.
    3. Nếu vị trí được tính là sau khi kết thúc chuỗi, nó không thể là một phần của giải pháp, vì vậy hãy bỏ vị trí khỏi danh sách ứng cử viên.
    4. Kiểm tra giải pháp.
  4. Nếu không tìm thấy giải pháp, hãy thêm 1 hiện tại vào danh sách ứng cử viên.
  5. Lặp lại cho đến khi không tìm thấy thêm 1 giây.

Bây giờ hãy xem xét các chuỗi chuỗi đầu vào như sau, sẽ không có giải pháp:

101
101001
1010010001
101001000100001
101001000100001000001

Nói chung, đây là sự kết hợp của các chuỗi k có dạng j 0 'theo sau là 1 cho j từ 0 đến k-1.

k=2  101
k=3  101001
k=4  1010010001
k=5  101001000100001
k=6  101001000100001000001

Lưu ý rằng độ dài của các chuỗi con là 1, 2, 3, v.v ... Vì vậy, kích thước bài toán n có các chuỗi con có độ dài từ 1 đến k sao cho n = k (k + 1) / 2.

k=2  n= 3  101
k=3  n= 6  101001
k=4  n=10  1010010001
k=5  n=15  101001000100001
k=6  n=21  101001000100001000001

Lưu ý rằng k cũng theo dõi số lượng 1 mà chúng ta phải xem xét. Hãy nhớ rằng mỗi khi chúng ta nhìn thấy 1, chúng ta cần xem xét tất cả các số 1 đã thấy cho đến nay. Vì vậy, khi chúng ta nhìn thấy cái thứ hai, chúng ta chỉ xem xét cái thứ nhất, khi chúng ta thấy cái thứ ba, chúng ta xem xét lại cái thứ nhất, khi chúng ta thấy cái thứ tư thứ nhất, chúng ta cần xem xét lại cái thứ nhất, v.v. Đến cuối thuật toán, chúng tôi đã xem xét k (k - 1) / 2 cặp 1. Gọi đó là p.

k=2  n= 3  p= 1  101
k=3  n= 6  p= 3  101001
k=4  n=10  p= 6  1010010001
k=5  n=15  p=10  101001000100001
k=6  n=21  p=15  101001000100001000001

Mối quan hệ giữa n và p là n = p + k.

Quá trình đi qua chuỗi mất thời gian O (n). Mỗi lần gặp 1, tối đa (k-1) so sánh được thực hiện. Vì n = k (k + 1) / 2, n> k ** 2, nên sqrt (n)> k. Điều này cho chúng ta O (n sqrt (n)) hoặc O (n ** 3/2). Tuy nhiên, lưu ý rằng đó có thể không phải là một ràng buộc thực sự chặt chẽ, bởi vì số lượng so sánh đi từ 1 đến tối đa là k, nó không phải là toàn bộ thời gian. Nhưng tôi không chắc làm thế nào để giải thích điều đó trong toán học.

Nó vẫn không phải là O (n log (n)). Ngoài ra, tôi không thể chứng minh những đầu vào đó là trường hợp xấu nhất, mặc dù tôi nghi ngờ chúng là như vậy. Tôi nghĩ rằng việc đóng gói dày đặc hơn 1 đến phía trước sẽ dẫn đến việc đóng gói thậm chí còn thưa thớt hơn ở cuối.

Vì ai đó vẫn có thể thấy nó hữu ích, đây là mã của tôi cho giải pháp đó trong Perl:

#!/usr/bin/perl

# read input as first argument
my $s = $ARGV[0];

# validate the input
$s =~ /^[01]+$/ or die "invalid input string\n";

# strip leading and trailing 0's
$s =~ s/^0+//;
$s =~ s/0+$//;

# prime the position list with the first '1' at position 0
my @p = (0);

# start at position 1, which is the second character
my $i = 1;

print "the string is $s\n\n";

while ($i < length($s)) {
   if (substr($s, $i, 1) eq '1') {
      print "found '1' at position $i\n";
      my @t = ();
      # assuming this is the middle '1', go through the positions
      # of all the prior '1's and check whether there's another '1'
      # in the correct position after this '1' to make a solution
      while (scalar @p) {
         # $p is the position of the prior '1'
         my $p = shift @p;
         # $j is the corresponding position for the following '1'
         my $j = 2 * $i - $p;
         # if $j is off the end of the string then we don't need to
         # check $p anymore
         next if ($j >= length($s));
         print "checking positions $p, $i, $j\n";
         if (substr($s, $j, 1) eq '1') {
            print "\nsolution found at positions $p, $i, $j\n";
            exit 0;
         }
         # if $j isn't off the end of the string, keep $p for next time
         push @t, $p;
      }
      @p = @t;
      # add this '1' to the list of '1' positions
      push @p, $i;
   }
   $i++;
}

print "\nno solution found\n";

Trình tự "không giải pháp" của bạn là sai; chỉ số của mỗi 1 là dãy các số tam giác 1, 3, 6, 10, 15 ... vv và nó bao gồm các số 6, 36 và 66, tạo thành một tiến trình số học.
Jason S

0

Trong khi quét 1s, hãy thêm vị trí của chúng vào Danh sách. Khi thêm các giây thứ hai và 1 liên tiếp, hãy so sánh chúng với từng vị trí trong danh sách cho đến nay. Khoảng cách bằng currentOne (giữa) - trướcOne (trái). Bit bên phải là currentOne + giãn cách. Nếu là 1, kết thúc.

Danh sách những cái phát triển ngược với không gian giữa chúng. Nói một cách đơn giản, nếu bạn có nhiều 0 trong số 1 (như trong trường hợp xấu nhất), danh sách 1 số đã biết của bạn sẽ tăng khá chậm.

using System;
using System.Collections.Generic;

namespace spacedOnes
{
    class Program
    {
        static int[] _bits = new int[8] {128, 64, 32, 16, 8, 4, 2, 1};

        static void Main(string[] args)
        {
            var bytes = new byte[4];
            var r = new Random();
            r.NextBytes(bytes);
            foreach (var b in bytes) {
                Console.Write(getByteString(b));
            }
            Console.WriteLine();
            var bitCount = bytes.Length * 8;
            var done = false;
            var onePositions = new List<int>();
            for (var i = 0; i < bitCount; i++)
            {
                if (isOne(bytes, i)) {
                    if (onePositions.Count > 0) {
                        foreach (var knownOne in onePositions) {
                            var spacing = i - knownOne;
                            var k = i + spacing;
                            if (k < bitCount && isOne(bytes, k)) {
                                Console.WriteLine("^".PadLeft(knownOne + 1) + "^".PadLeft(spacing) + "^".PadLeft(spacing));
                                done = true;
                                break;
                            }
                        }
                    }
                    if (done) {
                        break;
                    }
                    onePositions.Add(i);
                }
            }
            Console.ReadKey();
        }

        static String getByteString(byte b) {
            var s = new char[8];
            for (var i=0; i<s.Length; i++) {
                s[i] = ((b & _bits[i]) > 0 ? '1' : '0');
            }
            return new String(s);
        }

        static bool isOne(byte[] bytes, int i)
        {
            var byteIndex = i / 8;
            var bitIndex = i % 8;
            return (bytes[byteIndex] & _bits[bitIndex]) > 0;
        }
    }
}

0

Tôi nghĩ tôi sẽ thêm một bình luận trước khi đăng giải pháp ngây thơ thứ 22 cho vấn đề này. Đối với giải pháp ngây thơ, chúng ta không cần chỉ ra rằng số 1 trong chuỗi nhiều nhất là O (log (n)), mà thay vào đó là nhiều nhất là O (sqrt (n * log (n)).

Người giải quyết

def solve(Str):
    indexes=[]
    #O(n) setup
    for i in range(len(Str)):
        if Str[i]=='1':
            indexes.append(i)

    #O((number of 1's)^2) processing
    for i in range(len(indexes)):
        for j in range(i+1, len(indexes)):
                            indexDiff = indexes[j] - indexes[i]
            k=indexes[j] + indexDiff
            if k<len(Str) and Str[k]=='1':
                return True
    return False

Về cơ bản, nó khá giống với ý tưởng và cách thực hiện của flybywire, mặc dù nhìn về phía trước thay vì quay lại.

Trình tạo chuỗi tham lam:

#assumes final char hasn't been added, and would be a 1 
def lastCharMakesSolvable(Str):
    endIndex=len(Str)
    j=endIndex-1
    while j-(endIndex-j) >= 0:
        k=j-(endIndex-j)
        if k >= 0 and Str[k]=='1' and Str[j]=='1':
            return True
        j=j-1
    return False



def expandString(StartString=''):
    if lastCharMakesSolvable(StartString):
        return StartString + '0'
    return StartString + '1'

n=1
BaseStr=""
lastCount=0
while n<1000000:
    BaseStr=expandString(BaseStr)
    count=BaseStr.count('1')
    if count != lastCount:
        print(len(BaseStr), count)
    lastCount=count
    n=n+1

(Để bảo vệ tôi, tôi vẫn đang trong giai đoạn 'tìm hiểu trăn')

Ngoài ra, đầu ra có khả năng hữu ích từ việc xây dựng các chuỗi tham lam, có một bước nhảy khá nhất quán sau khi đạt được sức mạnh 2 trong số 1 ... mà tôi không sẵn sàng chờ đợi để chứng kiến ​​vào năm 2096.

strlength   # of 1's
    1    1
    2    2
    4    3
    5    4
   10    5
   14    8
   28    9
   41    16
   82    17
  122    32
  244    33
  365    64
  730    65
 1094    128
 2188    129
 3281    256
 6562    257
 9842    512
19684    513
29525    1024

0

Tôi sẽ cố gắng trình bày một cách tiếp cận toán học. Đây là một khởi đầu hơn là kết thúc, vì vậy bất kỳ trợ giúp, bình luận, hoặc thậm chí mâu thuẫn - sẽ được đánh giá cao. Tuy nhiên, nếu cách tiếp cận này được chứng minh - thuật toán là một tìm kiếm đơn giản trong chuỗi.

  1. Đưa ra một số lượng không gian cố định kvà một chuỗi S, việc tìm kiếm một bộ ba khoảng cách k mất O(n)- Chúng tôi chỉ đơn giản kiểm tra cho mọi 0<=i<=(n-2k)nếu S[i]==S[i+k]==S[i+2k]. Bài kiểm tra thực hiện O(1)và chúng tôi thực hiện nó n-kở những nơi kkhông đổi, vì vậy nó cần O(n-k)=O(n).

  2. Chúng ta hãy giả sử rằng có một Tỷ lệ nghịch giữa số lượng 1và không gian tối đa chúng ta cần tìm kiếm. Đó là, nếu có nhiều 1, phải có một bộ ba và nó phải khá dày đặc; Nếu chỉ có một vài 1, Bộ ba (nếu có) có thể khá thưa thớt. Nói cách khác, tôi có thể chứng minh rằng nếu tôi có đủ 1, bộ ba như vậy phải tồn tại - và 1tôi càng có nhiều, một bộ ba dày đặc hơn phải được tìm thấy. Điều này có thể được giải thích bằng nguyên tắc Pigeonhole - Hy vọng sẽ giải thích về điều này sau.

  3. Nói có giới hạn ktrên về số lượng không gian có thể tôi phải tìm. Bây giờ, đối với từng 1nằm trong S[i]chúng ta cần phải kiểm tra 1trong S[i-1]S[i+1], S[i-2]S[i+2], ... S[i-k]S[i+k]. Điều này cần O((k^2-k)/2)=O(k^2)cho mỗi 1trong S- do Công thức Tổng kết Sê-ri của Gauss . Lưu ý rằng điều này khác với phần 1 - Tôi có kgiới hạn trên cho số lượng không gian, không phải là không gian không đổi.

Chúng ta cần chứng minh O(n*log(n)). Đó là, chúng ta cần chỉ ra rằng k*(number of 1's)tỷ lệ thuận với log(n).

Nếu chúng ta có thể làm điều đó, thuật toán là tầm thường - đối với mỗi người 1trong Schỉ số của họ i, chỉ cần tìm kiếm 1từ mỗi phía cho đến khoảng cách k. Nếu hai được tìm thấy trong cùng một khoảng cách, trở lại ik. Một lần nữa, phần khó khăn sẽ là tìm kiếm kvà chứng minh tính đúng đắn.

Tôi thực sự đánh giá cao ý kiến ​​của bạn ở đây - Tôi đã cố gắng tìm mối quan hệ giữa kvà số lượng 1trên bảng trắng của tôi, cho đến nay mà không thành công.


0

Giả thiết:

Chỉ sai, nói về log (n) số giới hạn trên của những cái

BIÊN TẬP:

Bây giờ tôi thấy rằng sử dụng số Cantor (nếu đúng), mật độ trên tập là (2/3) ^ Log_3 (n) (thật là một hàm lạ) và tôi đồng ý, mật độ log (n) / n là mạnh.

Nếu đây là giới hạn trên, có algorhitm giải quyết vấn đề này trong ít nhất O (n * (3/2) ^ (log (n) / log (3))) và độ phức tạp thời gian O ((3/2) ^ ( log (n) / log (3))) độ phức tạp không gian. (kiểm tra câu trả lời của Justice cho algorhitm)

Điều này vẫn còn tốt hơn nhiều so với O (n ^ 2)

Hàm này ((3/2) ^ (log (n) / log (3))) thực sự trông giống như n * log (n) ngay từ cái nhìn đầu tiên.

Làm thế nào tôi có được công thức này?

Applaying số Cantors trên chuỗi.
Giả sử độ dài của chuỗi là 3 ^ p == n
Ở mỗi bước trong thế hệ của chuỗi Cantor, bạn giữ 2/3 số lượng phổ biến của chuỗi. Áp dụng p lần này.

Điều đó có nghĩa là (n * ((2/3) ^ p)) -> (((3 ^ p)) * ((2/3) ^ p)) những cái còn lại và sau khi đơn giản hóa 2 ^ p. Điều này có nghĩa là 2 ^ p trong chuỗi 3 ^ p -> (3/2) ^ p. Thay thế p = log (n) / log (3) và get
((3/2) ^ (log (n) / log (3)))


Sai: Tập hợp Cantor có mật độ n ^ log_3 (2).
sdcvvc

0

Làm thế nào về một giải pháp O (n) đơn giản, với không gian O (n ^ 2)? (Sử dụng giả định rằng tất cả các toán tử bitwise hoạt động trong O (1).)

Thuật toán về cơ bản hoạt động theo bốn giai đoạn:

Giai đoạn 1: Đối với mỗi bit trong số ban đầu của bạn, hãy tìm hiểu khoảng cách của các bit đó, nhưng chỉ xem xét một hướng. (Tôi đã xem xét tất cả các bit theo hướng của bit có trọng số thấp nhất.)

Giai đoạn 2: Đảo ngược thứ tự của các bit trong đầu vào;

Giai đoạn 3: Chạy lại bước 1 trên đầu vào đảo ngược.

Giai đoạn 4: So sánh kết quả từ Giai đoạn 1 và Giai đoạn 3. Nếu bất kỳ bit nào có khoảng cách đều nhau ở trên VÀ bên dưới, chúng ta phải có một lần truy cập.

Hãy nhớ rằng không có bước nào trong thuật toán trên mất nhiều thời gian hơn O (n). ^ _ ^

Là một lợi ích bổ sung, thuật toán này sẽ tìm thấy TẤT CẢ các khoảng cách đều nhau từ MERYI số. Vì vậy, ví dụ: nếu bạn nhận được kết quả là "0x0005" thì sẽ có những khoảng cách đều nhau ở BÓNG 1 và 3 đơn vị

Tôi đã không thực sự thử tối ưu hóa mã bên dưới, nhưng mã C # có thể biên dịch được dường như hoạt động.

using System;

namespace ThreeNumbers
{
    class Program
    {
        const int uint32Length = 32;

        static void Main(string[] args)
        {
            Console.Write("Please enter your integer: ");
            uint input = UInt32.Parse(Console.ReadLine());

            uint[] distancesLower = Distances(input);
            uint[] distancesHigher = Distances(Reverse(input));

            PrintHits(input, distancesLower, distancesHigher);
        }

        /// <summary>
        /// Returns an array showing how far the ones away from each bit in the input.  Only 
        /// considers ones at lower signifcant bits.  Index 0 represents the least significant bit 
        /// in the input.  Index 1 represents the second least significant bit in the input and so 
        /// on.  If a one is 3 away from the bit in question, then the third least significant bit 
        /// of the value will be sit.
        /// 
        /// As programed this algorithm needs: O(n) time, and O(n*log(n)) space.  
        /// (Where n is the number of bits in the input.)
        /// </summary>
        public static uint[] Distances(uint input)
        {
            uint[] distanceToOnes = new uint[uint32Length];
            uint result = 0;

            //Sets how far each bit is from other ones. Going in the direction of LSB to MSB
            for (uint bitIndex = 1, arrayIndex = 0; bitIndex != 0; bitIndex <<= 1, ++arrayIndex)
            {
                distanceToOnes[arrayIndex] = result;
                result <<= 1;

                if ((input & bitIndex) != 0)
                {
                    result |= 1;
                }
            }

            return distanceToOnes;
        }

        /// <summary>
        /// Reverses the bits in the input.
        /// 
        /// As programmed this algorithm needs O(n) time and O(n) space.  
        /// (Where n is the number of bits in the input.)
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static uint Reverse(uint input)
        {
            uint reversedInput = 0;
            for (uint bitIndex = 1; bitIndex != 0; bitIndex <<= 1)
            {
                reversedInput <<= 1;
                reversedInput |= (uint)((input & bitIndex) != 0 ? 1 : 0);
            }

            return reversedInput;
        }

        /// <summary>
        /// Goes through each bit in the input, to check if there are any bits equally far away in 
        /// the distancesLower and distancesHigher
        /// </summary>
        public static void PrintHits(uint input, uint[] distancesLower, uint[] distancesHigher)
        {
            const int offset = uint32Length - 1;

            for (uint bitIndex = 1, arrayIndex = 0; bitIndex != 0; bitIndex <<= 1, ++arrayIndex)
            {
                //hits checks if any bits are equally spaced away from our current value
                bool isBitSet = (input & bitIndex) != 0;
                uint hits = distancesLower[arrayIndex] & distancesHigher[offset - arrayIndex];

                if (isBitSet && (hits != 0))
                {
                    Console.WriteLine(String.Format("The {0}-th LSB has hits 0x{1:x4} away", arrayIndex + 1, hits));
                }
            }
        }
    }
}

Ai đó có thể sẽ nhận xét rằng đối với bất kỳ số lượng đủ lớn nào, các thao tác bitwise có thể được thực hiện trong O (1). Bạn đã đúng. Tuy nhiên, tôi phỏng đoán rằng mọi giải pháp sử dụng phép cộng, phép trừ, phép nhân hoặc phép chia (không thể thực hiện bằng cách dịch chuyển) cũng sẽ có vấn đề đó.


0

Dưới đây là một giải pháp. Có thể có một số sai lầm nhỏ ở đây và đó, nhưng ý tưởng là âm thanh.

Chỉnh sửa: Không phải n * log (n)

MÃ PSEUDO:

foreach character in the string
  if the character equals 1 {         
     if length cache > 0 { //we can skip the first one
        foreach location in the cache { //last in first out kind of order
           if ((currentlocation + (currentlocation - location)) < length string)
              if (string[(currentlocation + (currentlocation - location))] equals 1)
                 return found evenly spaced string
           else
              break;
        }
     }
     remember the location of this character in a some sort of cache.
  }

return didn't find evenly spaced string

Mã C #:

public static Boolean FindThreeEvenlySpacedOnes(String str) {
    List<int> cache = new List<int>();

    for (var x = 0; x < str.Length; x++) {
        if (str[x] == '1') {
            if (cache.Count > 0) {
                for (var i = cache.Count - 1; i > 0; i--) {
                    if ((x + (x - cache[i])) >= str.Length)
                        break;

                    if (str[(x + (x - cache[i]))] == '1')
                        return true;                            
                }
            }
            cache.Add(x);                    
        }
    }

    return false;
}

Làm thế nào nó hoạt động:

iteration 1:
x
|
101101001
// the location of this 1 is stored in the cache

iteration 2:
 x
 | 
101101001

iteration 3:
a x b 
| | | 
101101001
//we retrieve location a out of the cache and then based on a 
//we calculate b and check if te string contains a 1 on location b

//and of course we store x in the cache because it's a 1

iteration 4:
  axb  
  |||  
101101001

a  x  b  
|  |  |  
101101001


iteration 5:
    x  
    |  
101101001

iteration 6:
   a x b 
   | | | 
101101001

  a  x  b 
  |  |  | 
101101001
//return found evenly spaced string

1
Thuật toán của bạn hoạt động, nhưng bạn cần chứng minh rằng nó nhỏ hơn O (n ^ 2). Phân tích tầm thường đưa bạn đến O (n ^ 2). Đối với mỗi 1, bạn đi qua tất cả các số 1 trước đó. Làm cho hàm phức tạp là 1 + 2 + 3 + ... + (k / 2-1) = O (k ^ 2) [trong đó k là số 1s].
Anna

Tôi đã kiểm tra và thực sự trường hợp xấu nhất không có giải pháp nào lớn hơn O (n * log (n))
Niek H.

0

Rõ ràng là chúng ta cần kiểm tra ít nhất một loạt các bộ ba cùng một lúc, vì vậy chúng ta cần phải nén các kiểm tra bằng cách nào đó. Tôi có một thuật toán ứng viên, nhưng phân tích độ phức tạp thời gian vượt quá khả năng * ngưỡng thời gian của tôi.

Xây dựng một cây trong đó mỗi nút có ba con và mỗi nút chứa tổng số 1 tại các lá của nó. Xây dựng một danh sách liên kết trên 1, là tốt. Chỉ định mỗi nút một chi phí được phép tỷ lệ thuận với phạm vi mà nó bao gồm. Miễn là thời gian chúng tôi dành cho mỗi nút nằm trong ngân sách, chúng tôi sẽ có thuật toán O (n lg n).

-

Bắt đầu từ gốc. Nếu bình phương của tổng số 1 dưới nó nhỏ hơn chi phí cho phép, hãy áp dụng thuật toán ngây thơ. Nếu không thì tái diễn trên con của nó.

Bây giờ chúng tôi đã trả lại trong ngân sách hoặc chúng tôi biết rằng không có bộ ba hợp lệ nào hoàn toàn được chứa trong một trong những đứa trẻ. Do đó, chúng ta phải kiểm tra các bộ ba nút.

Bây giờ mọi thứ trở nên vô cùng lộn xộn. Về cơ bản, chúng tôi muốn tái diễn các nhóm trẻ tiềm năng trong khi giới hạn phạm vi. Ngay khi phạm vi bị hạn chế đủ để thuật toán ngây thơ sẽ chạy theo ngân sách, bạn thực hiện nó. Thích thực hiện điều này, bởi vì tôi đảm bảo nó sẽ tẻ nhạt. Có hàng tá trường hợp.

-

Lý do tôi nghĩ rằng thuật toán sẽ hoạt động là vì các chuỗi không có bộ ba hợp lệ dường như thay thế xen kẽ giữa các bó 1 và rất nhiều 0. Nó phân chia hiệu quả không gian tìm kiếm gần đó và cây mô phỏng sự phân tách đó.

Thời gian chạy của thuật toán là không rõ ràng, tất cả. Nó dựa vào các tính chất không tầm thường của chuỗi. Nếu số 1 thực sự thưa thớt thì thuật toán ngây thơ sẽ hoạt động theo ngân sách. Nếu số 1 dày đặc, thì trận đấu sẽ được tìm thấy ngay lập tức. Nhưng nếu mật độ là 'vừa phải' (ví dụ: gần ~ n ^ 0,63, bạn có thể đạt được bằng cách đặt tất cả các bit ở các vị trí không có chữ số '2' trong cơ sở 3), tôi không biết liệu nó có hoạt động không. Bạn sẽ phải chứng minh rằng hiệu ứng tách là đủ mạnh.


0

Không có câu trả lời lý thuyết nào ở đây, nhưng tôi đã viết một chương trình Java nhanh để khám phá hành vi thời gian chạy dưới dạng hàm của k và n, trong đó n là tổng chiều dài bit và k là số 1. Tôi có một vài người trả lời đang nói rằng thuật toán "thông thường" kiểm tra tất cả các cặp vị trí bit và tìm kiếm bit thứ 3, mặc dù nó sẽ yêu cầu O (k ^ 2) trong trường hợp xấu nhất, trong trường hợp xấu nhất thực tế bởi vì trường hợp xấu nhất cần bitstrings thưa thớt, là O (n ln n).

Dù sao đây là chương trình, dưới đây. Đó là chương trình kiểu Monte-Carlo chạy một số lượng lớn các thử nghiệm NTRIALS cho hằng số n và tạo ngẫu nhiên các bit cho một phạm vi giá trị k sử dụng các quy trình Bernoulli với mật độ bị ràng buộc giữa các giới hạn có thể được chỉ định và ghi lại thời gian chạy về việc tìm hoặc không tìm thấy bộ ba khoảng cách đều nhau, thời gian được đo theo các bước KHÔNG theo thời gian của CPU. Tôi đã chạy nó trong n = 64, 256, 1024, 4096, 16384 * (vẫn đang chạy), đầu tiên là chạy thử với 500000 thử nghiệm để xem giá trị k nào mất thời gian chạy lâu nhất, sau đó thử nghiệm khác với 5000000 thử nghiệm với các thử nghiệm bị thu hẹp- mật độ tập trung để xem những giá trị đó trông như thế nào. Thời gian chạy dài nhất xảy ra với mật độ rất thưa thớt (ví dụ: với n = 4096, các đỉnh thời gian chạy nằm trong phạm vi k = 16-64, với một đỉnh nhẹ cho thời gian chạy trung bình ở 4212 bước @ k = 31, thời gian chạy tối đa đạt cực đại ở 5101 bước @ k = 58). Có vẻ như sẽ cần các giá trị N cực lớn cho bước O (k ^ 2) trong trường hợp xấu nhất để trở nên lớn hơn bước O (n) trong đó bạn quét chuỗi bit để tìm chỉ số vị trí của 1.

package com.example.math;

import java.io.PrintStream;
import java.util.BitSet;
import java.util.Random;

public class EvenlySpacedOnesTest {
    static public class StatisticalSummary
    {
        private int n=0;
        private double min=Double.POSITIVE_INFINITY;
        private double max=Double.NEGATIVE_INFINITY;
        private double mean=0;
        private double S=0;

        public StatisticalSummary() {}
        public void add(double x) {
            min = Math.min(min, x);
            max = Math.max(max, x);
            ++n;
            double newMean = mean + (x-mean)/n;
            S += (x-newMean)*(x-mean);
            // this algorithm for mean,std dev based on Knuth TAOCP vol 2
            mean = newMean;
        }
        public double getMax() { return (n>0)?max:Double.NaN; }
        public double getMin() { return (n>0)?min:Double.NaN; }
        public int getCount() { return n; }
        public double getMean() { return (n>0)?mean:Double.NaN; }
        public double getStdDev() { return (n>0)?Math.sqrt(S/n):Double.NaN; } 
        // some may quibble and use n-1 for sample std dev vs population std dev    
        public static void printOut(PrintStream ps, StatisticalSummary[] statistics) {
            for (int i = 0; i < statistics.length; ++i)
            {
                StatisticalSummary summary = statistics[i];
                ps.printf("%d\t%d\t%.0f\t%.0f\t%.5f\t%.5f\n",
                        i,
                        summary.getCount(),
                        summary.getMin(),
                        summary.getMax(),
                        summary.getMean(),
                        summary.getStdDev());
            }
        }
    }

    public interface RandomBernoulliProcess // see http://en.wikipedia.org/wiki/Bernoulli_process
    {
        public void setProbability(double d);
        public boolean getNextBoolean();
    }

    static public class Bernoulli implements RandomBernoulliProcess
    {
        final private Random r = new Random();
        private double p = 0.5;
        public boolean getNextBoolean() { return r.nextDouble() < p; }
        public void setProbability(double d) { p = d; }
    }   
    static public class TestResult {
        final public int k;
        final public int nsteps;
        public TestResult(int k, int nsteps) { this.k=k; this.nsteps=nsteps; } 
    }

    ////////////
    final private int n;
    final private int ntrials;
    final private double pmin;
    final private double pmax;
    final private Random random = new Random();
    final private Bernoulli bernoulli = new Bernoulli();
    final private BitSet bits;
    public EvenlySpacedOnesTest(int n, int ntrials, double pmin, double pmax) {
        this.n=n; this.ntrials=ntrials; this.pmin=pmin; this.pmax=pmax;
        this.bits = new BitSet(n);
    }

    /*
     * generate random bit string
     */
    private int generateBits()
    {
        int k = 0; // # of 1's
        for (int i = 0; i < n; ++i)
        {
            boolean b = bernoulli.getNextBoolean();
            this.bits.set(i, b);
            if (b) ++k;
        }
        return k;
    }

    private int findEvenlySpacedOnes(int k, int[] pos) 
    {
        int[] bitPosition = new int[k];
        for (int i = 0, j = 0; i < n; ++i)
        {
            if (this.bits.get(i))
            {
                bitPosition[j++] = i;
            }
        }
        int nsteps = n; // first, it takes N operations to find the bit positions.
        boolean found = false;
        if (k >= 3) // don't bother doing anything if there are less than 3 ones. :(
        {       
            int lastBitSetPosition = bitPosition[k-1];
            for (int j1 = 0; !found && j1 < k; ++j1)
            {
                pos[0] = bitPosition[j1];
                for (int j2 = j1+1; !found && j2 < k; ++j2)
                {
                    pos[1] = bitPosition[j2];

                    ++nsteps;
                    pos[2] = 2*pos[1]-pos[0];
                    // calculate 3rd bit index that might be set;
                    // the other two indices point to bits that are set
                    if (pos[2] > lastBitSetPosition)
                        break;
                    // loop inner loop until we go out of bounds

                    found = this.bits.get(pos[2]);
                    // we're done if we find a third 1!
                }
            }
        }
        if (!found)
            pos[0]=-1;
        return nsteps;
    }

    /*
     * run an algorithm that finds evenly spaced ones and returns # of steps.
     */
    public TestResult run()
    {
        bernoulli.setProbability(pmin + (pmax-pmin)*random.nextDouble());
        // probability of bernoulli process is randomly distributed between pmin and pmax

        // generate bit string.
        int k = generateBits();
        int[] pos = new int[3];
        int nsteps = findEvenlySpacedOnes(k, pos);
        return new TestResult(k, nsteps); 
    }

    public static void main(String[] args)
    {
        int n;
        int ntrials;
        double pmin = 0, pmax = 1;
        try {
            n = Integer.parseInt(args[0]);
            ntrials = Integer.parseInt(args[1]);
            if (args.length >= 3)
                pmin = Double.parseDouble(args[2]);
            if (args.length >= 4)
                pmax = Double.parseDouble(args[3]);
        }
        catch (Exception e)
        {
            System.out.println("usage: EvenlySpacedOnesTest N NTRIALS [pmin [pmax]]");
            System.exit(0);
            return; // make the compiler happy
        }

        final StatisticalSummary[] statistics;
        statistics=new StatisticalSummary[n+1];
        for (int i = 0; i <= n; ++i)
        {
            statistics[i] = new StatisticalSummary();
        }

        EvenlySpacedOnesTest test = new EvenlySpacedOnesTest(n, ntrials, pmin, pmax);
        int printInterval=100000;
        int nextPrint = printInterval;
        for (int i = 0; i < ntrials; ++i)
        {
            TestResult result = test.run();
            statistics[result.k].add(result.nsteps);
            if (i == nextPrint)
            {
                System.err.println(i);
                nextPrint += printInterval;
            }
        }
        StatisticalSummary.printOut(System.out, statistics);
    }
}

0
# <algorithm>
def contains_evenly_spaced?(input)
  return false if input.size < 3
  one_indices = []
  input.each_with_index do |digit, index|
    next if digit == 0
    one_indices << index
  end
  return false if one_indices.size < 3
  previous_indexes = []
  one_indices.each do |index|
    if !previous_indexes.empty?
      previous_indexes.each do |previous_index|
        multiple = index - previous_index
        success_index = index + multiple
        return true if input[success_index] == 1
      end
    end
    previous_indexes << index
  end
  return false
end
# </algorithm>

def parse_input(input)
  input.chars.map { |c| c.to_i }
end

Tôi gặp rắc rối với các tình huống xấu nhất với hàng triệu chữ số. Rối rắm từ /dev/urandomvề cơ bản mang lại cho bạn O (n), nhưng tôi biết trường hợp xấu nhất còn tệ hơn thế. Tôi chỉ không thể nói xấu hơn bao nhiêu. Đối với nhỏ n, việc tìm kiếm đầu vào xung quanh là chuyện nhỏ 3*n*log(n), nhưng thật khó để phân biệt chúng với một số thứ tự tăng trưởng khác cho vấn đề cụ thể này.

Bất cứ ai đang làm việc trên các đầu vào trong trường hợp xấu nhất có thể tạo ra một chuỗi có độ dài lớn hơn một trăm nghìn không?


Như tôi đã chỉ ra trong câu trả lời của mình, thật dễ dàng để tạo ra các chuỗi xấu (mặc dù không phải là trường hợp xấu nhất) với bất kỳ số chữ số nào: đặt 1s vào chính xác các vị trí p không chứa bất kỳ "1" nào trong biểu diễn tạm thời của chúng (ví dụ tại vị trí 2, 6, 8, 18, 20, 24, 26, 54, 56, 60 ...: xem các công thức tại Research.att.com/~njas/ Hậuences / trên). Với 3 ^ 13 ≈ 1 triệu, cái này có 2 ^ 13 8000 1s. Thời gian chạy trên các chuỗi như vậy sẽ là ≈ n ^ (1.26) - có thể vẫn khó phân biệt với O (n log n) cho n nhỏ như vậy. Hãy thử nó và xem.
ShreevatsaR


-3

Đây có thể là một giải pháp? Tôi ', không chắc đó có phải là O (nlogn) hay không nhưng theo tôi thì tốt hơn O (n²) bởi vì cách duy nhất để không tìm thấy bộ ba sẽ là phân phối số nguyên tố.

Có chỗ để cải thiện, cái thứ hai tìm thấy 1 có thể là cái đầu tiên tiếp theo 1. Cũng không có kiểm tra lỗi.

#include <iostream>

#include <string>

int findIt(std::string toCheck) {
    for (int i=0; i<toCheck.length(); i++) {
        if (toCheck[i]=='1') {
            std::cout << i << ": " << toCheck[i];
            for (int j = i+1; j<toCheck.length(); j++) {
                if (toCheck[j]=='1' && toCheck[(i+2*(j-i))] == '1') {
                    std::cout << ", " << j << ":" << toCheck[j] << ", " << (i+2*(j-i)) << ":" << toCheck[(i+2*(j-i))] << "    found" << std::endl;
                    return 0;
                }
            }
        }
    }
    return -1;
}

int main (int agrc, char* args[]) {
    std::string toCheck("1001011");
    findIt(toCheck);
    std::cin.get();
    return 0;
}

1
Về mặt kỹ thuật đây là O (n ^ 2). Trung bình vòng lặp bên trong sẽ lặp lại hơn một nửa n mỗi lần nó được chạy. Vì vậy, nó có thể được viết là O (n * (n / 2)) và có thể được đơn giản hóa thành O (n ^ 2)
Robert Parker

Hừm, có vẻ như bạn đúng. Đây không phải là một vấn đề đơn giản, chỉ để tìm tất cả 1 mất O (n), không có nhiều chỗ cho bất kỳ tìm kiếm / so sánh nào với độ phức tạp O (logn).
DaClown

-3

Tôi nghĩ thuật toán này có độ phức tạp O (n log n) (C ++, DevStudio 2k5). Bây giờ, tôi không biết chi tiết về cách phân tích thuật toán để xác định độ phức tạp của nó, vì vậy tôi đã thêm một số thông tin thu thập số liệu vào mã. Mã này đếm số lượng thử nghiệm được thực hiện trên chuỗi 1 và 0 cho bất kỳ đầu vào cụ thể nào (hy vọng, tôi đã không tạo ra một quả bóng của thuật toán). Chúng ta có thể so sánh số lượng thử nghiệm thực tế với giá trị O và xem liệu có mối tương quan nào không.

#include <iostream>
using namespace std;

bool HasEvenBits (string &sequence, int &num_compares)
{
  bool
    has_even_bits = false;

  num_compares = 0;

  for (unsigned i = 1 ; i <= (sequence.length () - 1) / 2 ; ++i)
  {
    for (unsigned j = 0 ; j < sequence.length () - 2 * i ; ++j)
    {
      ++num_compares;
      if (sequence [j] == '1' && sequence [j + i] == '1' && sequence [j + i * 2] == '1')
      {
        has_even_bits = true;
        // we could 'break' here, but I want to know the worst case scenario so keep going to the end
      }
    }
  }

  return has_even_bits;
}

int main ()
{
  int
    count;

  string
    input = "111";

  for (int i = 3 ; i < 32 ; ++i)
  {
    HasEvenBits (input, count);
    cout << i << ", " << count << endl;
    input += "0";
  }
}

Chương trình này xuất ra số lượng kiểm tra cho mỗi chuỗi dài tối đa 32 ký tự. Đây là kết quả:

 n  Tests  n log (n)
=====================
 3     1     1.43
 4     2     2.41
 5     4     3.49
 6     6     4.67
 7     9     5.92
 8    12     7.22
 9    16     8.59
10    20    10.00
11    25    11.46
12    30    12.95
13    36    14.48
14    42    16.05
15    49    17.64
16    56    19.27
17    64    20.92
18    72    22.59
19    81    24.30
20    90    26.02
21   100    27.77
22   110    29.53
23   121    31.32
24   132    33.13
25   144    34.95
26   156    36.79
27   169    38.65
28   182    40.52
29   196    42.41
30   210    44.31
31   225    46.23

Tôi cũng đã thêm các giá trị log n log n '. Vẽ sơ đồ này bằng cách sử dụng công cụ đồ họa của bạn để xem mối tương quan giữa hai kết quả. Phân tích này có mở rộng đến tất cả các giá trị của n không? Tôi không biết.


Nó không phải là một mối tương quan hoàn hảo, tôi đồng ý. Tuy nhiên, đường cong gần với n log n hơn so với n ^ 2.
Skizz

3
Hãy thử bơm kích thước đầu vào lên đến một triệu hoặc nhiều hơn. Ở các đầu vào nhỏ, đường cong thường trông tương tự như các đường cong của thuật toán rõ ràng là tốt hơn khi kích thước đầu vào được bơm lên.
Nick Larsen

Một vòng lặp đôi với vòng trong được giới hạn bởi vòng ngoài tạo nên hình tam giác, vẫn là O (n ^ 2) về độ phức tạp. Hãy nghĩ về tất cả (i, j) sao cho i trong [0, n] và j trong [0, n-2 * i], bạn có một hình tam giác và diện tích của một hình tam giác có xu hướng bậc hai.
Matthieu M.

Nói chính xác, Tests = (n ^ 2-2n) / 4 cho chẵn n; rõ ràng là bậc hai.
Ông
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.