2 개의 숫자를 반복해서 추가하여 숫자 만들기


14

당신은 두 개의 16 비트 레지스터와 기계를 주어,있다 xy. 레지스터는 초기화 x=1하고 y=0. 기계가 수행 할 수있는 유일한 작업은 모듈로 65536을 추가하는 것입니다.

  • x+=y- x로 대체됩니다 (x + y) mod 65536. y변하지 않았다
  • y+=x -유사하게 y
  • x+=x- x로 대체됩니다 2x mod 65536. x짝수 인 경우에만 합법적
  • y+=y -유사하게 y

목표는 레지스터 중 하나에서 미리 결정된 숫자를 얻는 것입니다 ( x또는 y).

숫자 (in stdin,, argvfunction parameter, top of top 또는 기타 기존 장소)를 수신하고이 숫자를 얻기 위해 프로그램을 출력 하는 프로그램 또는 서브 루틴을 작성하십시오 . 출력은 다른 기존 출력 장치 stdout로 또는 로 이동해야 합니다 (언어가없는 경우 stdout).

출력 프로그램은 최대 100 % + 최적에서 2 단계 멀어 질 수 있습니다. 즉, 목표 번호를 얻는 가장 짧은 프로그램에 n단계가있는 경우 솔루션은보다 길 수 없습니다 2n+2. 이 제한은 "너무 쉬운"솔루션 (예 : 1, 2, 3, ...)을 피하기위한 것이지만 전체 최적화가 필요하지는 않습니다. 가장 짧은 프로그램을 찾는 것이 가장 쉽지만 확실하지는 않습니다.

예를 들어 : 입력 = 25입니다. 출력 :

y+=x
x+=y
x+=y
x+=x
x+=x
x+=x
y+=x

또 다른 예 : 피보나치 수의 경우 출력에이 대체 패턴이 있습니다. 입력 = 21의 경우 출력은

y+=x
x+=y
y+=x
x+=y
y+=x
x+=y
y+=x

가장 짧은 코드 (바이트 단위로 측정)가 이깁니다.

(이 퍼즐은 최근에 생성해야했던 16 비트 프로세서 용 코드에서 영감을 얻었습니다)

추신 : 나는 최적의 프로그램이 가장 긴 숫자는 무엇입니까?


9
호기심에서 왜 x+=x합법적 인 경우에만 합법적 x입니까? 또한 가장 짧은 프로그램의 경우 BFS와 같은 것이 효과가 있다고 생각합니다.
Sp3000

목표에 도달 한 후 다음 목표 번호로 계속 가고 싶을 것입니다. 어떤 목표에도 도달 할 수 있으려면 숫자 중 하나가 홀수 여야합니다. 나는 불필요한 복잡성을 피하기 위해 끝없는 목표 스트림을 만들고 싶지 않았지만 정신은 그러한 흐름을 허용하는 것입니다.
anatolyg

단계 수에 대한 제한을 변경 했으므로 대상 번호 0 또는 1의 경우 출력 프로그램을 비워 둘 필요가 없습니다.
anatolyg

3
x+=x짝수 만 작동 한다면 x25 복식 3의 입력 예는 어떻게됩니까?
bcsb1001

답변:


2

CJam, 31

마찬가지로 @Tobia 의 대답은, 내 알고리즘도 뻔뻔하게되어 도난당한 영감을 @CChak 의 대답 '. 그러나 CJam이라는 흑 마법을 사용하면서 알고리즘의 구현을 더 작게 만들었습니다.

여기에서 시도하십시오.

골프 :

qi{_4%!:X)/X!-'xX+"
y+="@}h;]W%`

언 골프 드 :

qi          "Read input and convert to integer.";
{           "Do...";
  _4%!:X    "Assign (value mod 4 == 0) to X.";
  )/X!-     "If X, divide value by 2. If not X, decrement value.";
  'xX+      "If X, put 'y' on the stack. If not X, put 'x' on the stack.";
  "
y+="        "Put '\ny+=' on the stack.";
  @         "Rotate top 3 elements of stack left so the value is on top.";
}h          "... while value is not zero.";
;           "Discard zero value on stack.";
]W%         "Collect stack into array and reverse.";

