O (nlogn) Algorithm-이진 문자열 내에서 균등 한 간격으로 3 개 찾기


173

어제 알고리즘 테스트 에서이 질문을 받았으며 답을 알 수 없습니다. 그것은 약 40 점의 가치가 있었기 때문에 나를 절대적으로 미치게합니다. 지난 24 시간 동안 해결책을 찾지 못했기 때문에 대부분의 수업이 올바르게 해결되지 않았다고 생각합니다.

길이가 n 인 임의의 이진 문자열이 주어지면 문자열 내에서 3 개의 균일 한 간격이있는 문자열을 찾으십시오. 이것을 O (n * log (n)) 시간 안에 해결하는 알고리즘을 작성하십시오.

따라서 이와 같은 문자열에는 "균등 간격"인 세 개의 문자열이 있습니다 : 11100000, 0100100100

편집 : 임의의 숫자이므로 모든 숫자에서 작동 할 수 있어야합니다. 내가 제공 한 예는 "균등 간격"속성을 설명하는 것이 었습니다. 따라서 1001011은 유효한 숫자입니다. 1, 4, 7은 균등 한 간격입니다.


4
다음이 가능합니까 : 10011010000? 균등 한 간격으로 세 개의 1 (첫 번째, 두 번째, 네 번째)이 있지만 추가 1도 있습니다.
Anna

5
Robert, 교수님이 이에 대한 답변을하도록하고 여기에 게시해야합니다. 이 문제는 나를 벽으로 이끌고있다. n ^ 2에서는 수행하는 방법을 알 수 있지만 n * log (n)는 아닙니다.
James McMahon

3
흠 나는 이것을 알아 내려고 오랜 시간을 보냈지 만 아직 좋은 대답을 얻지 못했습니다. 아마도 당신은 그 질문을 오해했을 것입니까? 예를 들어, 질문이 묻 으면 간격이 균등 한 간격 k의 시퀀스를 훨씬 더 큰 시퀀스로 결정하는 O (n log n)에서 실행되는 알고리즘을 찾으십시오. 이는 빠른 푸리에 변환을 사용하여 쉽게 수행 할 수 있습니다.
ldog

2
전문가가 해결책을 제시하면 답변으로 게시하십시오.
ldog

5
클라우스 로스 (Klaus Roth)가 각기 다른 밀도 d> 0에 대해 자연수 N이 있다는 것을 증명하기 위해 1958 년 Fields Medal을 받았다는 사실을 고려할 때, 적어도 d *를 가진 {1, ..., N}의 각 부분 집합 N 요소에는 길이 3의 산술 진행이 포함되어 있습니다. 지금까지 아무도 문제에 대한 설득력있는 알고리즘을 찾지 못했습니다. 참조 en.wikipedia.org/wiki/Szemer%C3%A9di%27s_theorem
JP

답변:


128

드디어! sdcvvc 's answer 에서 리드를 수행 하면 문제에 대한 O (n log n) 알고리즘이 있습니다! 이해 한 후에도 간단합니다. FFT를 추측 한 사람들이 옳았습니다.

문제 : S길이가 n 인 이진 문자열이 주어지고 그 안에 3 개의 고른 간격으로 1을 찾으려고합니다. 예를 들면, S일 수있다 110110010, 여기서, N = 9. 위치 2, 5 및 8에서 1을 균등하게 배치했습니다.

  1. S왼쪽에서 오른쪽으로 스캔 하고 L1의 위치 목록 을 만듭니다. S=110110010위의 경우 L = [1, 2, 4, 5, 8] 목록이 있습니다. 이 단계는 O (n)입니다. 문제는 발견 지금 길이 3의 등차 수열 하여 L별개의 발견, 즉 A, B, C를L되도록 BA = CB 또는 등가 A + C = 2B . 위의 예에서는 진행률 (2, 5, 8)을 찾고 싶습니다.

  2. k in 에 대해 x k 항을 사용 하여 다항식 p 을 만듭니다 . 위의 예에서는 다항식 p (x) = (x + x 2 + x 4 + x 5 + x 8 )로 만듭니다. 이 단계는 O (n)입니다.L

  3. 고속 푸리에 변환을 사용하여 다항식 q= p 2를 찾습니다 . 위의 예에서, 우리는 다항식 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를 얻습니다 . 이 단계는 O (n log n)입니다.

  4. 일부 k in에 대해 x 2k 에 해당하는 용어를 제외한 모든 항을 무시하십시오 . 위의 예에서 우리는 x 16 , 3x 10 , x 8 , x 4 , x 2 항을 얻습니다 . 이 단계는 O (n)입니다.L

여기서 중요한 점이다 : 임의의 계수 X에 2B 에 대한 B 의이 L있다 정확하게 쌍의 수 (a, c)L되도록 A + C = 2B . [CLRS, 예. 30.1-7] 하나의 이러한 쌍은 (b, b) 는 항상 (계수는 적어도 1 정도)이지만 다른 쌍 존재하는 경우 (a, c)를 , 다음 계수로부터, 적어도 3 (a, C )(c, a) . 위의 예 에서 AP (2,5,8) 때문에 x 10 의 계수는 정확하게 3입니다. (이 계수 x 2b위의 이유로 항상 홀수입니다. q의 다른 모든 계수는 항상 짝수입니다.)

따라서 알고리즘은 이러한 항 x 2b 의 계수를보고 그 중 하나가 1보다 큰지 확인합니다. 없으면 1이 고르지 않습니다. 이 경우 이다 BL계수되는 X (2B)가 1보다 크면, 그때 우리는 어떤 쌍 있다는 것을 알고 (a, c) 이외 - (B, B)에 있는 - A + C = 2b는 . 실제 쌍을 찾으려면 각 a in L(해당 c2b-a 일 것입니다)을 시도 하고 위치 2b-a 에서 1이 있는지 확인하십시오 S. 이 단계는 O (n)입니다.

그게 다야 사람들.


FFT를 사용해야합니까? beta 's , flybywire 'srsp ' s 와 같은 많은 답변은 각 쌍의 1을 확인하고 "제 3"위치에 1이 있는지 확인하는 방법은 직관에 따라 O (n log n)에서 작동 할 수 있다고 제안합니다. 1이 너무 많으면 트리플이 쉽게 발견되고 1이 너무 적 으면 모든 쌍을 확인하는 데 시간이 거의 걸리지 않습니다. 불행하게도,이 직관은 정확하고 간단한 접근법 O (n 2 ) 보다 낫지 만 , 크게 나아지지는 않습니다. 마찬가지로 sdcvvc의 대답 , 우리는 길이의 문자열 "세트 캔터 같은"취할 수 N = 3 케이삼항 표현이 0과 2 (1은 없음) 인 위치에 1이 있습니다. 이러한 문자열에는 2 k = n (log 2) / (log 3) ≈ n 0.63이 있고 균등 한 간격이 1이 없으므로 모든 쌍을 확인하는 것은 1의 수의 제곱의 순서입니다. 불행히도 (n log n)보다 훨씬 더 큰 4 k ≈ n 1.26 . 1953 년 레오 모서 : 사실, 최악의 경우는 더욱 심각하다 구성 (효과적으로)가 같은 문자열 N 1-C / √ (로그 n)을 그들의 1S하지만 균등 1S, 어떤 의미 같은 문자열에서 단순 접근에는 Θ (n 2-2c / √ (log n) )가 필요합니다.- 만 작은 더 이상의 비트 Θ (N 2 ) , 놀랍게도!


길이의 문자열 1S의 최대 개수에 대한 N과 함께, 우리는 적어도 상기보고되었다 NO 3 균등 것들은 없다 ( N 0.63 쉬운 칸토어 형 구조 및 상기 적어도에서 N 1-C / √ (로그 N)를 가진 모저의 구성)-이것은 OEIS A003002 입니다. 또한 A065825 (k) ≤ n <A065825 (k + 1)가 되도록 OEIS A065825 에서 k로 직접 계산할 수도 있습니다 . 나는 이것을 찾기위한 프로그램을 작성했으며 욕심 많은 알고리즘이 그러한 문자열을 가장 길게 제공 하지 않는다는 것이 밝혀졌습니다 . 예를 들어, n = 9의 경우 5 1s (110100011)를 얻을 수 있지만 욕심쟁이는 n 에 대해 4 (110110000) 만 제공합니다.= 26 우리는 11 1s (11001010001000010110001101)를 얻을 수 있지만 욕심쟁이는 8 (11011000011011000000000000)을 제공하고 n = 74이면 22 1s (11000010110001000001011010001000000000000000010001011010000010001101000011)를 얻을 수 있지만 욕심쟁이는 16 (110110000110110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000110000000100000110110000000100,000) 그러나 그들은 50 세가 될 때까지 (예 : 38에서 50까지) 꽤 많은 곳에서 동의합니다. OEIS 참고 자료에 따르면 Jaroslaw Wroblewski는이 질문에 관심이있는 것으로 보이며 이러한 비 평균 세트 에 대한 웹 사이트를 유지 관리 합니다 . 정확한 숫자는 194까지만 알려져 있습니다.


