거품 정리


26

math.stackexchange 에서 질문 한 질문에서 복사 한 질문에 유의하십시오 .

최근에 나는 거품을 날리는 기술을 꽤 많이 얻었습니다. 처음에는 다음과 같이 거품을 날려 버릴 것입니다. 여기에 이미지 설명을 입력하십시오

그러나 상황이 이상해지기 시작했습니다.

여기에 이미지 설명을 입력하십시오

잠시 후, 나는 꽤 이상한 거품을 불고있었습니다.

여기에 이미지 설명을 입력하십시오

수백, 심지어 수천 개의 거품을 날린 후, 이마는 갑자기 다음과 같은 질문으로 주름을 잡았습니다. 예를 들어, n = 1이면 배열은 하나만 있습니다. n = 2 인 경우 두 가지 배열이 있습니다. n = 3이면 4 개의 배열이 있습니다. n = 4이면 9 개의 배열이 있습니다.

다음은 4 가지 버블의 9 가지 배열입니다.
여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오

이 놀라운 거품들을 모두 불고 난 후에, 나는 그들의 배치를 세는 기쁨을 당신과 나누겠다고 결심했습니다. 그래서 당신의 임무는 다음과 같습니다.


n거품 을 배열 할 수있는 방법의 수를 세는 프로그램, 기능 또는 이와 유사한 것을 작성하십시오 .


입력

n거품의 수입니다. n> 0


산출

이 거품을 배열 할 수있는 방법의 수


승리 기준

코드 주위에 거품을 불어 넣을 수 있다면 정말 멋질 것입니다. 코드를 작게 만들수록 더 쉽게 할 수 있습니다. 따라서 바이트 수가 가장 적은 코드를 작성하는 사람이 컨테스트에서 우승합니다.


추가 정보

OEIS


5
기포가 교차있다 경우가있다 개방 문제 와, N = 4 (173 개) 솔루션 .
orlp

@orlp 다행히도,이 거품들은 교차하지 않습니다.
TheNumberOne

1
0유효한 입력은?
Martin Ender

@ KenY-N 예. 하단에 이미 OEIS 링크가 있습니다
Roman Gräf

죄송합니다! 멍청한 댓글 시간 삭제 ...
Ken YN

답변:


12

파이썬 2, 92 87 바이트

a=lambda n:n<2or sum((k%d<1)*d*a(d)*a(n-k)for k in range(1,n)for d in range(1,1+k))/~-n

평범한 영어로 : 계산하기 위해 a(n)우리 는 모든 양의 정수의 d*a(d)*a(n-k)제수 dk작거나 같게 계산하고 n, 이들을 모두 합한 다음로 나눕니다 n-1.