내가 틀렸다면 정정하십시오. 그러나 비슷한 알고리즘으로 답변에 사용되는 모듈로 65536 연산이 필요하지 않다고 생각했습니다. 입력이 유효한 부호없는 16 비트 정수라고 가정 할 수있는 질문을 해석 했으며이 알고리즘의 중간 값이나 결과도 마찬가지입니다.


mod 65536 제거에 대한 흥미로운 점입니다. "미리 결정된 숫자"가 0-65535 내에 있어야한다는 것이 좋습니다.
CChak

8

107 97

첫 번째 게시물입니다.

sub h{($i)=@_;return if(($i%=65536)==0);($i%4==0)?do{h($i/2);say"y+=y";}:do{h($i-1);say"y+=x";}}

모든 레지스터 추가 기준에 적합하지만 내 답변이 항상 최적의 단계 수의 2n + 2 내에 있는지 확인하기 위해 철저한 검사를 수행하지 않았습니다. 그러나 모든 피보나치 수의 한계 안에 있습니다.

자세한 내용은 다음과 같습니다.

sub h{                           # Declare the subroutine, it should be called referencing an integer value
   ($i)=@_;                      # Assign the i variable to the integer used in the call
   return if(($i%=65536)==0);    # Check for base condition of called by 0, and enforce modulo from the start.
   ($i%4==0) ?                   # If the value passed is even, and will result in an even number if we halve it
   do{h($i/2);say"y+=y";}        # Then do so and recurse 
   :do{h($i-1);say"y+=x";}       # Otherwise "subtract one" and recurse
}                                # Note that the print statements get called in reverse order as we exit.

내가 언급했듯이, 이것은 골프를 처음 시도한 것이므로 이것이 향상 될 수 있다고 확신합니다. 또한 초기 서브 루틴 호출이 재귀 호출에서 계산되어야하는지 여부가 확실하지 않아 몇 개의 문자가 발생할 수 있습니다.

흥미롭게도 우리는 코드를 11 바이트 *로 줄이고 레지스터 작업 수 측면에서 "효율"을 향상시킬 수 있으며, 심지어 값만 "배가"될 수있는 요구 사항을 완화합니다. 나는 그것을 재미있게 여기에 포함시켰다.

sub h{($i)=@_;return if(($i%=65536)==0);($i%2==0)?do{h($i/2);say"y+=y";}:do{h($i-1);say"y+=x";}}

부록 시작 :

이 문제를 정말 좋아했으며 지난 몇 주 동안 문제를 해결했습니다. 결과를 게시하겠다고 생각했습니다.

일부 숫자 :

BFS 알고리즘을 사용하여 최적의 솔루션을 찾으면 처음 2 ^ 16 숫자에는 23 개의 단계가 필요한 18 개의 숫자 만 있습니다. 58558, 59894, 60110, 61182, 61278, 62295, 62430, 62910, 63422, 63462, 63979, 64230, 64314, 4486, 64510, 64698, 64854, 65295입니다.

위에서 설명한 재귀 알고리즘을 사용하여 도달하기 "가장 어려운"수는 45 개 작업에서 65535입니다. (65534는 44를 취하고 43 개의 스텝을 갖는 14 개의 숫자가 있습니다) 65535는 최적에서 45에서 22까지 가장 큰 이탈입니다. 23 개의 스텝 차이는 2n + 1입니다. 정의 된 범위에서 사소한 (제로 단계) 사례를 제외하고 재귀 방법은 평균적으로 최적 솔루션의 약 1.4 배입니다.

결론 : 1-2 ^ 16 숫자의 경우 재귀 알고리즘은 정의 된 임계 값 2n + 2를 절대로 넘지 않으므로 답이 유효합니다. 그러나 더 큰 레지스터 / 더 많은 비트에 대한 최적의 솔루션에서 너무 멀어지기 시작할 것이라고 생각합니다.

