일치하는 수를 합산하는 방법

일치하는 수를 합산하는 방법

저는 스크립팅을 처음 접했고 도움이 필요합니다. 대답 해 주셔서 감사합니다.

나는 다음 숫자 그룹 중 두 개를 포함하는 모든 5자리 숫자(10000 - 99999 범위)의 합을 찾는 임무를 받았습니다: {4, 5, 6}. 이는 동일한 횟수 내에서 반복될 수 있으며, 그렇다면 각 발생은 한 번 계산됩니다.

일치하는 숫자의 예로는 42057, 74638 및 89515가 있습니다. 나는 이 작은 코드 조각만을 가지고 있습니다.

#! /bin/bash
for (( CON1=10000; CON1<=99999; CON1++ )) ;
    do
        ## UNKNOWN COMMANDS
    done

답변1

다음은 숫자에 4, 5, 6이 몇 개 나오는지 계산하고 bash결과가 2인지 여부에 따라 명령문을 실행하는 한 가지 방법입니다.

$ con1=1457
$ a=${con1//[^456]/}; [ ${#a} -eq 2 ] && echo Yes
Yes

답변2

시작하기

저는 이런 프로젝트가 있을 때마다 단계별로 진행하는 것을 좋아합니다. 내가 가장 먼저 하고 싶은 일은 echo루프 내부에 추가하고 실행하여 루프가 내가 원하는 것을 제공하는지 확인하는 것입니다.

#! /bin/bash
for (( CON1=10000; CON1<=99999; CON1++ )) ;
do
  echo $CON1
done

이제 실행하면 head -5출력되는 처음 5줄만 표시됩니다.

$ ./cmd.bash | head -5
10000
10001
10002
10003
10004

좋습니다. 다음과 같이 결말을 확인하세요.

$ ./cmd.bash | tail -5
99995
99996
99997
99998
99999

너무 좋아 보인다. 이제 집합 {4,5,6}에서 두 자리 숫자를 식별하는 다음 단계를 수행하는 몇 가지 방법을 찾아보겠습니다. 내 첫 번째 본능 은 그것을 찾는 것이었습니다 grep. Bash에서만 이 작업을 수행하는 방법이 있지만 저는 다양한 도구, grep, awksed이런 작업을 수행하는 것을 좋아합니다. 주로 그렇게 생각하기 때문입니다.

떨어져 있는

grep그러면 {4,5,6} 집합에서 2자리 숫자가 포함된 행을 어떻게 찾을 수 있을까요 ? 이를 위해 정규 표현식으로 다음과 같이 작성된 집합 표기법을 사용할 수 있습니다 [456]. 세트에서 일치시킬 자릿수를 지정할 수도 있습니다. 다음과 같이 작성하세요.

[456]{#}

#숫자 또는 숫자 범위는 어디에 있습니까? 3개를 원하면 이라고 쓰고, [456]{3}2~5자리를 원하면 이라고 씁니다 [456]{2,5}. 3개 이상을 원하면 [456]{3,}`라고 씁니다.

따라서 귀하의 시나리오에서는 입니다 [456]{2}. 에서 정규식을 사용하려면 grep특정 버전이 grep해당 스위치를 지원해야 합니다 -E. 이는 일반적으로 대부분의 표준에서 사용할 수 있습니다 grep.

$ echo "45123" | grep -E "[456]{2}"
45123

작동하는 것처럼 보이지만 숫자 3을 지정하면 문제가 발생하기 시작합니다.

$ echo "45423" | grep -E "[456]{2}"
45423

이것도 잘 들어맞는데, grep이는 문자열에 숫자라는 개념이 없기 때문입니다. 이것은 어리석은 일입니다. 문자열의 일련의 문자가 집합에서 나온 것이고 문자열에 2개의 문자와 2개의 숫자가 있는지 알려주도록 지시합니다 45423.

다음 문자열에도 실패합니다.

$ echo "41412" | grep -E "[456]{2}"
$

그럼 이 방법이 효과가 있나요? 전략을 조금만 변경하면 끝이지만 정규식을 다시 조정해야 합니다.

$ echo -e "41123\n44123\n44423\n41423" | grep -E "[^456]*([456][^456]*){2}"
44123
44423
41423

위에서는 4가지 유형의 문자열을 소개합니다. echo -e "41123\n44123\n44423\n41423"우리 범위에 있는 4개의 숫자 만 인쇄합니다.

$ echo -e "41123\n44123\n44423\n41423"
41123
44123
44423
41423

이 정규 표현식은 어떻게 작동하나요? 0개 이상의 "not [456]" 문자와 1개 이상의 [456] 또는 0개 이상의 "not [456]" 문자로 구성된 정규식 패턴을 설정하여 후자를 2번 찾습니다.

이제 스크립트에서 일부 어셈블리를 수행해 보겠습니다.

for (( CON1=10000; CON1<=99999; CON1++ )) ;
do
  if echo $CON1 | grep -q -E "[^456]*([456][^456]*){2}"; then
      echo $CON1
    fi
done

위의 head& 트릭을 사용하면 tail작동하는 것을 볼 수 있습니다.

$ ./cmd.bash | head -5
10044
10045
10046
10054
10055

$ ./cmd.bash | tail -5
99955
99956
99964
99965
99966

그러나 이 방법은 매우 느린 것으로 판명되었습니다. 문제는 입니다 grep. 비용이 많이 들고 루프에서 반복당 grep을 1회 실행하므로 약 80,000번이 됩니다!

이를 개선하기 위해 grep명령을 루프 밖으로 이동하고 목록을 생성한 후 다음과 같이 방금 숫자를 에코한 스크립트의 원래 버전을 사용하여 한 번 실행할 수 있습니다.

$ ./cmd.bash | grep -E "[^456]*([456][^456]*){2}"

노트:for 루프를 완전히 버리고 명령줄 도구를 사용할 수 있습니다 seq. 이렇게 하면 동일한 숫자 시퀀스가 ​​생성됩니다 seq 10000 99999.

라이너?

이를 수행하는 멋진 방법은 위 명령에서 일련의 숫자를 가져와 각 숫자 사이에 pastea를 삽입하는 명령 에 파이프 +한 다음 해당 출력을 명령줄 계산기로 실행하는 것입니다 bc.

$ ./cmd.bash | grep -E "[^456]*([456][^456]*){2}" | paste -s -d"+"
10044+10045+10046+10054+10055+10056+10064+10065+10066+10144+10145+...

$ ./cmd.bash | grep -E "[^456]*([456][^456]*){2}" | paste -s -d"+" | bc
2409327540

하지만 이는 이 문제를 해결하는 완전히 다른 방법이므로 루프로 돌아가겠습니다 for.

순수한 Bash 사용

따라서 Bash의 숫자가 정확히 2자리인지 테스트할 수 있는 방법이 필요하지만 grep80,000번 호출하는 것만큼 비용이 많이 들지는 않습니다. 최신 버전의 Bash에는 =~AND 를 수행할 수 있는 연산자를 사용하는 기능이 포함되어 있습니다 grep.

#!/bin/bash
for (( CON1=10000; CON1<=99999; CON1++ )) ;
  if [[ $CON1 =~ [^456]*([456][^456]*){2} ]]; then
    echo $CON1
  fi
done

이것을 실행하는 것이 정확히 우리가 원하는 것 같습니다.

$ ./cmd1.bash  | head -5
10044
10045
10046
10054
10055

$ ./cmd1.bash  | tail -5
99955
99956
99964
99965
99966

확인 결과 이제 41511에서 작동하는 것으로 나타났습니다.

$ ./cmd1.bash | grep 41511
41511

인용하다

답변3

내 생각엔 순수한 Bash 스크립트에서 이 작업을 수행해야 할 것 같지만 John1024의 알고리즘을 awk로 변환하면많이가속하다:

awk 'BEGIN{k=0;for(i=10000;i<100000;i++){j=i;if(gsub(/[456]/,"",j)==2)k+=i};print k}'

bash 버전 시간의 1/20 미만으로 실행되며 str.count()Python의 내장 메서드를 사용하는 Python 버전보다 약간 빠릅니다.

관련 정보