27
아주 좋아요 감동적인. 누군가 테스트에서 이것을 생각 해낼 것으로 예상되는 것 같습니다.
hughdbrown

4
문제를 AP 찾기로 번역하는 1 단계는 간단합니다. 다항식에 O (n log n) 시간을 곱할 수있는 3 단계는 사실입니다. 실제 트릭과 문제를 어렵게 만드는 것은 11011을 계수 [1,1,0,1,1] 등의 다항식으로 생각하는 아이디어입니다. 이것은 영리하고 종종 유용한 아이디어입니다. 오일러로 돌아가는 길. [현대 박람회에 대한 Wilf의 멋진 책 "generatingfunctionology"참조 : math.upenn.edu/~wilf/DownldGF.html ] 따라서 학생들이 최근 메모리에서 함수를 생성했는지 여부에 따라 달라집니다. :-)
ShreevatsaR

2
계산이 잘못되어 죄송합니다. 110110010 ^ 2 = 12124214302200100이어야합니다. 그러나 아이디어는 유효합니다. 그냥 (3)의 위치주의
기예르모 필립스

11
매우 인상적. 이 스레드 / 질문이 모여서 해결책을 찾는 것이 정말 좋습니다. 나는 그것이 불가능하다고 생각하기 시작했다. 또한이 교수는 사악합니다.
KingNestor

1
@RexE : p가 n-1도 (n 개의 항을 가짐)이면 q = p ^ 2는 2n-2도 (최대 2n-1 항을 가짐)입니다. n ^ 2는 어떻게 얻었습니까? (또한 FFT를 사용하여 O (n log n) 시간에 n의 두 개의 다항식을 곱하는 것은 꽤 표준적인 작업입니다. 답변의 링크를 클릭하거나 Wikipedia 기사를 참조하십시오 .
ShreevatsaR

35

이 백서 (1999) 에서는 문제를 평균이라고합니다 .

문제 3SUM에서 이차 감소가있는 경우 문제는 3SUM-hard입니다. n 개의 정수 세트 A가 주어지면 A에 a + b + c = 0이되도록 요소 a, b, c가 있습니까? AVERAGE가 3SUM 하드인지는 알려져 있지 않습니다. 그러나 AVERAGE에서 3SUM으로의 간단한 선형 시간 단축이 있으며 그 설명은 생략합니다.

위키 백과 :

정수가 [-u ... u] 범위에있을 때, S를 비트 벡터로 나타내고 FFT를 사용하여 컨볼 루션을 수행함으로써 3SUM은 시간 O (n + u lg u)에서 풀 수 있습니다.

이것은 문제를 해결하기에 충분합니다 :).

무엇 매우 중요한 것은 (N 로그 n)은 0과의 수로 복잡성이 O이고, 사람의하지 카운트 (같은 배열로 제공 될 수있다 [1,5,9,15]). 세트에 산술 진행이 있는지 확인하는 것은 1의 수라는 용어가 어렵고 1999 년 현재 그 논문에 따르면 O (n 2 ) 보다 빠른 알고리즘 은 알려져 있지 않으며 존재하지 않는 것으로 추측됩니다. 이것을 고려하지 않은 모든 사람들은 공개 된 문제를 해결하려고합니다.

관련이없는 다른 흥미로운 정보 :

하한:

쉬운 하한은 캔터와 같은 세트 (3 진수 확장에 1을 포함하지 않는 1.1.3 ^ n-1의 숫자)입니다. 밀도는 n ^ (log_3 2)입니다 (0.631 경). 따라서 집합이 너무 크지 않은지 확인한 다음 모든 쌍을 확인하는 것만으로는 O (n log n)를 얻을 수 없습니다. 시퀀스를 더 똑똑하게 조사해야합니다. 더 나은 하한이 여기 에 인용 됩니다 -n 1-c / (log (n)) ^ (1/2) 입니다. 이것은 캔터 세트가 최적 이 아님을 의미 합니다.

상한-내 오래된 알고리즘 :

큰 n의 경우, 산술 진행을 포함하지 않는 {1,2, ..., n}의 서브 세트는 최대 n / (log n) ^ (1/20) 요소를 갖는 것으로 알려져 있습니다. 산술 진행의 트리플에서 종이 는 더 많은 것을 입증합니다. 세트는 n * 2 28 * (log log n / log n) 1/2 요소를 초과 할 수 없습니다 . 따라서 해당 범위가 달성되었는지 확인하고 그렇지 않은 경우 순진하게 쌍을 확인하십시오. 이것은 O (n 2 ) 보다 빠른 O (n 2 * log log n / log n) 알고리즘 입니다. 불행히도 "On triples ..."은 Springer에 있지만 첫 페이지를 볼 수 있으며 Ben Green의 설명은 28 페이지, 정리 24 페이지에서 볼 수 있습니다 .

그건 그렇고, 논문은 1999 년부터 처음 언급 한 것과 같은 해이므로 첫 번째 논문이 그 결과를 언급하지 않은 이유 일 것입니다.


2
큰 문제는,이 문제에 대해 결정적인 내용이 처음입니다. 따라서 Cantor와 같은 세트의 n ^ 0.63 1s 는 최악의 경우 "1 쌍의 모든 쌍 확인"알고리즘이 n ^ 1.26 (≫ n log n) 이상 임을 의미합니다 . Szemeredi의 논문에 인용 된 하한 (BTW가 인용 한 Moser 논문은 여기에서 볼 수 있습니다 : books.google.com/books?id=Cvtwu5vVZF4C&pg=PA245 ) 실제로 n ^ (2-o (1))을 암시하는 것처럼 보이지만 {1, ..., n}에서 가져온 숫자가 있기 때문에 약간주의해야합니다. 그러나 여기에는 n의 시퀀스에있는 숫자 의 이 있습니다.
ShreevatsaR

어, n ^ (log_3 2) 1을 포함하고 세 개의 균등 한 1을 포함하지 않는 "Cantor-like"이진 시퀀스는 정확히 무엇입니까?
ShreevatsaR

예 : 101000101000000000101000101. 길이는 3 ^ n이며 길이는 2 ^ n입니다 (따라서 n ^ 0.63 밀도). 1의 자리를 이진수로 기록하면 {0,2,20,22,200,202,220,222}가됩니다. 이것을 생각할 수있는 또 다른 방법은 일련의 순서를 취하고 정상적인 Cantor 세트 구성 에서처럼 "중간"을 제거하는 것입니다 : 111111111-> 111000111-> 101000101. 산술 진행을 포함하지 않는 이유는 다음과 같습니다. , y, z는 1을 형성 한 다음 y = (x + z) / 2와 x와 z는 일부 확장 위치에서 다릅니다. 가장 중요한 것을 취하십시오. x에 0이 있고 z에 2가 있다고 가정하면 y에 1이 있어야합니다. 모순.
sdcvvc

3
다시 한 번 훌륭한 연구! 나는 2008 년 3SUM 논문에 이어 CLRS Exercise를 언급했다. 30.1-7, 내가 답을 찾은 후 O (n log n) 알고리즘은 실제로 매우 간단합니다! (다항식 / 생성 함수를 제곱하면됩니다.) 아래에 답변을 게시했습니다. (이제 이전 ... 간단한 솔루션은 항상 반응을 이끌어 그것의 생각을 가지고 있지 자신을 발로 : P)
ShreevatsaR

따라서 그의 시험 문제에 대한 답은 "이 문제는 3-SUM 어려운 문제로 환원 될 수 있으며 3-SUM hard는 이차 해결책이 없기 때문에이 문제는 O (n logn)에서 해결할 수 없습니다. " 예?
hughdbrown

8

이것은 해결책이 아니라 Olexiy가 생각한 것과 비슷한 생각입니다.

나는 최대 수의 시퀀스를 생성하면서 놀고 있었고, 모두 매우 흥미 롭습니다. 최대 125 자리를 얻었으며 가능한 한 많은 '1'비트를 삽입하려고 시도한 첫 3 개의 숫자입니다.

  • 11011000011011000000000000001101100001101100000000000000000000000000000000000000000000000110110000110110000000000000011011000011011
  • 10110100010110100000000000010110100010110100000000000000000000000000000000000000000000000101101000101101000000000000101101000101101
  • 10011001010011001000000000010011001010011001000000000000000000000000000000000000000000010011001010011001000000000010011001010011001

그것들은 모두 프랙탈입니다 (제한이 주어지면 너무 놀랍지 않습니다). 문자열이 특성을 가진 프랙탈 이 아닌 경우 거꾸로 생각하는 것이있을 수 있습니다 . 반복적 인 패턴이 있어야합니까?

이 숫자를 설명하는 더 나은 용어에 대한 베타 덕분입니다.

최신 정보: 아아, 그것은 충분히 큰 초기 문자열로 시작할 때 패턴이 무너지는 것처럼 보입니다 : 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
이런 * @ !!, 이들은 FRACTALS입니다! 이것이 유지되면 1의 수에 상한을 두며 O (n)보다 작습니다.
Beta

프랙탈, 그것은 그것들을 설명하기에 훨씬 좋은 용어입니다. 감사합니다
z-