BFS를 만들 때 사용한 코드는 조잡하고 메모리가 많이 사용되며 주석 처리되지 않았으며 의도적으로 포함되지 않았습니다. 따라서 ... 결과를 신뢰할 필요는 없지만 결과에 대해 확신합니다.


비 BFS 솔루션, 훌륭합니다!
anatolyg

나는 가장 병리학 적 사례조차도 4 배 안에 머무를 것이라고 생각합니다. 최적의 솔루션에 대한 하한에 대해서만 알고 있기 때문에 더 좋을 것입니다. 여전히 꽤 좋습니다.
rationalis

7

파이썬 3, 202 바이트

def S(n):
 q=[(1,0,"")];k=65536
 while q:
  x,y,z=q.pop(0)
  if n in{x,y}:print(z);return
  q+=[((x+y)%k,y,z+"x+=y\n"),(x,(x+y)%k,z+"y+=x\n")]+[(2*x%k,y,z+"x+=x\n")]*(~x&1)+[(x,2*y%k,z+"y+=y\n")]*(~y&1)

(몇 바이트 동안 @rationalis에게 감사합니다)

다음은 매우 기본적인 솔루션입니다. 나는 마지막 라인을 더 잘 골프화 할 수 있기를 원하지만 현재 아이디어가 부족합니다. 로 전화하십시오 S(25).

이 프로그램은 캐싱없이 간단한 BFS 만 수행하므로 매우 느립니다. S(97)일부 샘플 출력 은 다음과 같습니다 .

y+=x
x+=y
x+=x
x+=x
x+=x
x+=x
y+=x
y+=x
x+=y

5

Dyalog APL, 49 자 / 바이트 *

{0=N←⍵|⍨2*16:⍬⋄0=4|N:⎕←'y+=y'⊣∇N÷2⋄⎕←'y+=x'⊣∇N-1}

@CChak 의 답변에서 부끄럽게 영감을 얻은 알고리즘 .

예:

    {0=N←⍵|⍨2*16:⍬⋄0=4|N:⎕←'y+=y'⊣∇N÷2⋄⎕←'y+=x'⊣∇N-1} 25
y+=x
y+=x
y+=y
y+=x
y+=x
y+=y
y+=y
y+=x

언 골프 드 :

{
    N←(2*16)|⍵                 # assign the argument modulo 65536 to N
    0=N: ⍬                     # if N = 0, return an empty value (that's a Zilde, not a 0)
    0=4|N: ⎕←'y+=y' ⊣ ∇N÷2     # if N mod 4 = 0, recurse with N÷2 and *then* print 'y+=y'
    ⎕←'y+=x' ⊣ ∇N-1            # otherwise, recurse with N-1 and *then* print 'y+=x'
}

* Dyalog APL은 APL 기호가 상위 128 바이트 값에 매핑 된 레거시 문자 집합을 지원합니다. 따라서 ASCII 문자와 APL 기호 만 사용하는 APL 프로그램은 바이트 == 문자로 간주 될 수 있습니다.


3

파이썬, 183

def S(n):
 b,c,e=16,'x+=x\n','x+=y\n';s=d='y+=x\n';a=i=0
 if n<2:return
 while~n&1:n>>=1;a+=1
 while n:n>>=1;s+=[e,c][i]+d*(n&1);i=1;b-=1
 while a:s+=[c,c*b+e*2][i];i=0;a-=1
 print(s)

나는 이것이 짝수에 대한 최적의 프로그램의 2 배 안에 머무르는 것을 보장 할 수는 없지만 효율적입니다. 유효한 모든 입력 0 <= n < 65536에 대해 기본적으로 즉각적이며 최대 33 개의 명령어로 구성된 프로그램을 생성합니다. 임의의 레지스터 크기 n(상수를 고정한 후)의 경우, O(n)최대 2n+1명령으로 시간 이 걸립니다 .

일부 이진 논리