그것은 (교체 파이썬 3에서 실행, 실행 속도 ///위의 기능에) 및 memoize :

import functools
a = functools.lru_cache(None)(a)

이렇게하면 a(50) = 425976989835141038353즉시 계산 됩니다.


우와 짱이다. lru_cache()함수 를 기억 한다고 가정 합니까?
Patrick Roberts

@PatrickRoberts 네, 보통 함수 데코레이터로 사용되지만 수동으로 함수에 적용 할 수도 있습니다.
orlp

@PatrickRoberts 여기에 대한 문서가lru_cache 있습니다.
PM 2Ring

이 함수는를 반환 True합니다 n<2. n=1파이썬 True에서는 숫자 컨텍스트에서 1로 평가되지만 a(0)0을 반환해야 하기 때문에 괜찮습니다 . 고칠 수는 n<2 and n or sum...있지만 더 간단한 방법이있을 수 있습니다.
PM 2Ring

제로 버블을 배열하는 한 가지 방법이 있지만 A000081과 일치하지 않는다는 주장을 할 수 있습니다. OTOH, 우리는 긍정적 인 것만으로 해결해야한다면 n이 코너 케이스를 무시할 수 있습니다. 왜냐하면 그것이 재귀 호출에 영향을 미치지 않기 때문 n입니다.
PM 2Ring

10

GNU 프롤로그, 98 바이트

b(x,0,x).
b(T/H,N,H):-N#=A+B+1,b(H,A,_),b(T,B,J),H@>=J.
c(X,Y):-findall(A,b(A,X,_),L),length(L,Y).

이 답변은 Prolog가 가장 간단한 I / O 형식으로 어려움을 겪을 수있는 좋은 예입니다. 문제를 해결하는 알고리즘이 아니라 문제를 설명함으로써 진정한 프롤로그 스타일로 작동합니다. 적법한 버블 배열로 계산할 대상을 지정하고 모든 버블 배열을 생성하도록 Prolog에 요청한 다음 계산합니다. 세대는 55 자 (프로그램의 처음 두 줄)를 사용합니다. 카운팅 및 I / O는 나머지 43 개 (세 번째 줄과 두 부분을 구분하는 줄 바꿈)를 사용합니다. OP가 언어로 인해 I / O로 어려움을 겪을 것으로 예상되는 문제는 아닙니다. (참고 : Stack Exchange의 구문 강조 표시를 사용하면 읽기가 더 어렵고 쉽지 않기 때문에 해제했습니다.)

설명

실제로 작동하지 않는 유사 프로그램의 유사 코드 버전으로 시작해 보겠습니다.

b(Bubbles,Count) if map(b,Bubbles,BubbleCounts)
                and sum(BubbleCounts,InteriorCount)
                and Count is InteriorCount + 1
                and is_sorted(Bubbles).
c(Count,NPossibilities) if listof(Bubbles,b(Bubbles,Count),List)
                       and length(List,NPossibilities).

b작동 방식 이 매우 명확해야 합니다. 우리는 정렬 된 목록을 통해 거품을 표현하고 있습니다 (동일한 다중 집합을 동일하게 비교하는 다중 집합의 간단한 구현). 단일 거품 []의 개수는 1이고 큰 거품은 개수를 갖습니다. 내부에있는 거품의 총 수에 1을 더한 값 1과 같습니다. 4의 경우이 프로그램은 (작동 한 경우) 다음 목록을 생성합니다.

[[],[],[],[]]
[[],[],[[]]]
[[],[[],[]]]
[[],[[[]]]]
[[[]],[[]]]
[[[],[],[]]]
[[[],[[]]]]
[[[[],[]]]]
[[[[[]]]]]

이 프로그램은 여러 가지 이유로 대답하기에 부적합하지만 가장 중요한 것은 Prolog에 실제로 map술어 가 없다는 것입니다 (작성하는 데 너무 많은 바이트가 소요됨). 대신 우리는 다음과 같이 프로그램을 작성합니다.

b([], 0).
b([Head|Tail],Count) if b(Head,HeadCount)
                    and b(Tail,TailCount)
                    and Count is HeadCount + TailCount + 1
                    and is_sorted([Head|Tail]).
c(Count,NPossibilities) if listof(Bubbles,b(Bubbles,Count),List)
                       and length(List,NPossibilities).

다른 주요 문제는 Prolog의 평가 순서가 작동하는 방식으로 인해 실행될 때 무한 루프로 진행된다는 것입니다. 그러나 프로그램을 약간 재정렬하여 무한 루프를 해결할 수 있습니다.

b([], 0).
b([Head|Tail],Count) if Count #= HeadCount + TailCount + 1
                    and b(Head,HeadCount)
                    and b(Tail,TailCount)
                    and is_sorted([Head|Tail]).
c(Count,NPossibilities) if listof(Bubbles,b(Bubbles,Count),List)
                       and length(List,NPossibilities).

우리는 그들이 무엇인지 알기 전에 우리가 함께 카운트를 추가하는 - - 이것은 매우 이상한 보일 수 있지만, GNU 프롤로그의이 #=비인 연산의 종류를 처리 할 수 있고, 그것은의 첫 번째 라인이기 때문에 b, 그리고 HeadCount그리고 TailCount반드시 모두 미만이 될 Count(알려진) 재귀 용어와 일치하는 횟수를 자연스럽게 제한하는 방법으로 사용되므로 프로그램이 항상 종료됩니다.

다음 단계는 골프를 조금만하는 것입니다. 같은 약어를 사용하여 단일 문자 변수 이름을 사용하여, 여백 제거 :-if,대를 and이용 setof하기보다는 listof(이것은 짧은 이름을 가지고 있으며,이 경우에도 동일한 결과를 생성하는)을 사용 sort0(X,X)하기보다는 is_sorted(X)때문에 ( is_sorted실제로는 실제 함수 아니다 나는 그것을 만들었다) :

b([],0).
b([H|T],N):-N#=A+B+1,b(H,A),b(T,B),sort0([H|T],[H|T]).
c(X,Y):-setof(A,b(A,X),L),length(L,Y).

이것은 상당히 짧지 만 더 잘 할 수 있습니다. 주요 통찰력은 [H|T]목록 구문이 진행됨에 따라 실제로 장황 하다는 것입니다. Lisp 프로그래머가 알고 있듯이,리스트는 기본적으로 단지 튜플 인 cons 셀로 구성되며이 프로그램의 어떤 부분도리스트 내장을 사용하지 않습니다. 프롤로그에는 몇 가지 매우 짧은 튜플 구문이 있습니다 (내가 가장 좋아하는 것은 A-B이지만 두 번째로 가장 좋아하는 것은 A/B입니다.이 경우 읽기 쉬운 디버그 출력을 생성하므로 여기에서 사용하고 있습니다). 그리고 우리는 또한 nil두 개의 문자에 집착하지 않고 목록의 끝에 자신의 단일 문자 를 선택할 수 있습니다 [](나는 선택 x했지만 기본적으로 모든 것이 작동합니다). 대신에을 [H|T]사용 T/H하여b 이것은 다음과 같습니다 (튜플의 정렬 순서는 목록의 정렬 순서와 약간 다르므로 위와 동일한 순서는 아닙니다).

x/x/x/x/x
x/x/x/(x/x)
x/(x/x)/(x/x)
x/x/(x/x/x)
x/(x/x/x/x)
x/x/(x/(x/x))
x/(x/x/(x/x))
x/(x/(x/x/x))
x/(x/(x/(x/x)))

위의 중첩 목록보다 읽기가 다소 어렵지만 가능합니다. 정신적으로 xs를 건너 뛰고 /()거품으로 해석하십시오 (또는 /내용이 없으면 퇴행성 거품으로 일반화 ()됩니다). 요소는 위에 표시된 목록 버전과 일대일 (무질서한 경우) 대응 관계가 있습니다 .

물론이 목록 표현은 훨씬 짧아 지더라도 큰 단점이 있습니다. 언어에 내장되어 있지 않으므로 sort0목록이 정렬되어 있는지 확인할 수 없습니다 . sort0어쨌든 상당히 장황하므로 손으로하는 것은 큰 손실이 아닙니다 (사실, [H|T]목록 표현에서 손으로하는 것은 정확히 동일한 바이트 수에 해당합니다). 여기서 중요한 통찰력 목록이 정렬되어있는 경우 꼬리가 정렬되어있는 경우 경우 서면 검사와 같은 프로그램이, 볼 수 있다는 것입니다 그것의 꼬리 정렬 등입니다; 중복 검사가 많이 있으며이를 활용할 수 있습니다. 대신, 처음 두 요소가 순서대로 정렬되어 있는지 확인합니다 (목록 자체와 모든 접미사를 확인하면 목록이 정렬되도록합니다).

첫 번째 요소는 쉽게 접근 할 수 있습니다. 그것은 단지 목록의 머리 일뿐 H입니다. 그러나 두 번째 요소는 액세스하기가 다소 어렵고 존재하지 않을 수 있습니다. 운 좋게도 x(Prolog의 일반 비교 연산자를 통해) 우리가 고려하고있는 모든 튜플보다 적기 @>=때문에 싱글 톤 목록의 "두 번째 요소"를 고려할 수 x있으며 프로그램은 정상적으로 작동합니다. 실제로 번째 요소 액세스 같이 tersest 방법으로 세 번째 인자 (아웃 인수)를 추가하는 것 b, 즉 복귀 x베이스 경우와 H재귀 경우; 이것은에 대한 두 번째 재귀 호출의 출력으로 꼬리의 머리를 잡을 수 B있으며 물론 꼬리의 머리는 목록의 두 번째 요소입니다. 이제 b다음과 같이 보입니다.

b(x,0,x).
b(T/H,N,H):-N#=A+B+1,b(H,A,_),b(T,B,J),H@>=J.

기본 사례는 충분히 간단합니다 (빈 목록, 카운트 0을 리턴하고 빈 목록의 "첫 번째 요소"는 x). 재귀 경우는 이전과 같은 방법으로 밖으로 시작 (단지로 T/H표기보다는를 [H|T]하고, H인수 밖으로 추가로); 우리는 머리에 대한 재귀 호출의 추가 인수를 무시하지만 J꼬리의 재귀 호출에 저장합니다 . 그럼 우리가 할 일은이 보장되는 H보다 큰거나 같은 J목록 종료까지 정렬되도록하기 위해 (즉, "목록이 두 개 이상의 요소가있는 경우, 먼저 두 번째 같거나보다 큰 경우).

불행히도, 이 새로운 정의와 함께 setof이전 정의를 사용하려고 시도하면 사용 하지 않는 매개 변수가 SQL과 거의 같은 방식으로 처리되므로 적합하지 않습니다. 원하는 것을 수행하도록 재구성 할 수는 있지만 재구성에는 문자가 필요합니다. 대신, 우리 는보다 편리한 기본 동작을 가지고 있으며 두 글자 만 더 길어 다음과 같은 정의를 제공합니다 .cbGROUP BYfindallc

c(X,Y):-findall(A,b(A,X,_),L),length(L,Y).

그리고 그것은 완전한 프로그램입니다. 버블 패턴을 생성 한 다음, 바이트를 계산하는 데 전체 바이트를 소비합니다 ( findall생성기를리스트로 변환 하는 데 다소 시간이 걸리고 , 불행히도 length리스트의 길이와 함수 선언을위한 상용구를 확인하기 위해 이름 이 불명확합니다 ).


"프롤로그에는 실제로 맵 술어가 없습니다" : 프롤로그에는 maplist/2-8술어 가 있지만 여기에서는 더 짧아 질 것입니다.
Fatalize

@ Fatalize : 허, 새로운 버전에 추가 된 것 같습니다. 그것은 내가 가진 설치에 대한 문서에 없으며 실제로 작동하지 않습니다 :| ?- maplist(reverse,[A,B]). uncaught exception: error(existence_error(procedure,maplist/2),top_level/0)

정말 이상합니다. maplistSWI-Prolog 및 SiCStus와 같은 주요 Prolog 배포판에서 제공되는 매우 일반적으로 사용되는 술어입니다.
Fatalize

10

Mathematica, 68 바이트

나는 스크래치에서 구현으로 (Mathematica에서도) 이길 수 있지만 내기 버전은 다음과 같습니다.

<<NumericalDifferentialEquationAnalysis`
Last@ButcherTreeCount[#+1]&

ButcherTreeCount따라서 인덱스가 0이므로 인덱스 [#+1]까지 모든 값의 목록을 인수까지 반환합니다 Last@. 그러나 그렇지 않으면이 기능에 내장 된 것입니다. 그러나 첫 번째 행이 수행하는 패키지를로드해야합니다.


8
"물론 Mathematica는이를 위해 내장되어 있습니다."
orlp
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.