흥미롭게도 이러한 패턴은 Cantor의 3 진 세트 ( en.wikipedia.org/wiki/Cantor_set ) 와 매우 유사 합니다. 만약 그렇다면, 1의 비율은 0이되는 경향이 있습니다.
flybywire

트리플이없는 최대 개수 1의 시퀀스가 ​​알고리즘의 최악의 실행 시간과 직접 관련이 있다는 것이 명백합니까? 1이 많은 문자열을 가질 수 있지만 트리플이 매우 늦게 발견되는 경우가 있습니다. 1은 알고리즘에서 늦게 검사하는 위치에 있기 때문입니다.
ShreevatsaR

3
문자열의 전체 수와 비교하여 문자열의 수에 대한 나의 분석은 1의 수와 문자열의 크기 사이에 선형 관계가 있음을 나타내는 것으로 보이며, 우리가 말할 수있는 행복한 상한이 없다는 것을 믿게합니다. 문자열에서 하나의 숫자는 주어진 문자열에 대해 최대 log (n)입니다. 따라서 전체 문자열 자체가 아닌 위치를 처리하는 솔루션도 O (n ^ 2)가됩니다. 또는보다 정확하게 말하면 O (n + m ^ 2)입니다. 여기서 m은 문자열의 숫자이고 n은 문자열의 크기이며 m은 빅 세타 (n)입니다.
Welbog

6

O (n ^ 2)처럼 보이는 간단한 접근 방식이 실제로 O (n ln (n))와 같이 더 나은 결과를 얻을 것으로 생각합니다. 테스트하는 데 가장 오래 걸리는 시퀀스 (주어진 n)는 트리오를 포함하지 않는 시퀀스이며 시퀀스에있을 수있는 1의 수에 심각한 제한을 둡니다.

나는 몇 가지 손짓하는 논쟁을 생각해 냈지만 깔끔한 증거를 찾지 못했습니다. 나는 어둠 속에서 찌를 것입니다. 그 대답은 교수가 너무 오랫동안 명백 해져 왔지만 학생들에게는 너무 어렵다는 것을 알고있는 매우 영리한 생각입니다. (그것을 다루거나 강의를 잤다.)


2
롤, 아니 나는 강의를 통해 잠을하지 않았다. 나는 몇몇 다른 학생들과 이야기를 나누었고, 그것을 해결하는 방법에 대한 명확한 생각을 가진 사람은 아무도 없었습니다. 대부분은 일부 학점을 얻기 위해 탄원과 분할에 대한 BS를 썼다.
Robert Parker

3

개정 : 2009-10-17 23:00

나는 이것을 2 천만 개의 문자열과 같이 많은 수로 실행 했으며이 알고리즘은 O (n logn)이 아니라고 생각합니다. 그럼에도 불구하고, 그것은 충분히 멋진 구현이며 실제로 빠르게 실행되도록 많은 최적화를 포함합니다. 25 초 미만의 24 진수 이하의 이진 문자열 배열을 모두 평가합니다.

0 <= L < M < U <= X-1오늘 초의 관찰 내용 을 포함하도록 코드를 업데이트했습니다 .


실물

이것은 개념적으로 내가 대답 한 다른 질문 과 유사합니다 . 이 코드는 또한 일련의 세 가지 값을보고 삼중 항이 조건을 만족하는지 여부를 결정했습니다. 다음은 C # 코드입니다.

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);
            }
        }
    }
}

주요 차이점은 다음과 같습니다.

  1. 솔루션의 철저한 검색
    이 코드는이 알고리즘에 대해 해결하기 가장 어려운 입력을 찾기 위해 강력한 데이터 세트를 생성합니다.
  2. 모든 솔루션과 가장 어려운 솔루션
    이전 질문의 코드는 파이썬 생성기를 사용하여 모든 솔루션을 생성했습니다. 이 코드는 각 패턴 길이에 대해 가장 어려운 것을 표시합니다.
  3. 스코어링 알고리즘
    이 코드는 중간 요소에서 왼쪽 및 오른쪽 가장자리까지의 거리를 확인합니다. 파이썬 코드는 합계가 0보다 크거나 작은 지 테스트했습니다.
  4. 후보에 대한 수렴
    현재 코드는 후보를 찾기 위해 중간에서 가장자리로 작동합니다. 이전 문제의 코드는 가장자리에서 중간 방향으로 작동했습니다. 이 마지막 변경으로 성능이 크게 향상되었습니다.
  5. 짝수 및 홀수 풀 사용
    이 기록이 끝날 때의 관측치에 따라 코드는 짝수의 짝수 쌍의 쌍을 검색하여 L과 U를 찾아 M을 고정시킵니다. 이렇게하면 사전 계산 정보로 검색 횟수가 줄어 듭니다. 따라서이 코드는 FindCandidate의 메인 루프에서 두 가지 수준의 간접 지향성을 사용하며 각 중간 요소에 대해 FindCandidate에 대해 두 번의 호출이 필요합니다. 짝수는 한 번, 홀수는 한 번입니다.

일반적인 아이디어는 데이터의 원시 표현이 아니라 인덱스 작업을하는 것입니다. 1이 나타나는 배열을 계산하면 알고리즘이 데이터 길이에 비례하는 시간이 아니라 데이터의 1 수에 비례하는 시간으로 실행됩니다. 이것은 표준 변환입니다. 문제를 동일하게 유지하면서 더 빠른 작업을 가능하게하는 데이터 구조를 만듭니다.

결과가 오래되었습니다. 제거되었습니다.


편집 : 2009-10-16 18:48

계산할 하드 데이터를 대표하는 다른 응답에서 약간의 신뢰를 얻은 yx의 데이터에서 이러한 결과를 얻었습니다 ... 나는 이것을 제거했습니다. 오래되었습니다.

이 데이터가 내 알고리즘에 가장 어려운 것은 아니므로 yx의 프랙탈이 해결하기 가장 어렵다는 가정이 잘못되었다고 생각합니다. 특정 알고리즘에 대한 최악의 경우는 알고리즘 자체에 달려 있으며 다른 알고리즘에서 일관성이 없을 것입니다.


편집 : 2009-10-17 13:30

이것에 대한 추가 관찰.

먼저, 0과 1의 문자열을 1의 각 위치에 대한 인덱스 배열로 변환하십시오. 배열 A의 길이가 X라고 가정하면 목표는

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

그런

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

또는

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

A [L] 및 A [U]는 짝수로 계산되므로 (짝수, 홀수) 또는 (홀수, 짝수)가 될 수 없습니다. A []를 홀수 및 짝수 풀로 분할하고 홀수 및 짝수 후보 풀에서 A [M]에서 일치하는 항목을 차례로 검색하여 일치 검색을 개선 할 수 있습니다.

그러나 이것은 알고리즘 개선보다 성능 최적화에 가깝습니다. 비교 횟수는 줄어들어야하지만 알고리즘 순서는 동일해야합니다.


2009-10-18 00:45 수정

또 다른 최적화는 후보를 짝수와 홀수로 분리하는 것과 같은 맥락에서 발생합니다. 세 개의 인덱스는 3의 배수 (a, a + x, a + 2x-mod 3은 a, x에 관계없이 0)에 추가해야하므로 L, M 및 U를 mod 3 값으로 분리 할 수 ​​있습니다 :

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

실제로, 이것을 짝수 / 홀수 관측 값과 결합하여 mod 6 값으로 분리 할 수 ​​있습니다.

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

등등. 이것은 추가적인 성능 최적화를 제공하지만 알고리즘 속도 향상은 제공하지 않습니다.


2

