2013-03-13 4 views
4

무차별 대입 (Brute Force) 접근법은 문제를 풀기위한 것이 아니며 연구에 도움이되는 것은 아닙니다. 나는 Project Euler 문제로 X에서 숫자 하나 하나를 "substring"숫자의 자릿수로 나눌 수있는 Y보다 하나 작은 숫자를 찾는 문제가 있습니다.python/pypy에서 하나 자식 (one-child) 번호 찾기를위한 무차별 대입 프로세스 최적화

이러한 것을 하나의 하위 번호라고합니다. 104는 하나의 자식 번호입니다. 하위 문자열 중 [1, 0, 4, 10, 04, 104]는 0 만 3으로 나눌 수 있습니다.이 질문은 10 * 17보다 적은 one-child 숫자의 양을 찾습니다. 올바른 접근법이 아닙니다. 그러나 나는 1 * 11 이전에 발생하는 한 어린이 수의 양을 알 필요가있는 이론이 있습니다.

나는 랩톱을 반나절 동안두고도이 번호를 찾지 못했습니다. 나는 Cython을 시도했다. 나는 C에 대해 아무것도 모르는 초보 프로그래머이다. 결과는 정말 나빴다. 나는 심지어 클라우드 컴퓨팅을 시도했지만 프로세스가 완료되기 전에 내 ssh 파이프는 항상 중단됩니다. 누군가가 나 10 ** 11이 문제에 대한 폭력 방법을 예비 성형에 대한 몇 가지 다른 접근법 또는 최적화를 파악하는 데 도움 수 있다면

, 그것은 크게 감상 할 수있다. 나는 많은 시간을 위해 작업을 한 것처럼

은 ...

는 정수론 또는이 문제에 대한 답에 나에게 조언을 빌려하지 마십시오, 나는 정말 결론에 도달 할 혼자서.

## a one child number has only one "substring" divisable by the 
## number of digits in the number. Example: 104 is a one child number as 0 
## is the only substring which 3 may divide, of the set [1,0,4,10,04,104] 

## FYI one-child numbers are positive, so the number 0 is not one-child 


from multiprocessing import Pool 
import os.path 

def OneChild(numRange): # hopefully(10*11,1) 
    OneChild = [] 
    start = numRange[0] 
    number = numRange[1] 

    ## top loop handles one number at a time 
    ## loop ends when start become larger then end 
    while number >= start: 

     ## preparing to analayze one number 
     ## for exactly one divisableSubstrings 
     numberString = str(start) 
     numDigits = len(numberString) 
     divisableSubstrings = 0 
     ticker1,ticker2 = 0, numDigits 

     ## ticker1 starts at 0 and ends at number of digits - 1 
     ## ticker2 starts at number of digits and ends +1 from ticker1 
     ## an example for a three digit number: (0,3) (0,2) (0,1) (1,3) (1,2) (2,3) 
     while ticker1 <= numDigits+1: 
      while ticker2 > ticker1: 
       if int(numberString[ticker1:ticker2]) % numDigits == 0: 
        divisableSubstrings += 1 
        if divisableSubstrings == 2: 
         ticker1 = numDigits+1 
         ticker2 = ticker1 

       ##Counters  
       ticker2 -= 1 
      ticker1 += 1 
      ticker2 = numDigits    
     if divisableSubstrings == 1: ## One-Child Bouncer 
      OneChild.append(start) ## inefficient but I want the specifics 
     start += 1 
    return (OneChild) 

## Speed seems improve with more pool arguments, labeled here as cores 
## Im guessing this is due to pypy preforming best when task is neither 
## to large nor small 
def MultiProcList(numRange,start = 1,cores = 100): # multiprocessing 
    print "Asked to use %i cores between %i numbers: From %s to %s" % (cores,numRange-start, start,numRange) 
    cores = adjustCores(numRange,start,cores) 
    print "Using %i cores" % (cores) 

    chunk = (numRange+1-start)/cores 
    end = chunk+start -1 
    total, argsList= 0, [] 
    for i in range(cores): 
     # print start,end-1 
     argsList.append((start,end-1)) 
     start, end = end , end + chunk 
    pool = Pool(processes=cores) 
    data = pool.map(OneChild,argsList) 
    for d in data: 
     total += len(d) 
    print total 

## f = open("Result.txt", "w+") 
## f.write(str(total)) 
## f.close() 

def adjustCores(numRange,start,cores): 
    if start == 1: 
     start = 0 
    else: 
     pass 
    while (numRange-start)%cores != 0: 
     cores -= 1 
    return cores 

#MultiProcList(10**7) 
from timeit import Timer 
t = Timer(lambda: MultiProcList(10**6)) 
print t.timeit(number=1) 
+0

왜 브 루트 포스 방식을 사용하고 있습니까? – Blender

+0

