Tạo bất kỳ số nào bằng cách liên tục thêm 2 số


14

Bạn được cấp một máy có hai thanh ghi 16 bit xy. Các thanh ghi được khởi tạo x=1y=0. Hoạt động duy nhất mà máy có thể làm là bổ sung modulo 65536. Đó là:

  • x+=y- xđược thay thế bởi (x + y) mod 65536; ykhông thay đổi
  • y+=x - tương tự cho y
  • x+=x- xđược thay thế bởi 2x mod 65536; chỉ hợp pháp nếu xlà thậm chí
  • y+=y - tương tự cho y

Mục tiêu là để có được một số được xác định trước trong một trong các thanh ghi (hoặc xhoặc y).

Viết chương trình hoặc chương trình con nhận một số (in stdin,, argvtham số hàm, đỉnh ngăn xếp hoặc bất kỳ vị trí thông thường nào khác) và xuất ra một chương trình để lấy số này. Đầu ra phải chuyển đến stdouthoặc (nếu ngôn ngữ của bạn không có stdout) với bất kỳ thiết bị đầu ra thông thường nào khác.

Chương trình đầu ra có thể lên tới 100% cộng với 2 bước xa tối ưu. Đó là, nếu chương trình ngắn nhất để có được số mục tiêu có ncác bước, giải pháp của bạn không thể dài hơn 2n+2. Hạn chế này là để tránh các giải pháp "quá dễ dàng" (ví dụ: đếm 1, 2, 3, ...) nhưng không yêu cầu tối ưu hóa đầy đủ; Tôi hy vọng rằng chương trình ngắn nhất là dễ tìm nhất, nhưng không thể chắc chắn ...

Ví dụ: Input = 25. Đầu ra:

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

Một ví dụ khác: Đối với bất kỳ số Wikipedia nào, đầu ra có mẫu xen kẽ này. Đối với Đầu vào = 21, đầu ra là

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

Mã ngắn nhất (tính bằng byte) sẽ thắng.

(câu đố này được lấy cảm hứng từ một số mã cho bộ xử lý 16 bit mà tôi phải tạo gần đây)

PS tôi tự hỏi - chương trình tối ưu nào dài nhất?


9
Vì tò mò, tại sao x+=xchỉ hợp pháp nếu xlà thậm chí? Ngoài ra đối với chương trình ngắn nhất tôi nghĩ một cái gì đó như BFS có thể hoạt động.
Sp3000

Sau khi đến mục tiêu, người ta có thể muốn tiếp tục đi đến số mục tiêu tiếp theo; để có khả năng tiếp cận bất kỳ mục tiêu nào, một trong những con số phải là số lẻ. Tôi không muốn tạo ra một luồng mục tiêu vô tận để tránh sự phức tạp không cần thiết, nhưng tinh thần là cho phép một luồng như vậy ...
anatolyg 28/12/14

Tôi đã thay đổi hạn chế về số bước, vì vậy đối với mục tiêu số 0 hoặc 1, chương trình đầu ra không bắt buộc phải trống.
anatolyg

3
nếu x+=xchỉ hoạt động cho xs chẵn , làm thế nào đến ví dụ cho đầu vào 25 nhân đôi 3?
bcsb1001

Câu trả lời:


2

CJam, 31 tuổi

Giống như câu trả lời của @Tobia , thuật toán của tôi cũng bị đánh cắp một cách đáng xấu hổ lấy cảm hứng từ câu trả lời của @CChak . Nhưng, với ma thuật đen đó là CJam, tôi đã xoay sở để thực hiện thuật toán nhỏ hơn nữa.

Hãy thử nó ở đây.

Chơi gôn

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

Ung dung:

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.";

Vui lòng sửa lại cho tôi nếu tôi sai, nhưng tôi tin rằng thao tác modulo 65536 được sử dụng trong các câu trả lời với thuật toán tương tự là không cần thiết. Tôi đã giải thích câu hỏi sao cho chúng ta có thể giả sử rằng đầu vào sẽ là số nguyên 16 bit không dấu hợp lệ và bất kỳ giá trị trung gian hoặc kết quả nào của thuật toán này cũng sẽ như vậy.