아직 해결책을 찾지 못했습니다 :(,하지만 몇 가지 아이디어가 있습니다.

우리가 역 문제에서 시작한다면 어떻게 될까요? 최대 개수 1과 균일 한 간격의 트리오없이 시퀀스를 구성하십시오. 최대 1의 개수가 o (n)임을 증명할 수 있으면 1의 목록 만 반복하여 추정치를 향상시킬 수 있습니다.


1의 수는 확실히 O (n)에 의해 제한됩니다. O (n ** 2) 일 수는 없습니다. 1의 수가 데이터보다 빠르게 증가합니까? 중요한 질문은 상한이 그보다 낮은 지 여부입니다.
hughdbrown

나는 큰 것이 아니라 작은 o를 사용했다
Olexiy

2

도움이 될 수 있습니다 ....

이 문제는 다음과 같이 줄어 듭니다.

양의 정수의 시퀀스가 ​​주어지면, 접두사와 접미사로 분할 된 연속 된 서브 시퀀스를 찾아서 서브 시퀀스의 접두사의 합이 서브 시퀀스의 접미사의 합과 같도록하십시오.

예를 들어, 일련의를 제공 [ 3, 5, 1, 3, 6, 5, 2, 2, 3, 5, 6, 4 ], 우리의 서브 찾을 것 [ 3, 6, 5, 2, 2]접두사와 [ 3, 6 ]접두사의 합 9과의 접미사 [ 5, 2, 2 ]의 접미사의 합을 9.

축소는 다음과 같습니다.

0과 1의 시퀀스가 ​​주어지고 맨 왼쪽부터 시작하여 오른쪽으로 계속 이동하십시오. 다른 것이 발생할 때마다 이전의 움직임이 발생한 이후의 움직임 수를 기록하고 그 숫자를 결과 시퀀스에 추가하십시오.

예를 들어의 시퀀스가 ​​주어진 [ 0, 1, 1, 0, 0, 1, 0, 0, 0, 1 0 ]경우 감소가 발견 [ 1, 3, 4]됩니다. 이 축소에서 연속 하위 시퀀스 [ 1, 3, 4], 접두사 [ 1, 3]와 합 4과 접미사 [ 4 ]와 합을 계산 4합니다.

이 축소는에서 계산 될 수 있습니다 O(n).

불행히도 여기서 어디로 가야할지 모르겠습니다.


1
좀 더 간결한 표기법이지만 시간 복잡성에 도움이되지는 않습니다. "접두사"파티션 세트는 O (n ^ 2) 인 "1"의 모든 발생에서 모든 쌍 검색에 대해 동형입니다.
p00ya

연속적인 서브 시퀀스 합을 다루는 알고리즘이 분명히있다. 불행히도 그들은 모두 O (n)의 최대 합으로 인접한 하위 시퀀스를 찾는 것으로 보입니다.
yfeldblum

@ p00ya 이것은 정확하지 않습니다. 이 알고리즘을 사용하면 시간의 복잡성은 Cantor가 생성 한 문자열이 ((3/2) ^ (log (n) / log (3))) 인 assupton에 의해 허위 수의 상한에 따라 달라지며 공간 복잡성이 다음과 같이됩니다. 그러나 시간 복잡도에는 n이 곱해집니다. 두 번째 답을 확인하십시오. (하지 부정적 일) : D
루카 Rahne

@ ralu : Cantor 생성 문자열이 최악의 경우라고 가정합니다. 레코드의 경우 쌍 수는 확실히 O (n ^ 2)입니다. 그러나 나는 그것이 실제로 큰 오메가 (n ^ 2)임을 암시하고 있다고 생각합니다. 이 결과 (특히 NrootN 링크 참조)에 따라 잘못 되었습니다. )) 증거 또는 추측에 의한 big-Omega (n ^ (4/3)).
p00ya

1

간단한 문제 유형의 경우 (즉, "0" 사이에 "0"만있는 세 개의 "1"을 검색 하는 경우) 매우 간단합니다. 매 "1"마다 ​​시퀀스를 분할하고 두 개의 인접한 서브 시퀀스를 찾을 수 있습니다. 동일한 길이 (물론 두 번째 하위 시퀀스는 물론 마지막 시퀀스가 ​​아닙니다). 분명히, 이것은 할 수 있습니다 O (n) 시간 .

더 복잡한 버전 (즉, 당신은 색인 검색 과 갭 g > 0이되도록 s[i]==s[i+g]==s[i+2*g]=="1"이 생길 존재하는 경우, 내가 잘 모르겠어요)를 O를 (N N 로그) 가능성이 있기 때문에, 솔루션을 O (n²) 를 갖는 세 쌍둥이 이 속성 (모든 문자열을 생각하면 약 n² / 2 와 같은 삼중 항이 있습니다). 물론, 당신은 이것들 중 하나만을 찾고 있지만, 현재 그것을 찾는 방법을 모르겠습니다 ...


예, 더 어려운 문제에 대해 논의하고 있습니다. 여전히, n * log (n) 솔루션이 가능할 수 있습니다.
Olexiy

1
실제로 n은 3을 선택할 수 있습니다. O (n ^ 3) 가능한 트리플입니다. 약 n ^ 2 / 2라고 말했을 때 n은 2를 선택합니다.
ldog

@gmatt : n 2를 선택하면 충분하다; 두 개의 1을 고정하면 세 번째의 위치가 결정되고 해당 위치에 1이 있는지 여부를 확인하는 시간은 일정합니다.
ShreevatsaR

@ ShreevatsaR : 그래 맞아, 나는 구속되지 않은 경우를 생각하고 있었다.
ldog

1
@gmatt : 실제로, 우리는 0 <= i <(n-3) 및 0 <g <(ni-1) / 2의 제약 조건으로 위에서 정의한 튜플 (i, g)을 찾고 있습니다. n ^ 2 / 2 ...
MartinStettner 09 년

1

재미있는 질문이지만 일단 '1'사이의 실제 패턴이 중요하지 않다는 것을 알고 나면 알고리즘은 다음과 같이됩니다.

  • 스캔 '1'을 찾습니다
  • 다음 위치에서 시작하여 다른 '1'을 스캔합니다 (배열 끝에서 현재 첫 번째 '1'에서 거리를 뺀 값 또는 세 번째 '1'이 범위를 벗어남)
  • 두 번째 '1'의 위치와 첫 번째 1 '까지의 거리에 세 번째'1 '이 발견되면 간격이 고르게됩니다.

코드에서 JTest 방식으로 (이 코드는 가장 효율적으로 작성되지 않았으며 어떤 일이 발생했는지 println을 추가했습니다.)

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 번 실행되고 내부 루프가 평균 n / 2 번 실행되기 때문에 이것은 O (n²)입니다.
StriplingWarrior

외부 루프는 n 번 실행되고 내부 루프는 평균 n / 4로 실행되지만 '1'뒤에 오는 위치에서만 시작됩니다. n ^ 2 동작에 접근하려면 '1'의 수가 높아야하며 결과는 일찍 발생하므로 처리가 중지됩니다. 따라서 n ^ 2 동작이 발생하지 않습니다. 데이터의 알려진 속성을 기반으로 O를 결정하는 방법은 현재 나를 피합니다.
rsp

불행히도 그것은 평균 실제 런타임이 아니라 이론적 인 Big O 런타임에 관한 것입니다. 그리고 당신의 접근 방식은 O (n²)입니다 (당신의 접근 방식은 나의 것과 같습니다)
DaClown

나는 평균적인 행동이 아니라 최대의 행동에 대해 이야기하고있었습니다. 테스트에 실패한 최대 엔트로피에 문자열에 log n '1'이 포함되어 있다는 것이 놀랍지 않습니다.
rsp

내부 루프에서 찾은 첫 번째 1의 인덱스로 외부 루프의 인덱스를 업데이트하는 경우, 즉 (ones [m] == ONE) {n = m}? 그것이 큰 O를 돕는가?
steamer25

1

나는 작동 할 수있는 분할 및 정복 접근법을 생각했습니다.

먼저 전처리 과정에서 입력 크기의 절반 ( n / 3) 보다 작은 모든 숫자를 목록에 삽입해야 합니다.

주어진 문자열 : 0000010101000100(이 특정 예는 유효합니다)

1에서 16/2까지의 모든 소수 (및 1)를 목록에 삽입하십시오 : {1, 2, 3, 4, 5, 6, 7}

그런 다음 반으로 나눕니다.

100000101 01000100

크기가 1 인 문자열에 도달 할 때까지이 작업을 계속 수행하십시오. 1이 포함 된 모든 크기의 1 문자열에 대해 문자열 목록을 가능성 목록에 추가하십시오. 그렇지 않으면 실패에 대해 -1을 리턴하십시오.

또한 각 시작 색인과 관련된 여전히 가능한 간격 거리 목록을 반환해야합니다. (위에서 만든 목록으로 시작하고 갈 때 숫자를 제거하십시오.) 여기서 빈 목록은 하나만 처리하므로이 시점에서 간격이 가능하다는 의미입니다. 그렇지 않으면 목록에 제외해야하는 간격이 목록에 포함됩니다.

위의 예를 계속 진행하십시오.

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

첫 번째 결합 단계에서 이제 우리는 두 세트의 여덟 세트가 있습니다. 첫 번째로, 우리는 집합의 가능성을 가지고 있지만, 다른 0이 없기 때문에 1의 간격이 불가능하다는 것을 알게됩니다. 따라서 1의 간격이 불가능하다는 사실에 대해 0 (인덱스)과 {2,3,4,5,7}을 반환합니다. 두 번째로, 우리는 아무것도 없으므로 -1을 반환합니다. 세 번째에서는 인덱스 5에서 공백이 없어진 일치 항목이 있으므로 5, {1,2,3,4,5,7}을 반환합니다. 네 번째 쌍에서 우리는 7, {1,2,3,4,5,7}을 반환합니다. 다섯 번째는 9, {1,2,3,4,5,7}을 반환합니다. 여섯째, -1을 반환하십시오. 일곱 번째는 13, {1,2,3,4,5,7}을 반환합니다. 여덟 번째는 -1을 반환합니다.

다시 네 세트의 네 세트로 결합하면 다음과 같습니다.

1000: 반환 (0, {4,5,6,7}) 0101: 반환 (5, {2,3,4,5,6,7}), (7, {1,2,3,4,5,6 , 7}) 0100: 반환 (9, {3,4,5,6,7}) 0100: 반환 (13, {3,4,5,6,7})

8 개 세트로 결합 :

10000101: 반환 (0, {5,7}), (5, {2,3,4,5,6,7}), (7, {1,2,3,4,5,6,7}) 01000100: 리턴 (9, {4,7}), (13, {3,4,5,6,7})