모든 홀수는 n31 단계에서 도달 할 수 있습니다 할 y+=x, 받고 x,y = 1,1, 다음 두 배로 유지 xx+=x(첫 번째 배로 할 수 x+=y있기 때문에, x처음부터 홀수). x이 방법으로 모든 2의 거듭 제곱에 도달합니다 (왼쪽 시프트 일뿐입니다). 따라서 y해당하는 2의 거듭 제곱을 추가하여 임의의 비트를 1로 설정할 수 있습니다. 16 비트 레지스터를 사용하고 각 비트는 첫 번째는 도달하는 데 두 배가 걸리고 하나는 설정하는 데 걸리므 y+=x로 최대 31 ops를 얻습니다.

모든 짝수 n는 2의 거듭 제곱입니다. 호출하고 a, 홀수를 곱한 다음 호출하십시오 m. 즉 n = 2^a * m, 또는 이와 동등하게 n = m << a. 위의 프로세스를 사용하여을 얻은 m다음 x0이 될 때까지 왼쪽으로 이동하여 재설정하십시오. a x+=y를 설정 x = m한 다음을 x사용 x+=y하고 처음 으로을 계속 사용하여 두 배를 계속 하십시오 x+=x.

무엇이든이 a걸리는,이다 16-a의 변화를 x얻을 수 y=m및 추가 a변화 재설정 x=0. 후에 또 다른 a변화 x가 일어납니다 x=m. 따라서 총 16+a교대가 사용됩니다. 16-a얻도록 설정해야 할 비트가 m있으며 각 비트는 1을 갖습니다 y+=x. 마지막으로 x=0m,로 설정할 때 추가 단계가 필요합니다 x+=y. 따라서 짝수를 얻는 데 최대 33 단계가 필요합니다.

물론 이것을 모든 크기 레지스터로 일반화 할 수 있습니다.이 경우 항상 최대 2n-12n+1홀수 및 짝수 n비트 정수가 필요합니다.

최적

이 알고리즘은 홀수에 대해 거의 최적 인 (즉 , 최소 단계 수인 2n+2경우 n) 프로그램을 생성 합니다. 주어진 홀수 들어 n경우 m번째 비트가 1 선도하고있는 프로그램은 적어도 소요 m단계에 도착 x=n하거나 y=n, 빠른 레지스터 "의 값이 증가되는 동작 이후 x+=x또는 y+=y(즉, 세포 분열) 그리고 필요 m에 도착 배화를 m이 알고리즘은 이후부터 1 번째 비트는 기껏 얻어 2m단계 (최대 두 배마다, 변속 용, 하나는 y+=x임의의 홀수 거의 최적으로 표시된다).

숫자는 그다지 좋지 않습니다. 왜냐하면 항상 16 ops를 사용하여 x다른 것보다 먼저 재설정 하고 예를 들어 5 단계 내에 8에 도달 할 수 있기 때문입니다.

흥미롭게도 위의 알고리즘 은 항상 사용하지 y+=y않기 때문에 전혀 사용하지 않습니다 y. 이 경우 실제로 3 개의 제한된 조작 세트에 대해 가장 짧은 프로그램을 찾을 수 있습니다.

테스팅

# Do an exhaustive breadth-first search to find the shortest program for
# each valid input
def bfs():
    d = {(0,1):0}
    k = 0xFFFF
    s = set(range(k+1))
    current = [(0,1)]
    nexts = []
    def add(pt, dist, n):
        if pt in d: return
        d[pt] = dist
        s.difference_update(pt)
        n.append(pt)
    i = 0
    while len(s) > 0:
        i += 1
        for p in current:
            x,y = p
            add((x,x+y&k), i, nexts)
            add((y,x+y&k), i, nexts)
            if y%2 == 0: add(tuple(sorted((x,y+y&k))), i, nexts)
            if x%2 == 0: add(tuple(sorted((x+x&k,y))), i, nexts)
        current = nexts
        nexts = []
        print(len(d),len(s))

# Mine (@rationalis)
def S(n):
    b,c,e=16,'x+=x\n','x+=y\n';s=d='y+=x\n';a=i=0
    if n<2:return ''
    while~n&1:n>>=1;a+=1
    while n:n>>=1;s+=[e,c][i]+d*(n&1);i=1;b-=1
    while a:s+=[c,c*b+e*2][i];i=0;a-=1
    return s