그는 단지 문제의 작은 부분에 무차별 대입을 사용하고 있습니다. 그 다음에 실제 솔루션에 대한 숫자 이론이 적용됩니다. –

+2

PyPy를 사용해도 10 ** 7을 통과하는 데 15 초 밖에 걸리지 않습니다. 이 일에 무차별 한 방법이 있다는 것은 정말로 의심 스럽습니다. – Blender

답변

1

이것은 가장 빠른 무력 코드입니다. Cython을 사용하여 계산 속도를 높입니다. 모든 수를 확인하는 대신 재귀를 통해 모든 단일 수를 찾습니다.

%%cython 
cdef int _one_child_number(int s, int child_count, int digits_count): 
    cdef int start, count, c, child_count2, s2, part, i 
    if s >= 10**(digits_count-1): 
     return child_count 
    else: 
     if s == 0: 
      start = 1 
     else: 
      start = 0 
     count = 0 
     for c in range(start, 10): 
      s2 = s*10 + c 
      child_count2 = child_count 
      i = 10 
      while True: 
       part = s2 % i 
       if part % digits_count == 0: 
        child_count2 += 1 
        if child_count2 > 1: 
         break 
       if part == s2: 
        break 
       i *= 10 

      if child_count2 <= 1: 
       count += _one_child_number(s2, child_count2, digits_count) 
     return count 

def one_child_number(int digits_count): 
    return _one_child_number(0, 0, digits_count) 

는 F (10 ** 7)의 번호를 찾으려면, 당신이 큰 결과를 계산하기 위해 64 비트 정수를 필요 277674.

print sum(one_child_number(i) for i in xrange(8)) 

결과를 얻기 위해 약 100ms에 걸립니다.

편집 : 나는 몇 가지 코멘트를 추가,하지만 내 영어가 좋지 않다, 그래서 순수 파이썬 코드에 코드를 변환하고, 그것이 어떻게 작동하는지 알아내는 데 도움이되는 몇 가지 인쇄를 추가합니다. s 왼쪽에서

_one_child_numberdigits_counts의 마지막 자리이고, 재귀 child_counts에 아이 카운트가 자리를 추가한다.

def _one_child_number(s, child_count, digits_count): 
    print s, child_count 
    if s >= 10**(digits_count-1): # if the length of s is digits_count 
     return child_count # child_count is 0 or 1 here, 1 means we found one one-child-number. 
    else: 
     if s == 0: 
      start = 1 #if the length of s is 0, we choose from 123456789 for the most left digit. 
     else: 
      start = 0 #otherwise we choose from
     count = 0 # init the one-child-number count 
     for c in range(start, 10): # loop for every digit 
      s2 = s*10 + c # add digit c to the right of s 

      # following code calculates the child count of s2 
      child_count2 = child_count 
      i = 10 
      while True: 
       part = s2 % i 
       if part % digits_count == 0: 
        child_count2 += 1 
        if child_count2 > 1: # when child count > 1, it's not a one-child-number, break 
         break 
       if part == s2: 
        break 
       i *= 10 

      # if the child count by far is less than or equal 1, 
      # call _one_child_number recursively to add next digit. 
      if child_count2 <= 1: 
       count += _one_child_number(s2, child_count2, digits_count) 
     return count 

여기 _one_child_number(0, 0, 3)의 OUPUT 그이며, 3 자릿수의 한 하위 번호의 카운트는 첫 번째 열은 3 자리 번호 인 제 2 컬럼의 합이다.