16 개 세트로 결합 :

10000101 01000100

우리가 발전함에 따라, 우리는 지금까지 모든 가능성을 점검하고 있습니다. 이 단계까지 우리는 문자열의 끝을 넘어서는 물건을 남겼지 만 이제는 모든 가능성을 확인할 수 있습니다.

기본적으로 우리는 간격이 5와 7 인 첫 번째 1을 확인하고 1과 일치하지 않는 것을 발견합니다. (각 수표는 선형 시간이 아니라 일정하다는 점에 유의하십시오.) 그런 다음 간격이 2, 3, 4, 5, 6 및 7 인 두 번째 검사 (색인 5)를 검사합니다. 실제로 일치합니다.

휴! 다소 긴 알고리즘입니다.

마지막 단계로 인해 O (n log n) 이면 100 %를 모르지만 거기까지의 모든 것은 O (n log n)입니다. 입니다. 나중에 다시 돌아가서 마지막 단계를 개선해 보겠습니다.

편집 : Welbog의 의견을 반영하도록 내 대답을 변경했습니다. 오류로 죄송합니다. 나중에 다시 쓴 내용을 해독 할 시간이 조금 더 있으면 의사 코드도 나중에 작성하겠습니다. ;-)


나는 당신의 알고리즘을 따르지 않지만 실제로 O (n log n)가 되려고 시도하는 알고리즘을 시도해 +1
ldog

감사. 더 많은 시간을 얻었을 때 더 잘 설명하려고 노력할 것입니다 (의사 코드 또는 무언가를 작성했을 수도 있음).
Platinum Azure

왜 소수의 차이 가능성 만보고 있습니까? 다음과 같은 문자열을 어떻게 제안 100010001하시겠습니까? 귀하의 접근법을 올바르게 이해하면 정답 (0,{4})을 계산할 수 없으므로 접근 방식을 일치시킬 수 없습니다. 목록에 비 프라임이 필요하다는 것을 감안할 때 O (n log (n))보다 확인 해야하는 가능성 목록을 부 풀리는 병리학 적 문자열을 쉽게 찾을 수 있다고 생각합니다.
Welbog

맹세합니다. 원래는 배수를하려고했지만 중간에 답을 바꾸고 모든 것을 바꾸지 못했습니다. 죄송합니다. 곧 고칠 것입니다
Platinum Azure

3
나는 그것이 O (n log n)라고 생각하지 않습니다. 첫 번째 결합 단계에서는 (n / 2) 세트를 처리하며 각 세트는 가능한 O (n) 간격 세트를 리턴합니다. 이것만으로도 불행하게도 O (n ^ 2)가됩니다.
MartinStettner

1

여기에 대략적인 추측을하겠습니다. 복잡성을 계산하는 데 능숙한 사람들이 알고리즘이 O- 노테이션에 현명한 방법을 알려주는 데 도움이됩니다.

  1. 주어진 이진 문자열 0000010101000100 (예)
  2. 자르기 머리와 0의 꼬리-> 00000 101010001 00
  3. 이전 계산에서 101010001을 얻습니다.
  4. 중간 비트가 '1'인지 확인하고, true 인 경우 유효한 3 개의 균일 한 간격의 '1'을 찾았습니다 (비트 수가 홀수 인 경우에만)
  5. 상관적으로, 남은 자른 비트 수가 짝수 인 경우, 머리와 꼬리 '1'은 균일 한 간격으로 '1'의 일부가 될 수 없습니다.
  6. 우리는 예를 들어 1010100001을 사용합니다 (짝수로 자르기 위해 여분의 '0').이 경우 다시 자르고 나서-> 10101 00001이됩니다.
  7. 우리는 이전 계산에서 10101을 얻고 중간 비트를 확인하고 균등 간격의 비트를 다시 찾았습니다.

나는 이것을 위해 복잡성을 계산하는 방법을 모른다. 누구든지 도울 수 있습니까?

편집 : 내 아이디어를 설명하기 위해 코드를 추가하십시오.

edit2 : 내 코드를 컴파일하려고 시도하고 몇 가지 주요 실수를 발견했습니다.

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;
}

@ 재귀 나는 전화 find3even (head + 1, tail)에 도달했을 때 작동 할 것이라고 생각합니다. 그런 다음 head = 4에서 111이되도록 자르면 다시 확인할 수 있습니까?
andycjw

@recursive 의사 매우 엄격하고 간결한하지 앞서 만들어 코드, 설명 나은에 추가 된 코드 I를 확인하십시오
andycjw

이것은 nlogn입니다. n 비트의 경우 C가 상수 인 n * c 비트를 검사하는 대략의 로그온 반복이 필요합니다.
Ron Warholic

예, 111001과 100111에서 가장 간단한 사례로 실패한 것 같습니다. 균등 한 1은 중간 비트를 중심으로 할 필요는 없습니다.
Dean J

111001은 짝수의 비트를 가지므로 111과 001로 즉시 분할됩니다. 111은 홀수의 비트를 가지며 중간 비트는 성공적으로 리턴하는 비트이므로 1입니다.
Ron Warholic

1

나는 다음과 같은 것을 생각해 냈다.

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

이것은 andycjw에서 영감을 얻었습니다.

  1. 0을 자릅니다.
  2. 심지어 두 개의 하위 문자열 0-(len-2) (마지막 문자 건너 뛰기)와 1-(len-1) (첫 번째 문자 건너 뛰기)를 테스트하십시오
  3. 중간 문자가 하나라도 아니라면 우리는 성공을 거둔 것입니다. 그렇지 않으면 midle 요소없이 midle의 문자열을 나누고 두 부분을 모두 확인하십시오.

복잡성에 관해서는 각 재귀에서 우리가 2로 나누는 것처럼 O (nlogn) 일 수 있습니다.

도움이 되길 바랍니다.


N 요소 문제를 N-1 요소 문제 2 개로 변환하는 것 같습니다. 반으로 나누면 N / 2 요소의 두 가지 문제로 변환됩니다.
RHSeeger

길이가 균일 한 경우에만 해당됩니다. 따라서 len이 8이면 알고리즘은 길이가 7, 7, 3, 3, 3, 3 인 문자열을 만듭니다. 재귀 트리의 hight는 3이고 lg (8)과 같습니다.
Beku

1

좋아, 나는 문제에 대해 또 다른 찌르기를 할 것입니다. 균형 이진 트리를 사용하여 1 사이의 거리를 저장하여 이미 설명한 것과 유사한 O (n log (n)) 알고리즘을 증명할 수 있다고 생각합니다. 이 접근법은 1 사이의 거리 목록으로 문제를 줄이는 것에 대한 법무부의 관찰에서 영감을 얻었습니다.

입력 문자열을 스캔하여 1의 위치 주변에 균형 잡힌 이진 트리를 구성하여 각 노드가 1의 위치를 ​​저장하고 각 가장자리에 각 자식 노드의 인접 1까지의 거리가 표시되도록 할 수 있습니다. 예를 들면 다음과 같습니다.

10010001 gives the following tree

      3
     / \
  2 /   \ 3
   /     \
  0       7

크기 n의 문자열에 대해 최악의 경우 각 삽입에 O (log (n))이 걸리므로 O (n log (n))에서 수행 할 수 있습니다.

그런 다음 문제는 트리를 검색하여 어떤 노드에서든 해당 노드에서 왼쪽 자식을 통한 경로가 오른쪽 자식을 통한 경로와 같은 거리인지 여부를 발견하는 것입니다. 이것은 각 서브 트리에서 재귀 적으로 수행 될 수 있습니다. 검색에서 두 개의 하위 트리를 병합 할 때 왼쪽 하위 트리의 경로와의 거리를 오른쪽 경로의 거리와 비교해야합니다. 하위 트리의 경로 수는 log (n)에 비례하고 노드 수는 n이므로 O (n log (n)) 시간 내에 수행 할 수 있다고 생각합니다.

내가 놓친 것이 있습니까?


"하위 트리에있는 경로의 수가 log (n)에 비례하기 때문에"왜 그렇지 않습니까? 일반적으로 이것은 유망한 접근법입니다.
sdcvvc

@sdcwc : 균형 트리에서 각 하위 트리에는 노드의 절반이 있고 하위 트리의 루트에 대한 경로 수는 하위 트리의 노드 수와 같기 때문에 log (n)에 비례하고 n이 아닙니다. 뿌리).
Jeremy Bourque

0

이것은 재미있는 문제가 좋아 보였다. 그래서 나는 그것을 시도하기로 결정했다.

나는 111000001이 처음 세 가지를 찾아 성공할 것이라고 가정하고 있습니다. 0111000은 정의에 따라 111000과 동일하므로 본질적으로 1 뒤에 오는 0의 수가 중요합니다. 1의 두 경우를 찾으면 다음 1이 3 부작을 완성합니다.

다음은 파이썬입니다.

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

이것은 첫 번째 시도이므로 더 깔끔하게 작성 될 수 있습니다. 이 방법이 실패한 경우를 아래에 나열하십시오.


@ 재귀, 그 간격은 균등하지 않습니다.
James McMahon