# @CChak's approach
def U(i):
    if i<1:return ''
    return U(i//2)+'y+=y\n' if i%4==0 else U(i-1)+'y+=x\n'

# Use mine on odd numbers and @CChak's on even numbers
def V(i):
    return S(i) if i % 2 == 1 else U(i)

# Simulate a program in the hypothetical machine language
def T(s):
    x,y = 1,0
    for l in s.split():
        if l == 'x+=x':
            if x % 2 == 1: return 1,0
            x += x
        elif l == 'y+=y':
            if y % 2 == 1: return 1,0
            y += y
        elif l == 'x+=y': x += y
        elif l == 'y+=x': y += x
        x %= 1<<16
        y %= 1<<16
    return x,y

# Test a solution on all values 0 to 65535 inclusive
# Max op limit only for my own solution
def test(f):
    max_ops = 33 if f==S else 1000
    for i in range(1<<16):
        s = f(i); t = T(s)
        if i not in t or len(s)//5 > max_ops:
            print(s,i,t)
            break

# Compare two solutions
def test2(f,g):
    lf = [len(f(i)) for i in range(2,1<<16)]
    lg = [len(g(i)) for i in range(2,1<<16)]
    l = [lf[i]/lg[i] for i in range(len(lf))]
    print(sum(l)/len(l))
    print(sum(lf)/sum(lg))

# Test by default if script is executed
def main():
    test()

if __name__ == '__main__':
    main()

솔루션이 실제로 올바른 결과를 생성하고 모든 유효한 입력에 대해 33 단계를 넘지 않는지 확인하는 간단한 테스트를 작성했습니다 ( 0 <= n < 65536).

또한 솔루션의 출력을 최적의 출력과 비교하기 위해 경험적 분석을 시도했지만 너비 우선 검색은 모든 유효한 입력에 대한 최소 출력 길이를 얻기에는 너무 비효율적 n입니다. 예를 들어, BFS를 사용하여 출력을 찾는 n = 65535것이 적절한 시간 내에 종료되지 않습니다. 그럼에도 불구하고 나는 떠났고 bfs()제안에 개방적입니다.

그러나 @CChak에 대해 내 솔루션을 테스트했습니다 (Python에서로 구현 됨 U). 작은 짝수의 경우 비효율적으로 비효율적이기 때문에 광산이 더 나빠질 것으로 예상했지만 두 가지 방법으로 전체 범위에 걸쳐 평균적으로 10.8 %에서 12.3 % 더 짧은 길이의 출력을 생산했습니다. 나는 이것이 홀수에 대한 내 자신의 솔루션에서 더 나은 효율성으로 인한 것이라고 생각했기 때문에 홀수에 V마이닝을 사용하고 짝수에 @CChak을 사용하지만 V사이에 있습니다 (약 10 %보다 짧고 U, 3 % 더 큼 S).


1
201 바이트의 많은 논리!
아나톨리 크

@analtolyg 내가 말할 수있는 것은, 수학과 비트 피들 링을 좋아합니다. 짝수 솔루션에는 개선의 여지가 있기 때문에 다른 접근법을 조사 할 수 있습니다.
rationalis

우와, 나는 x,y='xy'지금까지 가능 하다는 것을 깨닫지 못했습니다 . 불행히도 형식을 c*b+e*2간결하게 다시 쓰는 방법을 생각할 수 없습니다 %.
rationalis

아, 다른 곳에서 사용한 걸 몰랐어요. 그것은 단지 나입니까, 아니면 S(2)실제로 출력이 길습니까?
Sp3000

불행히도 내 솔루션으로 모든 짝수는 적어도 19 단계 (19 S(2)에서 가장 짧은 단계 )를 수행합니다. 추적 x하고 y명시 적으로 추적하지 않으므로 x두 번째 단계 후에 2에 도달 하더라도 계속 x0 으로 재설정 됩니다. 더 나은 솔루션이 있어야한다고 생각하지만 아직 생각할 수는 없습니다. 하나.
rationalis
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.