Một điểm thú vị về việc loại bỏ mod 65536. Tôi nghĩ rằng bạn thực hiện một trường hợp tốt là "số được xác định trước" phải nằm trong khoảng 0-65535.
CChak

8

Perl 107 97

Bài đầu tiên, vì vậy ở đây đi.

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

Điều này phù hợp với tất cả các tiêu chí bổ sung đăng ký, nhưng tôi đã không thực hiện kiểm tra toàn diện để xem câu trả lời của tôi có luôn trong vòng 2n + 2 số bước tối ưu hay không. Mặc dù vậy, nó cũng nằm trong giới hạn cho mọi số Fibonacci.

Dưới đây là một sự cố chi tiết hơn

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.

Như tôi đã đề cập, đây là lần đầu tiên tôi chơi golf, vì vậy tôi chắc chắn điều này có thể được cải thiện. Ngoài ra, tôi không chắc liệu cuộc gọi chương trình con ban đầu có được tính trong một cuộc gọi đệ quy hay không, điều này có thể khiến chúng tôi tăng thêm vài ký tự.

Điều thú vị là chúng ta có thể giảm mã xuống 11 byte * và cải thiện "hiệu quả" về số lượng hoạt động đăng ký, bằng cách nới lỏng yêu cầu chỉ các giá trị mới có thể được "nhân đôi". Tôi đã bao gồm điều đó cho vui ở đây:

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

Bắt đầu phụ lục:

Thực sự thích vấn đề này, và tôi đã giải quyết vấn đề này trong vài tuần qua. Nghĩ rằng tôi sẽ đăng kết quả của tôi.

Một số số:

Sử dụng thuật toán BFS để tìm một giải pháp tối ưu, trong 2 ^ 16 số đầu tiên chỉ có 18 số yêu cầu 23 bước. Đó là: 58558, 59894, 60110, 61182, 61278, 62295, 62430, 62910, 63422, 63462, 63979, 64230, 64314, 4486, 64510, 64698, 64854, 65295.

Sử dụng thuật toán đệ quy được mô tả ở trên, số "khó nhất" đạt được là 65535, với 45 thao tác. (65534 mất 44 và có 14 số thực hiện 43 bước) 65535 cũng là mức khởi hành lớn nhất so với tối ưu, 45 so với 22. Chênh lệch 23 bước là 2n + 1. (Chỉ có ba số đạt 2n: 65534, 32767, 32751.) Ngoại trừ các trường hợp tầm thường (không bước), trong phạm vi được xác định, phương pháp đệ quy trung bình khoảng 1,4 lần giải pháp tối ưu.

Dòng dưới cùng: Đối với các số 1-2 ^ 16, thuật toán đệ quy không bao giờ vượt qua ngưỡng xác định là 2n + 2, vì vậy câu trả lời là hợp lệ. Tôi nghi ngờ rằng nó sẽ bắt đầu khởi hành quá xa từ giải pháp tối ưu cho các thanh ghi lớn hơn / nhiều bit hơn, tuy nhiên.

Mã tôi đã sử dụng để tạo BFS rất cẩu thả, tốn nhiều bộ nhớ, không được bình luận và không được đưa vào một cách có chủ ý. Vì vậy, ... bạn không cần phải tin vào kết quả của tôi, nhưng tôi khá tin tưởng vào chúng.


Một giải pháp không BFS, thật tuyệt!
anatolyg

Tôi nghĩ ngay cả đối với những trường hợp bệnh lý nhất, bạn sẽ ở trong hệ số 4, có thể tốt hơn (vì tôi chỉ biết giới hạn dưới cho giải pháp tối ưu). Mà vẫn còn khá tốt.
rationalis

7