균등 한 간격이란 무엇입니까? 인덱스 0, 3 및 6을보십시오. 모두 하나씩, 각각 2 개씩 분리하십시오.
재귀

아시다시피, 0은 간격에만 포함되어 있습니다.
James McMahon

질문에 "1001011"이 언급되어 있지만 작동하지 않습니다. 질문이 제기 된 직후 게시 된 이전 (현재 삭제 된) 답변이 있었으며이 질문과 동일한 (다른) 문제가 해결되었습니다. :-)
ShreevatsaR

나는 오늘 직장에서 이것을보고 있었고 Rob이 자신의 편집에서 의미하는 바를 이해하지 못했습니다. 명확성을 위해 질문을 편집했습니다. 나는 그것으로 쉬운 시간을 보냈을 때 뭔가 빠졌다는 것을 알았어 야했다.
James McMahon

0

이것이 nlog (n) 이유는 다음과 같은 것으로 가정합니다.

  • 삼중 항의 시작 인 1을 찾으려면 (n-2) 문자를 확인해야합니다. 그 시점까지 찾지 못하면 (n-1과 n은 삼중 항을 시작할 수 없습니다) (O (n))
  • 삼중 항의 일부인 첫 번째 1로 시작하는 두 번째 1을 찾으려면 m / 2 (m = nx, 여기서 x는 첫 번째 1의 오프셋) 문자를 확인해야합니다. 왜냐하면 첫 번째에서 끝까지 중간에있을 때까지 두 번째 1을 찾지 못하면 세 번째 1과 두 번째 1의 거리가 정확히 같아야하기 때문입니다. (O (로그 (n)))
  • 첫 번째와 두 번째를 찾을 때까지 색인을 알고 있기 때문에 마지막 1을 찾는 것은 O (1)입니다.

따라서 n, log (n) 및 1 ... O (nlogn)

편집 : 죄송합니다. 내 두뇌는 n / 2가 로그온되었다고 설정했습니다 ... 물론 그렇지 않습니다 (항목의 수를 두 배로 늘리면 여전히 내부 루프의 반복 횟수가 두 배가됩니다). 이것은 여전히 ​​n ^ 2에 있으며 문제를 해결하지 못합니다. 글쎄, 적어도 나는 약간의 코드를 작성해야했다 :)


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

문제를 해결하는 방법을 찾았지만 공식적인 증거를 만들 수는 없습니다. 내가 만든 솔루션은 Java로 작성되었으며 카운터 'n'을 사용하여 얼마나 많은 목록 / 배열 액세스를하는지 계산합니다. 따라서 n이 올바른 경우 stringLength * log (stringLength)보다 작거나 같아야합니다. 0에서 2 ^ 22까지의 숫자로 시도해 보았습니다.

입력 문자열을 반복하고 하나를 보유하는 모든 색인의 목록을 작성하여 시작합니다. 이것은 단지 O (n)입니다.

그런 다음 인덱스 목록에서 첫 번째 인덱스와 첫 번째보다 큰 두 번째 인덱스를 선택합니다. 이 두 인덱스는 인덱스 목록에 있으므로 인덱스를 보유해야합니다. 거기에서 thirdIndex를 계산할 수 있습니다. inputString [thirdIndex]가 1이면 중지됩니다.

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;

}

추가 참고 : 카운터 n은 인덱스 목록을 구성하기 위해 입력 문자열을 반복 할 때 증가하지 않습니다. 이 연산은 O (n)이므로 알고리즘 복잡성에는 영향을 미치지 않습니다.


여전히 O (n)의 두 개의 루프가 중첩되어있는 것처럼 보이며 O (n ^ 2)
RHSeeger

인덱스 배열은 입력 문자열과 크기가 다릅니다. 이것은 실제 증거를 작성하거나 그것이 틀렸다는 것을 증명하기 어렵게 만듭니다. 이 작업을 수행하는 기본적인 수학적 아이디어가 있다고 생각합니다.
Robert Parker

1
이 문제의 트릭은 알고리즘이 O (n ^ 2) 임에도 불구하고 얻을 수있는 최악의 문자열 경우 O (nlogn) 반복 만 발생하고 그렇지 않으면 알고리즘을 사용하여 솔루션을 찾은 것입니다.
z-

2
2 ^ 22까지 테스트한다고해서 실제로 복잡성을 테스트하는 것은 아닙니다. 2 ^ 22에는 22 비트 만 있으므로 N은 22입니다. N이 몇 백만인 몇 가지 값에 대해 시도하십시오.
Peter Recore

1
yx의 답변에 주어진 최대 "나쁜"문자열 중 하나를 사용 하여이 알고리즘을 시도하면 이것이 알고리즘이라는 것을 알 수 O(n^2)있습니다.
Welbog

0

문제에 들어가는 한 가지 요인은 요인과 변화를 생각하는 것입니다.

시프트를 사용하면 1과 0의 문자열을 시프트 된 버전 자체와 비교합니다. 그런 다음 일치하는 것을 가져옵니다. 이 예제를 2만큼 바꾸어 보자.

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

결과 1 (비트 AND)은 2만큼 균등 한 1을 모두 나타내야합니다. 동일한 예제가 3만큼 이동했습니다.

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

이 경우, 3의 간격이 균등 한 1이 없습니다.

이것이 무엇을 말해줍니까? 음수 인 시프트 만 테스트하면됩니다. 예를 들어 6이 떨어져있는 2가 1이라고 가정합니다. '2'교대와 '3'교대 만 테스트하면됩니다 (이는 6을 나누기 때문에). 예를 들면 다음과 같습니다.

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

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

따라서 확인해야 할 유일한 이동은 2,3,5,7,11,13 등입니다. 자릿수의 크기의 제곱근에 가장 가까운 소수까지.

거의 해결 되었습니까?

나는 해결책에 더 가깝다고 생각한다. 원래:

  1. 문자열을 1로 스캔하십시오. 각 1 개의 음에 대해 위치의 모듈러스를 취한 후에 나머지가 남습니다. 모듈러스 범위는 문자열 크기의 1에서 절반입니다. 가능한 가장 큰 분리 크기는 문자열의 절반이기 때문입니다. 이것은 O (n ^ 2)에서 수행됩니다. 그러나. 프라임 모듈러스 만 확인해야하므로 O (n ^ 2 / log (n))
  2. 가장 큰 모듈러스 순서로 모듈러스 / 나머지 목록을 먼저 정렬합니다. O (n * log (n)) 시간 안에 수행 할 수 있습니다.
  3. 동일한 세 개의 연속 모듈러스 / 나머지를 찾으십시오.
  4. 어떻게 든 그것들의 위치를 ​​검색하십시오!

대답의 가장 큰 단서는 가장 빠른 정렬 알고리즘이 O (n * log (n))이라는 것입니다.

잘못된

1 단계는 동료가 지적한대로 잘못되었습니다. 우리가 위치 2,12 및 102에 1을 가지고 있다면, 모듈러스 10을 취하면 모두 같은 잔차를 가지게되지만 똑같이 떨어져 있지 않습니다! 죄송합니다.


이것은 흥미로운 접근 방식입니다. 전체 솔루션을 제시하면 알려주십시오.
James McMahon

숫자 k O (n) 번만큼 시프트 한 다음 O (n)은 한 번호 씩 시프트하더라도 시프트 당 O (n ^ 2) 알고리즘을 생성합니다. 알고리즘은 둘 이상의 숫자만큼 이동해야합니다.
ldog

0

내 최선의 노력에도 불구하고 활에 싸이 지 않는 몇 가지 생각이 있습니다. 그럼에도 불구하고 그것들은 누군가의 분석에 유용한 출발점이 될 수 있습니다.

제안 된 솔루션을 다음과 같이 고려하십시오.이 답변은 이전 버전의 답변을 포함하여 여러 사람들이 제안한 접근법입니다. :)

  1. 선행 및 후행 0을 다듬습니다.
  2. 문자열을 스캔하여 1을 찾으십시오.
  3. 1이 발견되면 :
    1. 그것이 솔루션의 중간 1이라고 가정하십시오.
    2. 각각의 이전 1에 대해 저장된 위치를 사용하여 최종 1의 예상 위치를 계산하십시오.
    3. 계산 된 위치가 문자열의 끝 이후 인 경우 솔루션의 일부가 될 수 없으므로 후보 목록에서 위치를 삭제하십시오.
    4. 해결책을 확인하십시오.
  4. 솔루션을 찾을 수 없으면 현재 1을 후보 목록에 추가하십시오.
  5. 1이 더 이상 없을 때까지 반복하십시오.

이제 다음과 같은 입력 문자열 문자열을 고려하십시오. 해결책은 없습니다.

101
101001
1010010001
101001000100001
101001000100001000001

일반적으로 이것은 j 0 형식의 k 문자열을 연결 한 다음 j에 대해 0에서 k-1까지 1을 지정합니다.

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

서브 스트링의 길이는 1, 2, 3 등입니다. 따라서, 문제 크기 n은 n = k (k + 1) / 2가되도록 길이 1-k의 서브 스트링을 갖습니다.

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