0 0 
1 0 
10 1 
101 1 
104 1 
107 1 
11 0 
110 1 
111 1 
112 1 
113 1 
114 1 
115 1 
116 1 
117 1 
118 1 
119 1 
12 1 
122 1 
125 1 
128 1 
13 1 
131 1 
134 1 
137 1 
14 0 
140 1 
141 1 
142 1 
143 1 
144 1 
145 1 
146 1 
147 1 
148 1 
149 1 
15 1 
152 1 
155 1 
158 1 
16 1 
161 1 
164 1 
167 1 
17 0 
170 1 
171 1 
172 1 
173 1 
174 1 
175 1 
176 1 
177 1 
178 1 
179 1 
18 1 
182 1 
185 1 
188 1 
19 1 
191 1 
194 1 
197 1 
2 0 
20 1 
202 1 
205 1 
208 1 
21 1 
211 1 
214 1 
217 1 
22 0 
220 1 
221 1 
222 1 
223 1 
224 1 
225 1 
226 1 
227 1 
228 1 
229 1 
23 1 
232 1 
235 1 
238 1 
24 1 
241 1 
244 1 
247 1 
25 0 
250 1 
251 1 
252 1 
253 1 
254 1 
255 1 
256 1 
257 1 
258 1 
259 1 
26 1 
262 1 
265 1 
268 1 
27 1 
271 1 
274 1 
277 1 
28 0 
280 1 
281 1 
282 1 
283 1 
284 1 
285 1 
286 1 
287 1 
288 1 
289 1 
29 1 
292 1 
295 1 
298 1 
3 1 
31 1 
311 1 
314 1 
317 1 
32 1 
322 1 
325 1 
328 1 
34 1 
341 1 
344 1 
347 1 
35 1 
352 1 
355 1 
358 1 
37 1 
371 1 
374 1 
377 1 
38 1 
382 1 
385 1 
388 1 
4 0 
40 1 
401 1 
404 1 
407 1 
41 0 
410 1 
411 1 
412 1 
413 1 
414 1 
415 1 
416 1 
417 1 
418 1 
419 1 
42 1 
422 1 
425 1 
428 1 
43 1 
431 1 
434 1 
437 1 
44 0 
440 1 
441 1 
442 1 
443 1 
444 1 
445 1 
446 1 
447 1 
448 1 
449 1 
45 1 
452 1 
455 1 
458 1 
46 1 
461 1 
464 1 
467 1 
47 0 
470 1 
471 1 
472 1 
473 1 
474 1 
475 1 
476 1 
477 1 
478 1 
479 1 
48 1 
482 1 
485 1 
488 1 
49 1 
491 1 
494 1 
497 1 
5 0 
50 1 
502 1 
505 1 
508 1 
51 1 
511 1 
514 1 
517 1 
52 0 
520 1 
521 1 
522 1 
523 1 
524 1 
525 1 
526 1 
527 1 
528 1 
529 1 
53 1 
532 1 
535 1 
538 1 
54 1 
541 1 
544 1 
547 1 
55 0 
550 1 
551 1 
552 1 
553 1 
554 1 
555 1 
556 1 
557 1 
558 1 
559 1 
56 1 
562 1 
565 1 
568 1 
57 1 
571 1 
574 1 
577 1 
58 0 
580 1 
581 1 
582 1 
583 1 
584 1 
585 1 
586 1 
587 1 
588 1 
589 1 
59 1 
592 1 
595 1 
598 1 
6 1 
61 1 
611 1 
614 1 
617 1 
62 1 
622 1 
625 1 
628 1 
64 1 
641 1 
644 1 
647 1 
65 1 
652 1 
655 1 
658 1 
67 1 
671 1 
674 1 
677 1 
68 1 
682 1 
685 1 
688 1 
7 0 
70 1 
701 1 
704 1 
707 1 
71 0 
710 1 
711 1 
712 1 
713 1 
714 1 
715 1 
716 1 
717 1 
718 1 
719 1 
72 1 
722 1 
725 1 
728 1 
73 1 
731 1 
734 1 
737 1 
74 0 
740 1 
741 1 
742 1 
743 1 
744 1 
745 1 
746 1 
747 1 
748 1 
749 1 
75 1 
752 1 
755 1 
758 1 
76 1 
761 1 
764 1 
767 1 
77 0 
770 1 
771 1 
772 1 
773 1 
774 1 
775 1 
776 1 
777 1 
778 1 
779 1 
78 1 
782 1 
785 1 
788 1 
79 1 
791 1 
794 1 
797 1 
8 0 
80 1 
802 1 
805 1 
808 1 
81 1 
811 1 
814 1 
817 1 
82 0 
820 1 
821 1 
822 1 
823 1 
824 1 
825 1 
826 1 
827 1 
828 1 
829 1 
83 1 
832 1 
835 1 
838 1 
84 1 
841 1 
844 1 
847 1 
85 0 
850 1 
851 1 
852 1 
853 1 
854 1 
855 1 
856 1 
857 1 
858 1 
859 1 
86 1 
862 1 
865 1 
868 1 
87 1 
871 1 
874 1 
877 1 
88 0 
880 1 
881 1 
882 1 
883 1 
884 1 
885 1 
886 1 
887 1 
888 1 
889 1 
89 1 
892 1 
895 1 
898 1 
9 1 
91 1 
911 1 
914 1 
917 1 
92 1 
922 1 
925 1 
928 1 
94 1 
941 1 
944 1 
947 1 
95 1 
952 1 
955 1 
958 1 
97 1 
971 1 
974 1 
977 1 
98 1 
982 1 
985 1 
988 1 
+0

잘 했어! 당신의 도움을 주셔서 감사합니다. –

+0

이 프로그램은 순환 적이기 때문에 print 문과 중첩 된 print 문은 코드 뒤에있는 마술을 전달하는 데 효과적이지 않습니다. 그것이 나를 위해 문제를 "망쳐 놓을"하나의 자식 숫자 뒤에 더 큰 기본 구조를 드러내지 않는다면, 그것이 작동하는 방식을 설명해 주시겠습니까? –