Python 3, 202 byte

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)

(Cảm ơn @rationalis cho một vài byte)

Đây là một giải pháp cực kỳ cơ bản. Tôi ước tôi có thể chơi golf dòng cuối cùng tốt hơn, nhưng hiện tại tôi không có ý tưởng. Gọi với S(25).

Chương trình chỉ thực hiện một BFS đơn giản không có bộ nhớ đệm, nên rất chậm. Đây là S(97), đối với một số đầu ra mẫu:

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

5

APL Dyalog, 49 ký tự / byte *

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

Thuật toán được lấy cảm hứng từ câu trả lời của @CChak .

Thí dụ:

    {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

Ung dung:

{
    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 hỗ trợ bộ ký tự kế thừa có các ký hiệu APL được ánh xạ tới các giá trị 128 byte trên. Do đó, một chương trình APL chỉ sử dụng các ký tự ASCII và các ký hiệu APL có thể được coi là byte == chars.


3

Con trăn, 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)

Tôi không thể đảm bảo chương trình này nằm trong phạm vi gấp đôi chương trình tối ưu cho các số chẵn, nhưng nó hiệu quả. Đối với tất cả các đầu vào hợp lệ 0 <= n < 65536, về cơ bản là tức thời và tạo ra một chương trình gồm tối đa 33 hướng dẫn. Đối với kích thước thanh ghi tùy ý n(sau khi sửa hằng số đó), sẽ mất nhiều O(n)thời gian với hầu hết các 2n+1hướng dẫn.

Một số logic nhị phân

Bất kỳ số lẻ nào ncũng có thể đạt được trong vòng 31 bước: làm y+=x, nhận x,y = 1,1, và sau đó tiếp tục nhân đôi xvới x+=x(đối với lần nhân đôi đầu tiên x+=y, vì xbắt đầu là số lẻ). xsẽ đạt mọi công suất của 2 theo cách này (chỉ là dịch chuyển trái) và vì vậy bạn có thể đặt bất kỳ bit nào ythành 1 bằng cách thêm công suất tương ứng là 2. Vì chúng tôi đang sử dụng các thanh ghi 16 bit và mỗi bit ngoại trừ lần đầu tiên phải tăng gấp đôi để đạt được và một lần y+=xđể thiết lập, chúng tôi nhận được tối đa 31 ops.

Bất kỳ số chẵn nào nchỉ là một số lũy thừa của 2, gọi nó a, nhân một số lẻ, gọi nó m; ví dụ n = 2^a * m, hoặc tương đương, n = m << a. Sử dụng quy trình trên để nhận m, sau đó đặt lại xbằng cách dịch chuyển sang trái cho đến khi 0. Thực hiện x+=yđể đặt x = mvà sau đó tiếp tục tăng gấp đôi x, lần đầu tiên sử dụng x+=yvà sau đó sử dụng x+=x.

alà gì đi nữa , phải 16-athay đổi xđể có được y=mvà một aca bổ sung để thiết lập lại x=0. Một asự thay đổi khác xsẽ xảy ra sau đó x=m. Vì vậy, tổng số 16+aca được sử dụng. Có tối đa 16-abit cần được thiết lập để có được m, và mỗi bit sẽ lấy một bit y+=x. Cuối cùng, chúng ta cần thêm một bước khi x=0đặt nó thành m , x+=y. Vì vậy, phải mất tối đa 33 bước để có được bất kỳ số chẵn nào.

Tất nhiên, bạn có thể khái quát hóa điều này cho bất kỳ thanh ghi kích thước nào, trong trường hợp đó, nó luôn luôn chiếm tối đa 2n-12n+1ops cho các nsố nguyên lẻ và chẵn , tương ứng.

Sự tối ưu

Thuật toán này tạo ra một chương trình gần tối ưu (tức là trong vòng 2n+2nếu nlà số bước tối thiểu) cho các số lẻ. Đối với một số lẻ nhất định n, nếu mbit thứ 1 là số 1, thì bất kỳ chương trình nào cũng phải thực hiện ít nhất mcác bước để đến x=nhoặc y=n, vì hoạt động làm tăng giá trị của các thanh ghi nhanh nhất là x+=xhoặc y+=y(tức là nhân đôi) và phải mnhân đôi để có được các mbit thứ từ 1. Kể từ khi thuật toán này mất ít nhất 2mbước (ít nhất hai phần tăng gấp đôi, một cho sự thay đổi và một y+=x), bất kỳ số lẻ được thể hiện gần như tối ưu.

Các số chẵn không quá tốt, vì nó luôn sử dụng 16 op để đặt lại xtrước bất kỳ thứ gì khác, và 8, chẳng hạn, có thể đạt được trong vòng 5 bước.

Thật thú vị, thuật toán trên không bao giờ sử dụng y+=ycả, vì yluôn luôn được giữ lẻ. Trong trường hợp đó, nó thực sự có thể tìm thấy chương trình ngắn nhất cho bộ hạn chế chỉ có 3 thao tác.

Kiểm tra

# 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()

Tôi đã viết một thử nghiệm đơn giản để kiểm tra xem giải pháp của tôi có thực sự tạo ra kết quả chính xác hay không và không bao giờ vượt quá 33 bước đối với tất cả các đầu vào hợp lệ ( 0 <= n < 65536).

Ngoài ra, tôi đã cố gắng phân tích theo kinh nghiệm để so sánh đầu ra của giải pháp của tôi với đầu ra tối ưu - tuy nhiên, hóa ra việc tìm kiếm đầu tiên quá kém hiệu quả để có được độ dài đầu ra tối thiểu cho mỗi đầu vào hợp lệ n. Ví dụ: sử dụng BFS để tìm đầu ra n = 65535không kết thúc trong một khoảng thời gian hợp lý. Tuy nhiên, tôi đã để lại bfs()và mở để đề xuất.

Tuy nhiên, tôi đã thử nghiệm giải pháp của riêng tôi chống lại @ CChak's (được triển khai bằng Python ở đây như U). Tôi dự đoán của tôi sẽ làm tồi tệ hơn, vì nó không hiệu quả đối với các số chẵn nhỏ hơn, nhưng tính trung bình trên toàn phạm vi theo hai cách, tôi đã tạo ra sản lượng có độ dài trung bình ngắn hơn 10,8% đến 12,3%. Tôi nghĩ có lẽ điều này là do hiệu quả tốt hơn từ giải pháp của riêng tôi đối với các số lẻ, vì vậy Vsử dụng của tôi cho các số lẻ và @ CChak trên các số chẵn, nhưng Vở giữa (ngắn hơn khoảng 10%, dài hơn U3% so với S).


1
Khá nhiều logic trong 201 byte!
anatolyg

@analtolyg Tôi có thể nói gì, tôi thích toán và hơi nghịch. Tôi có thể điều tra các phương pháp khác, vì giải pháp số chẵn có chỗ để cải thiện.
rationalis

Whoa, tôi thậm chí không nhận ra x,y='xy'là có thể cho đến bây giờ. Thật không may, tôi không thể nghĩ ra cách viết lại c*b+e*2chính xác bằng %định dạng.
rationalis

Ah tôi không nhận ra bạn đã sử dụng nó ở một nơi khác. Là tôi hay là S(2)đầu ra thực sự dài?
Sp3000

Thật không may, với giải pháp của tôi, mỗi số chẵn mất ít nhất 19 bước ( S(2)là số ngắn nhất ở 19). Tôi không theo dõi xyrõ ràng, vì vậy mặc dù xđạt 2 sau bước thứ hai, nhưng dù sao vẫn tiếp tục đặt lại xvề 0. Tôi cảm thấy như phải có một giải pháp tốt hơn, nhưng đến giờ tôi vẫn không thể nghĩ ra một.
rationalis
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.