k는 또한 우리가 고려해야 할 1의 수를 추적합니다. 1을 볼 때마다 지금까지 본 모든 1을 고려해야합니다. 따라서 우리는 두 번째 1을 볼 때 첫 번째만을 고려하고, 세 번째 1을 볼 때 첫 번째 2를 다시 고려하고, 네 번째 1을 볼 때 첫 번째 3을 다시 고려해야합니다. 알고리즘이 끝날 때 k (k-1) / 2 쌍의 1을 고려했습니다. 전화 해 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

n과 p의 관계는 n = p + k입니다.

문자열을 통과하는 프로세스는 O (n) 시간이 걸립니다. 1이 발생할 때마다 최대 (k-1) 비교가 수행됩니다. n = k (k + 1) / 2이므로 n> k ** 2이므로 sqrt (n)> k입니다. 이것은 우리에게 O (n sqrt (n)) 또는 O (n ** 3 / 2)를 제공합니다. 그러나 비교 횟수가 1에서 최대 k까지되므로 전체적으로 k가 아니기 때문에 실제로는 빡빡하지 않을 수 있습니다. 그러나 수학에서 어떻게 설명해야할지 모르겠습니다.

여전히 O (n log (n))가 아닙니다. 또한 입력이 의심되는 경우에도 입력이 최악임을 증명할 수 없습니다. 나는 앞면에 1의 밀도가 높은 패킹이 끝에는 더 희소 한 패킹을 초래한다고 생각합니다.

누군가가 여전히 유용하다고 생각할 수 있으므로 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";

"비 솔루션"순서가 잘못되었습니다. 각 1의 인덱스는 삼각형 숫자 1, 3, 6, 10, 15 ... 등의 시퀀스이며, 산술 진행을 형성하는 숫자 6, 36 및 66을 포함합니다.
Jason S

0

1을 스캔하는 동안 위치를 목록에 추가하십시오. 두 번째 및 연속 1을 추가 할 때는 지금까지 목록의 각 위치와 비교하십시오. 간격은 currentOne (중앙)-previousOne (왼쪽)과 같습니다. 오른쪽 비트는 currentOne + 간격입니다. 1이면 끝입니다.

그것들의 목록은 그들 사이의 공간에 반비례하여 증가합니다. 간단히 말해서, 1 사이에 많은 0이 있으면 (최악의 경우와 같이) 알려진 1의 목록이 상당히 느리게 증가합니다.

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

22 번째 순진한 솔루션을 문제에 게시하기 전에 한 가지 의견을 추가한다고 생각했습니다. 순진한 솔루션의 경우 문자열에서 1의 수가 최대 O (log (n)) 인 것이 아니라 최대 O (sqrt (n * log (n)) 인 것을 보여줄 필요는 없습니다.

솔버 :

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

기본적으로 flybywire의 아이디어 및 구현과 비슷하지만 다소 앞을 내다 보는 것이 좋습니다.

욕심 문자열 빌더 :

#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

(내 방어에서, 나는 여전히 '파이썬 학습'이해 단계에있다)

또한, 욕심 많은 줄의 건물에서 잠재적으로 유용한 결과물은 1의 수에서 2의 거듭 제곱을 친 후에 다소 일관된 점프가 있습니다.

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

나는 수학적 접근법을 제시하려고 노력할 것이다. 이것은 끝보다 시작에 불과하므로 도움, 의견 또는 모순까지 깊이 감사하겠습니다. 그러나이 방법이 입증되면 알고리즘은 문자열에서 간단하게 검색됩니다.

  1. 공간의 고정 된 수의 주어 k와 문자열을 S하는 K-간격 - 삼중의 검색 소요 O(n)- 모든 위해 우리는 단순히 시험 0<=i<=(n-2k)경우 S[i]==S[i+k]==S[i+2k]. 테스트를 수행 O(1)하고 상수가있는 n-k시간 을 수행하므로 시간 k이 걸립니다 O(n-k)=O(n).

  2. 의 수 1와 검색해야하는 최대 공간 사이에 반비례가 있다고 가정 해 봅시다 . 즉,의 수가 많으면 1삼중 항이 있어야하며 매우 조밀해야합니다. 이 몇 개만있는 1경우 삼중 항 (있는 경우)이 매우 희박 할 수 있습니다. 다시 말해, 나는 충분한 1삼중 항이 존재한다면 그러한 삼중 항이 존재해야 한다는 것을 증명할 수 있습니다 1. 이것은 Pigeonhole 원칙에 의해 설명 될 수 있습니다 -이것에 대해서는 나중에 자세히 설명하겠습니다.

  3. k내가 찾아야 할 가능한 공간 수에 상한이 있다고 가정 해보십시오 . 이제 각 1에있는 S[i]우리가 확인해야 1에서 S[i-1]그리고 S[i+1], S[i-2]그리고 S[i+2]... S[i-k]그리고 S[i+k]. 이것은 Gauss의 Series Summation Formula 로 인해 O((k^2-k)/2)=O(k^2)각각 1에 적용 됩니다. 이것은 섹션 1과 다릅니다-나는 일정한 공간이 아니라 공간 수의 상한으로 가지고 있습니다.Sk

우리는 증명해야합니다 O(n*log(n)). 즉, 우리 k*(number of 1's)log(n) .

우리가 할 수 있다면, 알고리즘은 간단하다 - 각각 1S인덱스가이 i단순히 찾아 1'거리 양쪽부터에요 k. 두 사람은 같은 거리에서 발견되면, 반환 ik. 다시 말하지만 까다로운 부분은 k정확성을 찾고 증명하는 것입니다.

귀하의 의견에 진심으로 감사드립니다 . 지금까지 화이트 보드의 k수와 수 사이의 관계를 찾으려고 노력해 왔습니다 1.


0

인수:

상한의 log (n) 수에 대해 말하면 잘못되었습니다.

편집하다:

이제 Cantor 번호를 사용하면 (정확한 경우) 세트 밀도가 (2/3) ^ Log_3 (n) (이상한 기능)이며 log (n) / n 밀도가 강하다는 것을 알았습니다.

이것이 상한이라면, 적어도 O (n * (3/2) ^ (log (n) / log (3))) 시간 복잡성 및 O ((3/2) ^ ( log (n) / log (3))) 공간 복잡성. (algorhitm에 대한 법무부의 답변을 확인하십시오)

이것은 여전히 ​​O (n ^ 2)보다 훨씬 낫습니다.

이 함수 ((3/2) ^ (log (n) / log (3)))는 실제로 첫눈에 n * log (n)처럼 보입니다.

이 공식을 어떻게 얻었습니까?

문자열에 Cantors 번호가 적용됩니다.
문자열 길이가 3 ^ p == n이라고 가정하십시오.
. Cantor 문자열 생성의 각 단계에서 이전 수의 2/3를 유지합니다. 이 p 번을 적용하십시오.

즉 (n * ((2/3) ^ p))-> (((3 ^ p)) * ((2/3) ^ p)) 나머지와 단순화 후 2 ^ p를 의미합니다. 이것은 3 ^ p string-> (3/2) ^ p ones의 2 ^ p 것을 의미합니다. p = log (n) / log (3)을 대입하고
((3/2) ^ (log (n) / log (3)))


False : Cantor 세트의 밀도가 n ^ log_3 (2)입니다.
sdcvvc

0

O (n ^ 2) 공간이있는 간단한 O (n) 솔루션은 어떻습니까? 모든 비트 연산자는 O (1)에서 작동한다는 가정을 사용합니다.

이 알고리즘은 기본적으로 4 단계로 작동합니다.

1 단계 : 원래 숫자의 각 비트마다 비트가 얼마나 멀리 있는지 알아 내지 만 한 방향 만 고려하십시오. (나는 최하위 비트 방향으로 모든 비트를 고려했습니다.)

2 단계 : 입력에서 비트 순서를 반대로 바꿉니다.

3 단계 : 반전 된 입력에서 1 단계를 다시 실행하십시오.

4 단계 : 1 단계와 3 단계의 결과를 비교합니다. 비트가 위와 아래에 동일하게 간격을두면 히트가 발생합니다.

위 알고리즘의 단계는 O (n)보다 오래 걸리지 않습니다. ^ _ ^

추가적인 이점으로,이 알고리즘은 모든 숫자에서 동일한 간격의 모든 것을 찾습니다. 예를 들어 "0x0005"의 결과가 나오면 둘 다 1과 3 단위의 간격이 동일합니다.

아래 코드를 실제로 최적화하려고 시도하지는 않았지만 컴파일 가능한 C # 코드로 작동합니다.

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));
                }
            }
        }
    }
}

누군가는 아마도 충분히 많은 수의 비트 연산을 O (1)에서 수행 할 수 없다고 언급 할 것입니다. 당신 말이 맞아요 그러나 덧셈, 뺄셈, 곱셈 또는 나눗셈 (시프 팅으로 수행 할 수 없음)을 사용하는 모든 솔루션에도 문제가 있다고 추측합니다.


0

아래는 해결책입니다. 여기 저기에 약간의 실수가있을 수 있지만 아이디어는 건전합니다.

편집 : 그것은 N * 로그가 아닙니다 (n)

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

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;
}

작동 방식 :

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
알고리즘은 작동하지만 O (n ^ 2)보다 작다는 것을 증명해야합니다. 사소한 분석으로 O (n ^ 2)가됩니다. 각 1에 대해 그 이전의 1을 모두 검토합니다. 복소수 함수를 1 + 2 + 3 + ... + (k / 2-1) = O (k ^ 2) [여기서 k는 1의 개수 임]로 만듭니다.
Anna

나는 해결책이없는 최악의 시나리오를 확인하고 실제로 O (n * log (n))
보다 큽니다.

0

분명히 우리는 적어도 동시에 여러 묶음을 점검해야하므로 어떻게 든 점검을 압축해야합니다. 후보 알고리즘이 있지만 시간 복잡성을 분석하는 것이 능력 * 시간 임계 값을 초과합니다.

각 노드에 세 개의 자식이 있고 각 노드에는 잎의 총 수가 1 인 트리를 만듭니다. 1에 대한 연결 목록도 작성하십시오. 각 노드에 적용되는 범위에 비례하여 허용 된 비용을 할당하십시오. 각 노드에서 보내는 시간이 예산 범위 내인 한 O (n lg n) 알고리즘이 있습니다.

-

루트에서 시작하십시오. 아래 1의 총 수의 제곱이 허용 된 비용보다 적 으면 순진 알고리즘을 적용하십시오. 그렇지 않으면 그 자녀들에게 재귀가 걸립니다.

이제 우리는 예산 내로 돌아 왔거나 어린이 중 하나 안에 완전히 포함 된 유효한 트리플렛이 없다는 것을 알고 있습니다. 따라서 노드 간 트리플렛을 확인해야합니다.

이제 상황이 엄청나게 지저분 해집니다. 우리는 본질적으로 범위를 제한하면서 잠재적 인 하위 집합에 대해 재귀를 원합니다. 순진 알고리즘이 예산에 맞게 실행될 정도로 범위가 제한 되 자마자이를 수행합니다. 지루할 것이므로 이것을 구현하십시오. 12 건의 사례가 있습니다.

-

알고리즘이 작동한다고 생각하는 이유는 유효한 삼중 항이없는 시퀀스가 ​​1과 0의 뭉치 사이에서 번갈아 나타나기 때문입니다. 주변 검색 공간을 효과적으로 분할하고 트리는 해당 분할을 에뮬레이트합니다.

알고리즘의 런타임은 전혀 명확하지 않습니다. 그것은 시퀀스의 사소한 속성에 의존합니다. 1이 실제로 희박한 경우 순진 알고리즘은 예산에 따라 작동합니다. 1이 조밀하면 즉시 일치하는 것을 찾아야합니다. 그러나 밀도가 '정확히'(예 : ~ n ^ 0.63 근처,베이스 3에서 '2'자리가없는 위치에서 모든 비트를 설정하여 달성 할 수있는 경우) 작동하는지 알 수 없습니다. 분할 효과가 충분히 강함을 증명해야합니다.


0

여기에 이론적 인 대답은 없지만 k와 n의 함수로 런타임 동작을 탐색하는 빠른 Java 프로그램을 작성했습니다. 여기서 n은 총 비트 길이이고 k는 1입니다. 나는 최악의 경우 O (k ^ 2)가 필요할지라도 모든 비트 위치 쌍을 검사하고 세 번째 비트를 찾는 "정규"알고리즘이라고 말하는 몇 명의 응답자와 함께 있습니다. 최악의 경우에는 드문 비트 스트링이 필요하기 때문에 실제로는 O (n ln n)입니다.

어쨌든 아래에 프로그램이 있습니다. 그것은 상수 n에 대해 많은 시도 NTRIALS를 실행하는 몬테카를로 스타일의 프로그램이며, 지정할 수있는 한계 사이에 제한된 밀도로 Bernoulli 프로세스를 사용하여 다양한 k 값의 비트 세트를 무작위로 생성하고 실행 시간을 기록합니다 균등 간격의 트리플렛을 찾거나 찾지 못하는 경우, CPU 시간이 아닌 단계로 측정 된 시간. 나는 n = 64, 256, 1024, 4096, 16384 * (여전히 실행)에 대해 실행했습니다. 먼저 500000 시도로 테스트를 실행하여 어떤 k 값이 가장 긴 실행 시간을 갖는지 확인한 다음 좁은 범위의 5000000 시도로 다른 테스트를 수행했습니다. 이러한 값의 모양을 확인하기 위해 밀도에 초점을 맞 춥니 다. 가장 긴 실행 시간은 매우 희박한 밀도로 발생합니다 (예 : n = 4096의 경우 실행 시간 피크는 k = 16-64 범위에 있으며 평균 실행 시간은 4212 단계 @ k = 31에서 완만 한 피크 임). 최대 런타임은 5101 단계 @ k = 58에서 정점). 최악의 O (k ^ 2) 단계가 비트 열을 스캔하여 1의 위치 인덱스를 찾는 O (n) 단계보다 커지기 위해서는 매우 큰 N 값이 필요한 것처럼 보입니다.

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

수백만 자리의 최악의 시나리오에 문제가 있습니다. /dev/urandom본질적으로 퍼지 하면 O (n)이 발생하지만 최악의 경우가 그보다 나쁘다는 것을 알고 있습니다. 나는 얼마나 더 나쁘게 말할 수 없습니다. 소규모의 n경우 주변 3*n*log(n)에서 입력을 찾는 것이 쉽지 않지만이 특정 문제의 다른 성장 순서와 입력 을 구별하는 것은 놀랍지 않습니다.

최악의 입력 작업을 한 사람이 1 억보다 큰 길이의 문자열을 생성 할 수 있습니까?


내 대답에서 지적했듯이 모든 자릿수의 나쁜 (최악의 경우는 아니지만) 문자열을 쉽게 생성 할 수 있습니다. 위치 2, 6, 8, 18, 20, 24, 26, 54, 56, 60 ... : research.att.com/~njas/sequences/…의 공식 참조) 3 ^ 13 ≈ 백만의 경우 2 ^ 13 ≈ 8000 1입니다. 이러한 문자열의 실행 시간은 ≈ n ^ (1.26)입니다. 이러한 작은 n의 경우 O (n log n)과 여전히 구분하기 어려울 수 있습니다. 사용해보십시오.
ShreevatsaR


-3

이것이 해결책이 될 수 있습니까? 나는 그것이 O (nlogn)인지 확실하지 않지만 내 의견으로는 O (n²)보다 낫습니다. 트리플을 찾지 못하는 유일한 방법은 소수 분포입니다.

개선의 여지가 있으며 두 번째 발견 1은 다음 첫 번째 1 일 수 있습니다. 또한 오류 검사가 없습니다.

#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
기술적으로 이것은 O (n ^ 2)입니다. 평균적으로 내부 루프는 실행될 때마다 n의 절반 이상을 반복합니다. 따라서 O (n * (n / 2))로 작성 될 수 있으며 O (n ^ 2)로 단순화 될 수 있습니다.
Robert Parker

흠, 당신이 옳은 것 같습니다. 이것은 단순한 문제가 아니며, O (logn) 복잡성에 대한 추가 검색 / 비교를위한 공간이 많지 않은 모든 1 테이크 O (n)을 찾는 것입니다.
DaClown

-3

이 알고리즘은 O (n log n) 복잡도 (C ++, DevStudio 2k5)를 가지고 있다고 생각합니다. 이제 알고리즘을 분석하여 복잡성을 결정하는 방법에 대한 세부 정보를 알지 못하므로 메트릭 수집 정보를 코드에 추가했습니다. 코드는 주어진 입력에 대해 1과 0의 시퀀스에서 수행 된 테스트 수를 계산합니다 (아마도 알고리즘을 사용하지 않았습니다). 실제 테스트 수를 O 값과 비교하여 상관 관계가 있는지 확인할 수 있습니다.

#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";
  }
}

이 프로그램은 각 문자열 길이에 대한 테스트 수를 최대 32 자까지 출력합니다. 결과는 다음과 같습니다.

 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

'n log n'값도 추가했습니다. 선택한 그래프 도구를 사용하여이를 플롯하여 두 결과 간의 상관 관계를 확인하십시오. 이 분석은 모든 n 값으로 확장됩니까? 모르겠어요


완벽한 상관 관계는 아닙니다. 동의합니다. 그러나 곡선은 n ^ 2보다 n log n에 더 가깝습니다.
Skizz

3
입력 크기를 백만 이상 펌핑 해보십시오. 작은 입력에서 커브는 종종 알고리즘의 커브와 유사하게 보이며 입력 크기가 펌핑 될 때 분명히 더 좋습니다.
Nick Larsen

내부와 외부가 경계를 이루는 이중 for 루프는 삼각형 모양을 만들며 여전히 복잡합니다. [0, n]의 i와 [0, n-2 * i]의 j가 삼각형이고 삼각형의 면적이 2 차 경향이 있도록 모든 (i, j)를 생각하십시오.
Matthieu M.

정확히 말하면, 짝수 n에 대해 테스트 = (n ^ 2-2n) / 4; 분명히 이차.
Grandpa
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.